diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 00000000000..37ebbafb104 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,100 @@ +# Code - OSS Development Container + +This repository includes configuration for a development container for working with Code - OSS in an isolated local container or using [GitHub Codespaces](https://github.com/features/codespaces). + +> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` with a web client at `6080`. For better performance, we recommend using a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Applications like the macOS Screen Sharing app will not perform as well. + +## Quick start - local + +1. Install Docker Desktop or Docker for Linux on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.) + +2. **Important**: Docker needs at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. If you on macOS, or using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item, going to **Preferences/Settings > Resources > Advanced**. + + > **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar. + +3. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Remote - Containers](https://aka.ms/vscode-remote/download/containers) extension. + + ![Image of Remote - Containers extension](https://microsoft.github.io/vscode-remote-release/images/remote-containers-extn.png) + + > Note that the Remote - Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. + +4. Press Ctrl/Cmd + Shift + P and select **Remote-Containers: Clone Repository in Container Volume...**. + + > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem. + +5. Type `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box and press Enter. + +6. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080) or use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. + +Anything you start in VS Code or the integrated terminal will appear here. + +Next: **[Try it out!](#try-it)** + +## Quick start - GitHub Codespaces + +> **IMPORTANT:** The current free user beta for GitHub Codespaces uses a "Basic" sized codespace which does not have enough RAM to run a full build of VS Code and will be considerably slower during codespace start and running VS Code. You'll soon be able to use a "Standard" sized codespace (4-core, 8GB) that will be better suited for this purpose (along with even larger sizes should you need it). + +1. From the [microsoft/vscode GitHub repository](https://github.com/microsoft/vscode), click on the **Code** dropdown, select **Open with Codespaces**, and the **New codespace** + + > Note that you will not see these options if you are not in the beta yet. + +2. After the codespace is up and running in your browser, press Ctrl/Cmd + Shift + P and select **View: Show Remote Explorer**. + +3. You should see port `6080` under **Forwarded Ports**. Select the line and click on the globe icon to open it in a browser tab. + + > If you do not see port `6080`, press Ctrl/Cmd + Shift + P, select **Forward a Port** and enter port `6080`. + +4. In the new tab, you should see noVNC. Click **Connect** and enter `vscode` as the password. + +Anything you start in VS Code or the integrated terminal will appear here. + +Next: **[Try it out!](#try-it)** + +### Using VS Code with GitHub Codespaces + +You will likely see better performance when accessing the codespace you created from VS Code since you can use a[VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Here's how to do it. + +1. [Create a codespace](#quick-start---github-codespaces) if you have not already. + +2. Set up [VS Code for use with GitHub Codespaces](https://docs.github.com/github/developing-online-with-codespaces/using-codespaces-in-visual-studio-code) + +3. After the VS Code is up and running, press Ctrl/Cmd + Shift + P, choose **Codespaces: Connect to Codespace**, and select the codespace you created. + +4. After you've connected to the codespace, use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. + +5. Anything you start in VS Code or the integrated terminal will appear here. + +Next: **[Try it out!](#try-it)** + +## Try it! + +This container uses the [Fluxbox](http://fluxbox.org/) window manager to keep things lean. **Right-click on the desktop** to see menu options. It works with GNOME and GTK applications, so other tools can be installed if needed. + +Note you can also set the resolution from the command line by typing `set-resolution`. + +To start working with Code - OSS, follow these steps: + +1. In your local VS Code, open a terminal (Ctrl/Cmd + Shift + \`) and type the following commands: + + ```bash + yarn install + bash scripts/code.sh + ``` + + Note that a previous run of `yarn install` will already be cached, so this step should simply pick up any recent differences. + +2. After the build is complete, open a web browser or a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to the desktop environnement as described in the quick start and enter `vscode` as the password. + +3. You should now see Code - OSS! + +Next, let's try debugging. + +1. Shut down Code - OSS by clicking the box in the upper right corner of the Code - OSS window through your browser or VNC viewer. + +2. Go to your local VS Code client, and use Run / Debug view to launch the **VS Code** configuration. (Typically the default, so you can likely just press F5). + + > **Note:** If launching times out, you can increase the value of `timeout` in the "VS Code", "Attach Main Process", "Attach Extension Host", and "Attach to Shared Process" configurations in [launch.json](../.vscode/launch.json). However, running `scripts/code.sh` first will set up Electron which will usually solve timeout issues. + +3. After a bit, Code - OSS will appear with the debugger attached! + +Enjoy! diff --git a/.devcontainer/cache/.gitignore b/.devcontainer/cache/.gitignore new file mode 100644 index 00000000000..4f96ddff402 --- /dev/null +++ b/.devcontainer/cache/.gitignore @@ -0,0 +1 @@ +*.manifest diff --git a/.devcontainer/cache/before-cache.sh b/.devcontainer/cache/before-cache.sh new file mode 100755 index 00000000000..cfa7acc9d95 --- /dev/null +++ b/.devcontainer/cache/before-cache.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# This file establishes a basline for the reposuitory before any steps in the "prepare.sh" +# are run. Its just a find command that filters out a few things we don't need to watch. + +set -e + +SCRIPT_PATH="$(cd "$(dirname $0)" && pwd)" +SOURCE_FOLDER="${1:-"."}" + +cd "${SOURCE_FOLDER}" +echo "[$(date)] Generating ""before"" manifest..." +find -L . -not -path "*/.git/*" -and -not -path "${SCRIPT_PATH}/*.manifest" -type f > "${SCRIPT_PATH}/before.manifest" +echo "[$(date)] Done!" + diff --git a/.devcontainer/cache/build-cache-image.sh b/.devcontainer/cache/build-cache-image.sh new file mode 100755 index 00000000000..78d0fbfdf0c --- /dev/null +++ b/.devcontainer/cache/build-cache-image.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# This file simply wraps the dockeer build command used to build the image with the +# cached result of the commands from "prepare.sh" and pushes it to the specified +# container image registry. + +set -e + +SCRIPT_PATH="$(cd "$(dirname $0)" && pwd)" +CONTAINER_IMAGE_REPOSITORY="$1" +BRANCH="${2:-"master"}" + +if [ "${CONTAINER_IMAGE_REPOSITORY}" = "" ]; then + echo "Container repository not specified!" + exit 1 +fi + +TAG="branch-${BRANCH//\//-}" +echo "[$(date)] ${BRANCH} => ${TAG}" +cd "${SCRIPT_PATH}/../.." + +echo "[$(date)] Starting image build..." +docker build -t ${CONTAINER_IMAGE_REPOSITORY}:"${TAG}" -f "${SCRIPT_PATH}/cache.Dockerfile" . +echo "[$(date)] Image build complete." + +echo "[$(date)] Pushing image..." +docker push ${CONTAINER_IMAGE_REPOSITORY}:"${TAG}" +echo "[$(date)] Done!" diff --git a/.devcontainer/cache/cache-diff.sh b/.devcontainer/cache/cache-diff.sh new file mode 100755 index 00000000000..362337ce6eb --- /dev/null +++ b/.devcontainer/cache/cache-diff.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This file is used to archive off a copy of any differences in the source tree into another location +# in the image. Once the codespace is up, this will be restored into its proper location (which is +# quick and happens parallel to other startup activities) + +set -e + +SCRIPT_PATH="$(cd "$(dirname $0)" && pwd)" +SOURCE_FOLDER="${1:-"."}" +CACHE_FOLDER="${2:-"/usr/local/etc/devcontainer-cache"}" + +echo "[$(date)] Starting cache operation..." +cd "${SOURCE_FOLDER}" +echo "[$(date)] Determining diffs..." +find -L . -not -path "*/.git/*" -and -not -path "${SCRIPT_PATH}/*.manifest" -type f > "${SCRIPT_PATH}/after.manifest" +grep -Fxvf "${SCRIPT_PATH}/before.manifest" "${SCRIPT_PATH}/after.manifest" > "${SCRIPT_PATH}/cache.manifest" +echo "[$(date)] Archiving diffs..." +mkdir -p "${CACHE_FOLDER}" +tar -cf "${CACHE_FOLDER}/cache.tar" --totals --files-from "${SCRIPT_PATH}/cache.manifest" +echo "[$(date)] Done! $(du -h "${CACHE_FOLDER}/cache.tar")" diff --git a/.devcontainer/cache/cache.Dockerfile b/.devcontainer/cache/cache.Dockerfile new file mode 100644 index 00000000000..79af3ee8a35 --- /dev/null +++ b/.devcontainer/cache/cache.Dockerfile @@ -0,0 +1,14 @@ +# This dockerfile is used to build up from a base image to create an image with cached results of running "prepare.sh". +# Other image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile +FROM mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:dev + +ARG USERNAME=node +COPY --chown=${USERNAME}:${USERNAME} . /repo-source-tmp/ +RUN mkdir /usr/local/etc/devcontainer-cache \ + && chown ${USERNAME} /usr/local/etc/devcontainer-cache /repo-source-tmp \ + && su ${USERNAME} -c "\ + cd /repo-source-tmp \ + && .devcontainer/cache/before-cache.sh \ + && .devcontainer/prepare.sh \ + && .devcontainer/cache/cache-diff.sh" \ + && rm -rf /repo-source-tmp diff --git a/.devcontainer/cache/restore-diff.sh b/.devcontainer/cache/restore-diff.sh new file mode 100755 index 00000000000..2f418d87480 --- /dev/null +++ b/.devcontainer/cache/restore-diff.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# This file restores the results of the "prepare.sh" into their proper locations +# once the container has been created. It runs as a postCreateCommand which +# in GitHub Codespaces occurs parallel to other startup activities and does not +# really add to the overal startup time given how quick the operation ends up being. + +set -e + +SOURCE_FOLDER="$(cd "${1:-"."}" && pwd)" +CACHE_FOLDER="${2:-"/usr/local/etc/devcontainer-cache"}" + +if [ ! -d "${CACHE_FOLDER}" ]; then + echo "No cache folder found." + exit 0 +fi + +echo "[$(date)] Expanding $(du -h "${CACHE_FOLDER}/cache.tar") file to ${SOURCE_FOLDER}..." +cd "${SOURCE_FOLDER}" +tar -xf "${CACHE_FOLDER}/cache.tar" +rm -f "${CACHE_FOLDER}/cache.tar" +echo "[$(date)] Done!" + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..cd632e134ef --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "Code - OSS", + + // Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile + "image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-master", + + "workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached", + "workspaceFolder": "/home/node/workspace/vscode", + "overrideCommand": false, + "runArgs": [ "--init", "--security-opt", "seccomp=unconfined"], + + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "resmon.show.battery": false, + "resmon.show.cpufreq": false + }, + + // noVNC, VNC, debug ports + "forwardPorts": [6080, 5901, 9222], + + "extensions": [ + "dbaeumer.vscode-eslint", + "mutantdino.resourcemonitor" + ], + + // Optionally loads a cached yarn install for the repo + "postCreateCommand": ".devcontainer/cache/restore-diff.sh", + + "remoteUser": "node" +} diff --git a/.devcontainer/prepare.sh b/.devcontainer/prepare.sh new file mode 100755 index 00000000000..47a77a533ae --- /dev/null +++ b/.devcontainer/prepare.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# This file contains the steps that should be run when creating the intermediary image that contains +# contents for that should be in the image by default. It will be used to build up from the base image +# to create an image that speeds up first time use of the dev container by "caching" the results +# of these commands. Developers can still run these commands without an issue once the container is +# up, but only differences will be processed which also speeds up the first time these operations occur. + +yarn install +yarn electron diff --git a/.eslintignore b/.eslintignore index dda0884b381..b2c4a5b6efa 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,9 +3,9 @@ **/vs/css.build.js **/vs/css.js **/vs/loader.js -**/promise-polyfill/** **/insane/** **/marked/** +**/semver/** **/test/**/*.js **/node_modules/** **/vscode-api-tests/testWorkspace/** diff --git a/.eslintrc.json b/.eslintrc.json index ef22512e284..055bc22f8e4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,7 +7,8 @@ }, "plugins": [ "@typescript-eslint", - "jsdoc" + "jsdoc", + "mocha" ], "rules": { "constructor-super": "warn", @@ -26,12 +27,32 @@ "no-throw-literal": "warn", "no-unsafe-finally": "warn", "no-unused-labels": "warn", - "no-restricted-globals": ["warn", "name", "length", "event", "closed", "external", "status", "origin", "orientation"], // non-complete list of globals that are easy to access unintentionally + "no-restricted-globals": [ + "warn", + "name", + "length", + "event", + "closed", + "external", + "status", + "origin", + "orientation", + "context" + ], // non-complete list of globals that are easy to access unintentionally "no-var": "warn", "jsdoc/no-types": "warn", "semi": "off", + "mocha/no-exclusive-tests": "warn", "@typescript-eslint/semi": "warn", - "@typescript-eslint/class-name-casing": "warn", + "@typescript-eslint/naming-convention": [ + "warn", + { + "selector": "class", + "format": [ + "PascalCase" + ] + } + ], "code-no-unused-expressions": [ "warn", { @@ -52,13 +73,18 @@ "browser": [ "common" ], - "electron-main": [ + "electron-sandbox": [ "common", - "node" + "browser" ], "electron-browser": [ "common", "browser", + "node", + "electron-sandbox" + ], + "electron-main": [ + "common", "node" ] } @@ -93,12 +119,20 @@ "**/vs/base/{common,browser}/**" ] }, + { + "target": "**/vs/base/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,electron-sandbox}/**" + ] + }, { "target": "**/vs/base/node/**", "restrictions": [ "vs/nls", "**/vs/base/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -135,7 +169,16 @@ "vs/nls", "**/vs/base/{common,node}/**", "**/vs/base/parts/*/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules + ] + }, + { + "target": "**/vs/base/parts/*/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,electron-sandbox}/**", + "**/vs/base/parts/*/{common,browser,electron-sandbox}/**" ] }, { @@ -143,9 +186,9 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/{common,browser,node,electron-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules ] }, { @@ -154,7 +197,7 @@ "vs/nls", "**/vs/base/{common,node,electron-main}/**", "**/vs/base/parts/*/{common,node,electron-main}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -173,6 +216,8 @@ "sinon", "vs/nls", "**/vs/base/common/**", + "**/vs/base/parts/*/common/**", + "**/vs/base/test/common/**", "**/vs/platform/*/common/**", "**/vs/platform/*/test/common/**" ] @@ -194,7 +239,17 @@ "**/vs/base/{common,node}/**", "**/vs/base/parts/*/{common,node}/**", "**/vs/platform/*/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules + ] + }, + { + "target": "**/vs/platform/*/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,electron-sandbox}/**", + "**/vs/base/parts/*/{common,browser,electron-sandbox}/**", + "**/vs/platform/*/{common,browser,electron-sandbox}/**" ] }, { @@ -202,10 +257,10 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/{common,browser,node}/**", - "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", - "**/vs/platform/*/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules ] }, { @@ -215,7 +270,8 @@ "**/vs/base/{common,node,electron-main}/**", "**/vs/base/parts/*/{common,node,electron-main}/**", "**/vs/platform/*/{common,node,electron-main}/**", - "!path" // node modules (except path where we have our own impl) + "**/vs/code/**", + "*" // node modules ] }, { @@ -384,12 +440,6 @@ "assert" ] }, - { - "target": "**/vs/workbench/workbench.desktop.main.ts", - "restrictions": [ - "**" - ] - }, { "target": "**/vs/workbench/api/common/**", "restrictions": [ @@ -413,19 +463,35 @@ "**/vs/**/{common,worker}/**" ] }, + { + "target": "**/vs/workbench/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,electron-sandbox}/**", + "**/vs/base/parts/*/{common,browser,electron-sandbox}/**", + "**/vs/platform/*/{common,browser,electron-sandbox}/**", + "**/vs/editor/{common,browser,electron-sandbox}/**", + "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention + "**/vs/workbench/{common,browser,electron-sandbox}/**", + "**/vs/workbench/api/{common,browser,electron-sandbox}/**", + "**/vs/workbench/services/*/{common,browser,electron-sandbox}/**" + ] + }, { "target": "**/vs/workbench/electron-browser/**", "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/{common,browser,node,electron-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", - "**/vs/platform/*/{common,browser,node,electron-browser}/**", - "**/vs/editor/{common,browser,node,electron-browser}/**", + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/editor/{common,browser,node,electron-sandbox,electron-browser}/**", "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention - "**/vs/workbench/{common,browser,node,electron-browser,api}/**", - "**/vs/workbench/services/*/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/services/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules ] }, { @@ -436,11 +502,11 @@ "**/vs/base/**", "**/vs/platform/**", "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-browser}/**", + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", "vs/workbench/contrib/files/common/editors/fileEditorInput", "**/vs/workbench/services/**", "**/vs/workbench/test/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -454,7 +520,11 @@ "**/vs/workbench/common/**", "**/vs/workbench/services/**/common/**", "**/vs/workbench/api/**/common/**", - "vscode-textmate" + "vscode-textmate", + "vscode-oniguruma", + "iconv-lite-umd", + "tas-client-umd", + "jschardet" ] }, { @@ -483,7 +553,9 @@ "**/vs/workbench/api/{common,browser}/**", "**/vs/workbench/services/**/{common,browser}/**", "vscode-textmate", - "onigasm-umd" + "vscode-oniguruma", + "iconv-lite-umd", + "jschardet" ] }, { @@ -496,7 +568,24 @@ "**/vs/workbench/{common,node}/**", "**/vs/workbench/api/{common,node}/**", "**/vs/workbench/services/**/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules + ] + }, + { + "target": "**/vs/workbench/services/**/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser,worker,electron-sandbox}/**", + "**/vs/platform/**/{common,browser,electron-sandbox}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser,electron-sandbox}/**", + "**/vs/workbench/api/{common,browser,electron-sandbox}/**", + "**/vs/workbench/services/**/{common,browser,electron-sandbox}/**", + "vscode-textmate", + "vscode-oniguruma", + "iconv-lite-umd", + "jschardet" ] }, { @@ -504,12 +593,29 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/**/{common,browser,worker,node,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-browser}/**", + "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-browser,api}/**", - "**/vs/workbench/services/**/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/workbench/contrib/**/test/**", + "restrictions": [ + "assert", + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**", + "**/vs/platform/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/services/**", + "**/vs/workbench/contrib/**", + "**/vs/workbench/test/**", + "*" ] }, { @@ -518,21 +624,146 @@ // xterm and its addons are strictly browser-only components "xterm", "xterm-addon-*", - "**/vs/**" + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/contrib/**/{common,browser}/**", + "**/vs/workbench/services/**/{common,browser}/**" ] }, { "target": "**/vs/workbench/contrib/extensions/browser/**", "restrictions": [ - "semver-umd", - "**/vs/**" + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/contrib/**/{common,browser}/**", + "**/vs/workbench/services/**/{common,browser}/**" ] }, { "target": "**/vs/workbench/contrib/update/browser/update.ts", "restrictions": [ - "semver-umd", - "**/vs/**" + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/contrib/**/{common,browser}/**", + "**/vs/workbench/services/**/{common,browser}/**" + ] + }, + { + "target": "**/vs/workbench/contrib/notebook/common/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,worker}/**", + "**/vs/platform/**/common/**", + "**/vs/editor/**", + "**/vs/workbench/common/**", + "**/vs/workbench/api/common/**", + "**/vs/workbench/services/**/common/**", + "**/vs/workbench/contrib/**/common/**" + ] + }, + { + "target": "**/vs/workbench/contrib/**/common/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/common/**", + "**/vs/platform/**/common/**", + "**/vs/editor/**", + "**/vs/workbench/common/**", + "**/vs/workbench/api/common/**", + "**/vs/workbench/services/**/common/**", + "**/vs/workbench/contrib/**/common/**" + ] + }, + { + "target": "**/vs/workbench/contrib/**/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/api/{common,browser}/**", + "**/vs/workbench/services/**/{common,browser}/**", + "**/vs/workbench/contrib/**/{common,browser}/**", + "vscode-textmate", + "vscode-oniguruma", + "iconv-lite-umd", + "jschardet" + ] + }, + { + "target": "**/vs/workbench/contrib/**/node/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,node}/**", + "**/vs/platform/**/{common,node}/**", + "**/vs/editor/**/common/**", + "**/vs/workbench/{common,node}/**", + "**/vs/workbench/api/{common,node}/**", + "**/vs/workbench/services/**/{common,node}/**", + "**/vs/workbench/contrib/**/{common,node}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/workbench/contrib/**/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser,worker,electron-sandbox}/**", + "**/vs/platform/**/{common,browser,electron-sandbox}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser,electron-sandbox}/**", + "**/vs/workbench/api/{common,browser,electron-sandbox}/**", + "**/vs/workbench/services/**/{common,browser,electron-sandbox}/**", + "**/vs/workbench/contrib/**/{common,browser,electron-sandbox}/**", + "vscode-textmate", + "vscode-oniguruma", + "iconv-lite-umd", + "jschardet" + ] + }, + { + "target": "**/vs/workbench/contrib/**/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/contrib/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/code/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser}/**", + "**/vs/base/parts/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/code/**/{common,browser}/**", + "**/vs/workbench/workbench.web.api" ] }, { @@ -543,7 +774,7 @@ "**/vs/base/parts/**/{common,node}/**", "**/vs/platform/**/{common,node}/**", "**/vs/code/**/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -551,11 +782,11 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/**/{common,browser,node,electron-browser}/**", - "**/vs/base/parts/**/{common,browser,node,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-browser}/**", - "**/vs/code/**/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/code/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules ] }, { @@ -566,7 +797,7 @@ "**/vs/base/parts/**/{common,node,electron-main}/**", "**/vs/platform/**/{common,node,electron-main}/**", "**/vs/code/**/{common,node,electron-main}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -579,12 +810,68 @@ "**/vs/workbench/**/{common,node}/**", "**/vs/server/**", "**/vs/code/**/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { - "target": "**/{node,electron-browser,electron-main}/**", - "restrictions": "**/*" + "target": "**/src/vs/workbench/workbench.common.main.ts", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,browser}/**", + "**/vs/base/parts/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/**/{common,browser}/**" + ] + }, + { + "target": "**/src/vs/workbench/workbench.web.main.ts", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,browser}/**", + "**/vs/base/parts/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/**/{common,browser}/**", + "**/vs/workbench/workbench.common.main" + ] + }, + { + "target": "**/src/vs/workbench/workbench.web.api.ts", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,browser}/**", + "**/vs/base/parts/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/**", + "**/vs/workbench/**/{common,browser}/**", + "**/vs/workbench/workbench.web.main" + ] + }, + { + "target": "**/src/vs/workbench/workbench.sandbox.main.ts", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,browser,electron-sandbox}/**", + "**/vs/base/parts/**/{common,browser,electron-sandbox}/**", + "**/vs/platform/**/{common,browser,electron-sandbox}/**", + "**/vs/editor/**", + "**/vs/workbench/**/{common,browser,electron-sandbox}/**", + "**/vs/workbench/workbench.common.main" + ] + }, + { + "target": "**/src/vs/workbench/workbench.desktop.main.ts", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/editor/**", + "**/vs/workbench/**/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/workbench/workbench.common.main", + "**/vs/workbench/workbench.sandbox.main" + ] }, { "target": "**/extensions/**", @@ -594,23 +881,73 @@ "target": "**/test/smoke/**", "restrictions": [ "**/test/smoke/**", - "*" + "*" // node modules ] }, { "target": "**/test/automation/**", "restrictions": [ "**/test/automation/**", - "*" + "*" // node modules ] }, { - "target": "{**/**.test.ts,**/test/**}", - "restrictions": "{**/vs/**,assert,sinon,crypto}" + "target": "**/test/integration/**", + "restrictions": [ + "**/test/integration/**", + "*" // node modules + ] }, { - "target": "**/{common,browser,workbench}/**", - "restrictions": "**/vs/**" + "target": "**/api/**.test.ts", + "restrictions": [ + "**/vs/**", + "assert", + "sinon", + "crypto", + "vscode" + ] + }, + { + "target": "**/{node,electron-browser,electron-main}/**/*.test.ts", + "restrictions": [ + "**/vs/**", + "*" // node modules + ] + }, + { + "target": "**/{node,electron-browser,electron-main}/**/test/**", + "restrictions": [ + "**/vs/**", + "*" // node modules + ] + }, + { + "target": "**/test/{node,electron-browser,electron-main}/**", + "restrictions": [ + "**/vs/**", + "*" // node modules + ] + }, + { + "target": "**/**.test.ts", + "restrictions": [ + "**/vs/**", + "assert", + "sinon", + "crypto", + "xterm*" + ] + }, + { + "target": "**/test/**", + "restrictions": [ + "**/vs/**", + "assert", + "sinon", + "crypto", + "xterm*" + ] } ] }, @@ -622,6 +959,51 @@ "rules": { "jsdoc/no-types": "off" } + }, + { + "files": [ + "**/vscode.d.ts", + "**/vscode.proposed.d.ts" + ], + "rules": { + "vscode-dts-create-func": "warn", + "vscode-dts-literal-or-types": "warn", + "vscode-dts-interface-naming": "warn", + "vscode-dts-event-naming": [ + "warn", + { + "allowed": [ + "onCancellationRequested", + "event" + ], + "verbs": [ + "accept", + "change", + "close", + "collapse", + "create", + "delete", + "dispose", + "edit", + "end", + "expand", + "hide", + "open", + "override", + "receive", + "register", + "rename", + "save", + "send", + "start", + "terminate", + "trigger", + "unregister", + "write" + ] + } + ] + } } ] } diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4b385f04183..acb1cb3d9c6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,6 +2,7 @@ name: Bug report about: Create a report to help us improve --- + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 2dc1460b16f..b9c6c83caa3 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,6 +4,7 @@ about: Suggest an idea for this project --- + diff --git a/.github/classifier.json b/.github/classifier.json new file mode 100644 index 00000000000..e4acc63c170 --- /dev/null +++ b/.github/classifier.json @@ -0,0 +1,182 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier-deep/apply/apply-labels/deep-classifier-config.schema.json", + "vacation": [], + "assignees": { + "JacksonKearl": {"accuracy": 0.5} + }, + "labels": { + "L10N": {"assign": []}, + "VIM": {"assign": []}, + "api": {"assign": ["jrieken"]}, + "api-finalization": {"assign": []}, + "api-proposal": {"assign": ["jrieken"]}, + "authentication": {"assign": ["RMacfarlane"]}, + "breadcrumbs": {"assign": ["jrieken"]}, + "callhierarchy": {"assign": ["jrieken"]}, + "code-lens": {"assign": ["jrieken"]}, + "color-palette": {"assign": []}, + "comments": {"assign": ["rebornix"]}, + "config": {"assign": ["sandy081"]}, + "context-keys": {"assign": []}, + "css-less-scss": {"assign": ["aeschli"]}, + "custom-editors": {"assign": ["mjbvz"]}, + "debug": {"assign": ["weinand"]}, + "debug-console": {"assign": ["weinand"]}, + "dialogs": {"assign": ["sbatten"]}, + "diff-editor": {"assign": []}, + "dropdown": {"assign": []}, + "editor": {"assign": ["rebornix"]}, + "editor-autoclosing": {"assign": []}, + "editor-autoindent": {"assign": ["rebornix"]}, + "editor-bracket-matching": {"assign": []}, + "editor-clipboard": {"assign": ["jrieken"]}, + "editor-code-actions": {"assign": []}, + "editor-color-picker": {"assign": ["rebornix"]}, + "editor-columnselect": {"assign": ["alexdima"]}, + "editor-commands": {"assign": ["jrieken"]}, + "editor-comments": {"assign": []}, + "editor-contrib": {"assign": []}, + "editor-core": {"assign": []}, + "editor-drag-and-drop": {"assign": ["rebornix"]}, + "editor-error-widget": {"assign": ["sandy081"]}, + "editor-find": {"assign": ["rebornix"]}, + "editor-folding": {"assign": ["aeschli"]}, + "editor-hover": {"assign": []}, + "editor-indent-guides": {"assign": []}, + "editor-input": {"assign": ["alexdima"]}, + "editor-input-IME": {"assign": ["rebornix"]}, + "editor-minimap": {"assign": []}, + "editor-multicursor": {"assign": ["alexdima"]}, + "editor-parameter-hints": {"assign": []}, + "editor-render-whitespace": {"assign": []}, + "editor-rendering": {"assign": ["alexdima"]}, + "editor-scrollbar": {"assign": []}, + "editor-symbols": {"assign": ["jrieken"]}, + "editor-synced-region": {"assign": ["aeschli"]}, + "editor-textbuffer": {"assign": ["rebornix"]}, + "editor-theming": {"assign": []}, + "editor-wordnav": {"assign": ["alexdima"]}, + "editor-wrapping": {"assign": ["alexdima"]}, + "emmet": {"assign": []}, + "error-list": {"assign": ["sandy081"]}, + "explorer-custom": {"assign": ["sandy081"]}, + "extension-host": {"assign": []}, + "extensions": {"assign": ["sandy081"]}, + "extensions-development": {"assign": []}, + "file-decorations": {"assign": ["jrieken"]}, + "file-encoding": {"assign": ["bpasero"]}, + "file-explorer": {"assign": ["isidorn"]}, + "file-glob": {"assign": []}, + "file-guess-encoding": {"assign": ["bpasero"]}, + "file-io": {"assign": ["bpasero"]}, + "file-watcher": {"assign": ["bpasero"]}, + "font-rendering": {"assign": []}, + "formatting": {"assign": []}, + "git": {"assign": ["joaomoreno"]}, + "gpu": {"assign": ["deepak1556"]}, + "grammar": {"assign": ["mjbvz"]}, + "grid-view": {"assign": ["joaomoreno"]}, + "html": {"assign": ["aeschli"]}, + "i18n": {"assign": []}, + "icon-brand": {"assign": []}, + "icons-product": {"assign": ["misolori"]}, + "install-update": {"assign": []}, + "integrated-terminal": {"assign": ["Tyriar"]}, + "integrated-terminal-conpty": {"assign": ["Tyriar"]}, + "integrated-terminal-links": {"assign": ["Tyriar"]}, + "integration-test": {"assign": []}, + "intellisense-config": {"assign": []}, + "ipc": {"assign": ["joaomoreno"]}, + "issue-bot": {"assign": ["chrmarti"]}, + "issue-reporter": {"assign": ["RMacfarlane"]}, + "javascript": {"assign": ["mjbvz"]}, + "json": {"assign": ["aeschli"]}, + "keybindings": {"assign": []}, + "keybindings-editor": {"assign": ["sandy081"]}, + "keyboard-layout": {"assign": ["alexdima"]}, + "languages-basic": {"assign": ["aeschli"]}, + "languages-diagnostics": {"assign": ["jrieken"]}, + "layout": {"assign": ["sbatten"]}, + "lcd-text-rendering": {"assign": []}, + "list": {"assign": ["joaomoreno"]}, + "log": {"assign": []}, + "markdown": {"assign": ["mjbvz"]}, + "marketplace": {"assign": []}, + "menus": {"assign": ["sbatten"]}, + "merge-conflict": {"assign": ["chrmarti"]}, + "notebook": {"assign": ["rebornix"]}, + "outline": {"assign": ["jrieken"]}, + "output": {"assign": []}, + "perf": {"assign": []}, + "perf-bloat": {"assign": []}, + "perf-startup": {"assign": []}, + "php": {"assign": ["roblourens"]}, + "portable-mode": {"assign": ["joaomoreno"]}, + "proxy": {"assign": []}, + "quick-pick": {"assign": ["chrmarti"]}, + "references-viewlet": {"assign": ["jrieken"]}, + "release-notes": {"assign": []}, + "remote": {"assign": []}, + "remote-explorer": {"assign": ["alexr00"]}, + "rename": {"assign": ["jrieken"]}, + "scm": {"assign": ["joaomoreno"]}, + "screencast-mode": {"assign": ["lszomoru"]}, + "search": {"assign": ["roblourens"]}, + "search-editor": {"assign": ["JacksonKearl"]}, + "search-replace": {"assign": ["sandy081"]}, + "semantic-tokens": {"assign": ["aeschli"]}, + "settings-editor": {"assign": ["roblourens"]}, + "settings-sync": {"assign": ["sandy081"]}, + "simple-file-dialog": {"assign": ["alexr00"]}, + "smart-select": {"assign": ["jrieken"]}, + "smoke-test": {"assign": []}, + "snap": {"assign": ["joaomoreno"]}, + "snippets": {"assign": ["jrieken"]}, + "splitview": {"assign": ["joaomoreno"]}, + "suggest": {"assign": ["jrieken"]}, + "tasks": {"assign": ["alexr00"], "accuracy": 0.85}, + "telemetry": {"assign": []}, + "themes": {"assign": ["aeschli"]}, + "timeline": {"assign": ["eamodio"]}, + "timeline-git": {"assign": ["eamodio"]}, + "titlebar": {"assign": ["sbatten"]}, + "tokenization": {"assign": []}, + "tree": {"assign": ["joaomoreno"]}, + "typescript": {"assign": ["mjbvz"]}, + "undo-redo": {"assign": []}, + "unit-test": {"assign": []}, + "uri": {"assign": ["jrieken"]}, + "ux": {"assign": ["misolori"]}, + "variable-resolving": {"assign": []}, + "vscode-build": {"assign": []}, + "web": {"assign": ["bpasero"]}, + "webview": {"assign": ["mjbvz"]}, + "workbench-cli": {"assign": []}, + "workbench-diagnostics": {"assign": ["RMacfarlane"]}, + "workbench-dnd": {"assign": ["bpasero"]}, + "workbench-editor-grid": {"assign": ["sbatten"]}, + "workbench-editors": {"assign": ["bpasero"]}, + "workbench-electron": {"assign": ["deepak1556"]}, + "workbench-feedback": {"assign": ["bpasero"]}, + "workbench-history": {"assign": ["bpasero"]}, + "workbench-hot-exit": {"assign": ["Tyriar"]}, + "workbench-launch": {"assign": []}, + "workbench-link": {"assign": []}, + "workbench-multiroot": {"assign": ["bpasero"]}, + "workbench-notifications": {"assign": ["bpasero"]}, + "workbench-os-integration": {"assign": []}, + "workbench-rapid-render": {"assign": ["jrieken"]}, + "workbench-run-as-admin": {"assign": []}, + "workbench-state": {"assign": ["bpasero"]}, + "workbench-status": {"assign": ["bpasero"]}, + "workbench-tabs": {"assign": ["bpasero"]}, + "workbench-touchbar": {"assign": ["bpasero"]}, + "workbench-views": {"assign": ["sbatten"]}, + "workbench-welcome": {"assign": ["chrmarti"]}, + "workbench-window": {"assign": ["bpasero"]}, + "workbench-zen": {"assign": ["isidorn"]}, + "workspace-edit": {"assign": ["jrieken"]}, + "workspace-symbols": {"assign": []}, + "zoom": {"assign": ["alexdima"] } + } +} diff --git a/.github/classifier.yml b/.github/classifier.yml deleted file mode 100644 index 8d750ed3baa..00000000000 --- a/.github/classifier.yml +++ /dev/null @@ -1,309 +0,0 @@ -{ - perform: true, - alwaysRequireAssignee: false, - labelsRequiringAssignee: [], - autoAssignees: { - L10N: [], - VIM: [], - api: { - assignees: [ jrieken ], - assignLabel: false - }, - cli: [], - color-palette: [], - config: [], - css-less-scss: [], - debug-console: [], - debug: { - assignees: [ weinand ], - assignLabel: false - }, - diff-editor: { - assignees: [], - assignLabel: false - }, - dropdown: [], - editor: { - assignees: [], - assignLabel: false - }, - editor-1000-limit: { - assignees: [], - assignLabel: false - }, - editor-autoclosing: { - assignees: [], - assignLabel: false - }, - editor-autoindent: { - assignees: [], - assignLabel: false - }, - editor-brackets: { - assignees: [], - assignLabel: false - }, - editor-clipboard: { - assignees: [], - assignLabel: false - }, - editor-code-actions: { - assignees: [], - assignLabel: false - }, - editor-code-lens: { - assignees: [], - assignLabel: false - }, - editor-color-picker: { - assignees: [], - assignLabel: false - }, - editor-colors: { - assignees: [], - assignLabel: false - }, - editor-columnselect: { - assignees: [], - assignLabel: false - }, - editor-commands: { - assignees: [], - assignLabel: false - }, - editor-contrib: { - assignees: [], - assignLabel: false - }, - editor-drag-and-drop: { - assignees: [], - assignLabel: false - }, - editor-find: { - assignees: [], - assignLabel: false - }, - editor-folding: { - assignees: [], - assignLabel: false - }, - editor-hover: { - assignees: [], - assignLabel: false - }, - editor-ime: { - assignees: [], - assignLabel: false - }, - editor-input: { - assignees: [], - assignLabel: false - }, - editor-ligatures: { - assignees: [], - assignLabel: false - }, - editor-links: { - assignees: [], - assignLabel: false - }, - editor-minimap: { - assignees: [], - assignLabel: false - }, - editor-multicursor: { - assignees: [], - assignLabel: false - }, - editor-parameter-hints: { - assignees: [], - assignLabel: false - }, - editor-rendering: { - assignees: [], - assignLabel: false - }, - editor-smooth: { - assignees: [], - assignLabel: false - }, - editor-symbols: { - assignees: [], - assignLabel: false - }, - editor-textbuffer: { - assignees: [], - assignLabel: false - }, - editor-wrapping: { - assignees: [], - assignLabel: false - }, - emmet: [ octref ], - error-list: [], - explorer-custom: [], - extension-host: [], - extensions: [], - extensions-development: [ octref ], - file-decorations: [], - file-encoding: { - assignees: [], - assignLabel: false - }, - file-explorer: { - assignees: [ isidorn ], - assignLabel: false - }, - file-glob: [], - file-io: { - assignees: [], - assignLabel: false - }, - file-watcher: { - assignees: [], - assignLabel: false - }, - formatting: [], - git: [], - grammar: [], - hot-exit: [], - html: [], - install-update: [], - integrated-terminal: [], - integration-test: [], - intellisense-config: [], - issue-reporter: [ RMacfarlane ], - javascript: [ mjbvz ], - json: [], - keyboard-layout: { - assignees: [], - assignLabel: false - }, - keybindings: { - assignees: [], - assignLabel: false - }, - keybindings-editor: [], - lang-diagnostics: [], - languages basic: [], - list: [], - log: [], - markdown: [ mjbvz ], - marketplace: [], - menus: [], - merge-conflict: [ chrmarti ], - multi-root: { - assignees: [], - assignLabel: false - }, - os-integration: [], - outline: [], - output: [], - perf-profile: [], - perf-bloat: [], - perf-startup: [], - php: [ roblourens ], - proxy: [], - quick-pick: [ chrmarti ], - release-notes: [], - remote: { - assignees: [ ], - assignLabel: false - }, - rename: [], - run-as-admin: [], - samples: [], - scm: [], - search: [ roblourens ], - search-replace: [], - settings-editor: [], - shared-process: [], - smart-select: [], - smoke-test: [], - snippets: { - assignees: [ jrieken ], - assignLabel: false - }, - suggest: [], - tasks: [ alexr00 ], - telemetry: [], - themes: [], - tokenization: [], - tree: [], - typescript: [ mjbvz ], - unit-test: [], - uri: [], - ux: [], - vscode-build: [], - webview: [], - workbench: { - assignees: [], - assignLabel: false - }, - workbench-diagnostics: { - assignees: [], - assignLabel: false - }, - workbench-dnd: { - assignees: [], - assignLabel: false - }, - workbench-editors: { - assignees: [], - assignLabel: false - }, - workbench-electron: { - assignees: [], - assignLabel: false - }, - workbench-feedback: { - assignees: [], - assignLabel: false - }, - workbench-grid: { - assignees: [], - assignLabel: false - }, - workbench-history: { - assignees: [], - assignLabel: false - }, - workbench-layout: { - assignees: [], - assignLabel: false - }, - workbench-menu: { - assignees: [], - assignLabel: false - }, - workbench-notifications: { - assignees: [], - assignLabel: false - }, - workbench-state: { - assignees: [], - assignLabel: false - }, - workbench-status: { - assignees: [], - assignLabel: false - }, - workbench-tabs: { - assignees: [], - assignLabel: false - }, - workbench-title: { - assignees: [], - assignLabel: false - }, - workbench-touchbar: { - assignees: [], - assignLabel: false - }, - workbench-views: { - assignees: [], - assignLabel: false - }, - workbench-welcome: [ chrmarti ] - } -} diff --git a/.github/commands.json b/.github/commands.json new file mode 100644 index 00000000000..7a1220f7d43 --- /dev/null +++ b/.github/commands.json @@ -0,0 +1,388 @@ +[ + { + "type": "comment", + "name": "question", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "updateLabels", + "addLabel": "*question" + }, + { + "type": "label", + "name": "*question", + "action": "close", + "comment": "Please ask your question on [StackOverflow](https://aka.ms/vscodestackoverflow). We have a great community over [there](https://aka.ms/vscodestackoverflow). They have already answered thousands of questions and are happy to answer yours as well. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + }, + { + "type": "label", + "name": "*dev-question", + "action": "close", + "comment": "We have a great developer community [over on slack](https://aka.ms/vscode-dev-community) where extension authors help each other. This is a great place for you to ask questions and find support.\n\nHappy Coding!" + }, + { + "type": "label", + "name": "*extension-candidate", + "action": "close", + "comment": "We try to keep VS Code lean and we think the functionality you're asking for is great for a VS Code extension. Maybe you can already find one that suits you in the [VS Code Marketplace](https://aka.ms/vscodemarketplace). Just in case, in a few simple steps you can get started [writing your own extension](https://aka.ms/vscodewritingextensions). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + }, + { + "type": "label", + "name": "*not-reproducible", + "action": "close", + "comment": "We closed this issue because we are unable to reproduce the problem with the steps you describe. Chances are we've already fixed your problem in a recent version of VS Code. If not, please ask us to reopen the issue and provide us with more detail. Our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines might help you with that.\n\nHappy Coding!" + }, + { + "type": "label", + "name": "*out-of-scope", + "action": "close", + "comment": "We closed this issue because we don't plan to address it in the foreseeable future. You can find more detailed information about our decision-making process [here](https://aka.ms/vscode-out-of-scope). If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!" + }, + { + "type": "comment", + "name": "causedByExtension", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "updateLabels", + "addLabel": "*caused-by-extension" + }, + { + "type": "label", + "name": "*caused-by-extension", + "action": "close", + "comment": "This issue is caused by an extension, please file it with the repository (or contact) the extension has linked in its overview in VS Code or the [marketplace](https://aka.ms/vscodemarketplace) for VS Code. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + }, + { + "type": "label", + "name": "*as-designed", + "action": "close", + "comment": "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "duplicate", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "updateLabels", + "addLabel": "*duplicate" + }, + { + "type": "label", + "name": "*duplicate", + "action": "close", + "comment": "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues [here](https://aka.ms/vscodeissuesearch). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "verified", + "allowUsers": [ + "@author" + ], + "action": "updateLabels", + "addLabel": "z-author-verified", + "removeLabel": "author-verification-requested", + "requireLabel": "author-verification-requested", + "disallowLabel": "awaiting-insiders-release" + }, + { + "type": "comment", + "name": "confirm", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "updateLabels", + "addLabel": "confirmed", + "removeLabel": "confirmation-pending" + }, + { + "type": "comment", + "name": "confirmationPending", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "updateLabels", + "addLabel": "confirmation-pending", + "removeLabel": "confirmed" + }, + { + "type": "comment", + "name": "needsMoreInfo", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "updateLabels", + "addLabel": "~needs more info" + }, + { + "type": "comment", + "name": "jsDebugLogs", + "action": "updateLabels", + "addLabel": "needs more info", + "comment": "Please collect trace logs using the following instructions:\n\n> If you're able to, add `\"trace\": true` to your `launch.json` and reproduce the issue. The location of the log file on your disk will be written to the Debug Console. Share that with us.\n>\n> ⚠️ This log file will not contain source code, but will contain file paths. You can drop it into https://microsoft.github.io/vscode-pwa-analyzer/index.html to see what it contains. If you'd rather not share the log publicly, you can email it to connor@xbox.com" + }, + { + "type": "comment", + "name": "closedWith", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "unreleased" + }, + { + "type": "label", + "name": "~needs more info", + "action": "updateLabels", + "addLabel": "needs more info", + "removeLabel": "~needs more info", + "comment": "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" + }, + { + "type": "label", + "name": "~needs version info", + "action": "updateLabels", + "addLabel": "needs more info", + "removeLabel": "~needs version info", + "comment": "Thanks for creating this issue! We figured it's missing some basic information, such as a version number, or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "a11ymas", + "allowUsers": [ + "AccessibilityTestingTeam-TCS", + "dixitsonali95", + "Mohini78", + "ChitrarupaSharma", + "mspatil110", + "umasarath52", + "v-umnaik" + ], + "action": "updateLabels", + "addLabel": "a11ymas" + }, + { + "type": "label", + "name": "*off-topic", + "action": "close", + "comment": "Thanks for creating this issue. We think this issue is unactionable or unrelated to the goals of this project. Please follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extPython", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Python extension. Please file it with the repository [here](https://github.com/microsoft/vscode-python). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extC", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the C extension. Please file it with the repository [here](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extC++", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the C++ extension. Please file it with the repository [here](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extCpp", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the C++ extension. Please file it with the repository [here](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extTS", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the TypeScript language service. Please file it with the repository [here](https://github.com/microsoft/TypeScript/). Make sure to check their [contributing guidelines](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extJS", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the TypeScript/JavaScript language service. Please file it with the repository [here](https://github.com/microsoft/TypeScript/). Make sure to check their [contributing guidelines](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extC#", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the C# extension. Please file it with the repository [here](https://github.com/OmniSharp/omnisharp-vscode.git). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extGo", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Go extension. Please file it with the repository [here](https://github.com/golang/vscode-go). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extPowershell", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the PowerShell extension. Please file it with the repository [here](https://github.com/PowerShell/vscode-powershell). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extLiveShare", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the LiveShare extension. Please file it with the repository [here](https://github.com/MicrosoftDocs/live-share). Make sure to check their [contributing guidelines](https://github.com/MicrosoftDocs/live-share/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extDocker", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Docker extension. Please file it with the repository [here](https://github.com/microsoft/vscode-docker). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extJava", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Java extension. Please file it with the repository [here](https://github.com/redhat-developer/vscode-java). Make sure to check their [troubleshooting instructions](https://github.com/redhat-developer/vscode-java/wiki/Troubleshooting) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "extJavaDebug", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Java Debugger extension. Please file it with the repository [here](https://github.com/microsoft/vscode-java-debug). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, + { + "type": "comment", + "name": "gifPlease", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "comment", + "comment": "Thanks for reporting this issue! Unfortunately, it's hard for us to understand what issue you're seeing. Please help us out by providing a screen recording showing exactly what isn't working as expected. While we can work with most standard formats, `.gif` files are preferred as they are displayed inline on GitHub. You may find https://gifcap.dev helpful as a browser-based gif recording tool.\n\nIf the issue depends on keyboard input, you can help us by enabling screencast mode for the recording (`Developer: Toggle Screencast Mode` in the command palette).\n\nHappy coding!" + }, + { + "type": "comment", + "name": "label", + "allowUsers": [] + }, + { + "type": "comment", + "name": "assign", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ] + } +] diff --git a/.github/commands.yml b/.github/commands.yml index 24ac951d6f0..64fdf683bfe 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -1,141 +1,12 @@ { perform: true, commands: [ - { - type: 'comment', - name: 'question', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'updateLabels', - addLabel: '*question' - }, - { - type: 'label', - name: '*question', - allowTriggerByBot: true, - action: 'close', - comment: "Please ask your question on [StackOverflow](https://aka.ms/vscodestackoverflow). We have a great community over [there](https://aka.ms/vscodestackoverflow). They have already answered thousands of questions and are happy to answer yours as well. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - }, - { - type: 'label', - name: '*dev-question', - allowTriggerByBot: true, - action: 'close', - comment: "We have a great developer community [over on slack](https://aka.ms/vscode-dev-community) where extension authors help each other. This is a great place for you to ask questions and find support.\n\nHappy Coding!" - }, - { - type: 'label', - name: '*extension-candidate', - allowTriggerByBot: true, - action: 'close', - comment: "We try to keep VS Code lean and we think the functionality you're asking for is great for a VS Code extension. Maybe you can already find one that suits you in the [VS Code Marketplace](https://aka.ms/vscodemarketplace). Just in case, in a few simple steps you can get started [writing your own extension](https://aka.ms/vscodewritingextensions). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - }, - { - type: 'label', - name: '*not-reproducible', - allowTriggerByBot: true, - action: 'close', - comment: "We closed this issue because we are unable to reproduce the problem with the steps you describe. Chances are we've already fixed your problem in a recent version of VS Code. If not, please ask us to reopen the issue and provide us with more detail. Our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines might help you with that.\n\nHappy Coding!" - }, - { - type: 'label', - name: '*out-of-scope', - allowTriggerByBot: true, - action: 'close', - comment: "We closed this issue because we don't plan to address it in the foreseeable future. You can find more detailed information about our decision-making process [here](https://aka.ms/vscode-out-of-scope). If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!" - }, - { - type: 'comment', - name: 'causedByExtension', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'updateLabels', - addLabel: '*caused-by-extension' - }, - { - type: 'label', - name: '*caused-by-extension', - allowTriggerByBot: true, - action: 'close', - comment: "This issue is caused by an extension, please file it with the repository (or contact) the extension has linked in its overview in VS Code or the [marketplace](https://aka.ms/vscodemarketplace) for VS Code. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - }, - { - type: 'label', - name: '*as-designed', - allowTriggerByBot: true, - action: 'close', - comment: "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - }, - { - type: 'label', - name: '*english-please', - allowTriggerByBot: true, - action: 'close', - comment: "This issue is being closed because its description is not in English, that makes it hard for us to work on it. Please open a new issue with an English description. You might find [Bing Translator](https://www.bing.com/translator) useful." - }, - { - type: 'comment', - name: 'duplicate', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'updateLabels', - addLabel: '*duplicate' - }, - { - type: 'label', - name: '*duplicate', - allowTriggerByBot: true, - action: 'close', - comment: "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues [here](https://aka.ms/vscodeissuesearch). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" - }, - { - type: 'comment', - name: 'confirm', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'updateLabels', - addLabel: 'confirmed', - removeLabel: 'confirmation-pending' - }, - { - type: 'comment', - name: 'confirmationPending', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'updateLabels', - addLabel: 'confirmation-pending', - removeLabel: 'confirmed' - }, { type: 'comment', name: 'findDuplicates', allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], action: 'comment', comment: "Potential duplicates:\n${potentialDuplicates}" - }, - { - type: 'comment', - name: 'needsMoreInfo', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'updateLabels', - addLabel: 'needs more info', - comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" - }, - { - type: 'label', - name: '~needs more info', - action: 'updateLabels', - addLabel: 'needs more info', - removeLabel: '~needs more info', - comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" - }, - { - type: 'comment', - name: 'a11ymas', - allowUsers: ['AccessibilityTestingTeam-TCS', 'dixitsonali95', 'Mohini78', 'ChitrarupaSharma', 'mspatil110', 'umasarath52', 'v-umnaik'], - action: 'updateLabels', - addLabel: 'a11ymas' - }, - { - type: 'label', - name: '*off-topic', - action: 'close', - comment: "Thanks for creating this issue. We think this issue is unactionable or unrelated to the goals of this project. Please follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" } ] } diff --git a/.github/copycat.yml b/.github/copycat.yml deleted file mode 100644 index 690c803bd0a..00000000000 --- a/.github/copycat.yml +++ /dev/null @@ -1,5 +0,0 @@ -{ - perform: true, - target_owner: 'chrmarti', - target_repo: 'testissues' -} diff --git a/.github/feature-requests.yml b/.github/feature-requests.yml deleted file mode 100644 index 18055b84486..00000000000 --- a/.github/feature-requests.yml +++ /dev/null @@ -1,34 +0,0 @@ -{ - typeLabel: { - name: 'feature-request' - }, - candidateMilestone: { - number: 107, - name: 'Backlog Candidates' - }, - approvedMilestone: { - number: 8, - name: 'Backlog' - }, - onLabeled: { - delay: 60, - perform: true - }, - onCandidateMilestoned: { - candidatesComment: "This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", - perform: true - }, - onMonitorUpvotes: { - upvoteThreshold: 20, - acceptanceComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", - perform: true - }, - onMonitorDaysOnCandidateMilestone: { - daysOnMilestone: 60, - warningPeriod: 10, - numberOfCommentsToPreventAutomaticRejection: 20, - rejectionComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", - warningComment: "This feature request has not yet received the 20 community upvotes it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding", - perform: true - } -} diff --git a/.github/locker.yml b/.github/locker.yml deleted file mode 100644 index 6d8feccae14..00000000000 --- a/.github/locker.yml +++ /dev/null @@ -1,6 +0,0 @@ -{ - daysAfterClose: 45, - daysSinceLastUpdate: 3, - ignoredLabels: ['*out-of-scope'], - perform: true -} diff --git a/.github/needs_more_info.yml b/.github/needs_more_info.yml deleted file mode 100644 index e5b2cddd880..00000000000 --- a/.github/needs_more_info.yml +++ /dev/null @@ -1,6 +0,0 @@ -{ - daysUntilClose: 7, - needsMoreInfoLabel: 'needs more info', - perform: true, - closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" -} diff --git a/.github/new_release.yml b/.github/new_release.yml deleted file mode 100644 index 7482b60b108..00000000000 --- a/.github/new_release.yml +++ /dev/null @@ -1,6 +0,0 @@ -{ - newReleaseLabel: 'new release', - newReleaseColor: '006b75', - daysAfterRelease: 5, - perform: true -} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 276121a227b..a2523237c79 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,9 +1,9 @@ - This PR fixes # diff --git a/.github/subscribers.json b/.github/subscribers.json new file mode 100644 index 00000000000..7ee6e5cdadd --- /dev/null +++ b/.github/subscribers.json @@ -0,0 +1,9 @@ +{ + "notebook": [ + "claudiaregio", + "rchiodo", + "greazer", + "donjayamanne", + "jilljac" + ] +} diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml new file mode 100644 index 00000000000..868fa8b9660 --- /dev/null +++ b/.github/workflows/author-verified.yml @@ -0,0 +1,40 @@ +name: Author Verified +on: + repository_dispatch: + types: [trigger-author-verified] + schedule: + - cron: 20 14 * * * # 4:20pm Zurich + issues: + types: [closed] + +# also make changes in ./on-label.yml +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') + run: npm install --production --prefix ./actions + - name: Checkout Repo + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') + uses: actions/checkout@v2 + with: + path: ./repo + fetch-depth: 0 + - name: Run Author Verified + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') + uses: ./actions/author-verified + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" + pendingReleaseLabel: awaiting-insiders-release + verifiedLabel: verified + authorVerificationRequestedLabel: author-verification-requested diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c9a7a74c52..d509cce2a33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,113 @@ on: - release/* jobs: - linux: + # linux: + # runs-on: ubuntu-latest + # env: + # CHILD_CONCURRENCY: "1" + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # steps: + # - uses: actions/checkout@v1 + # # TODO: rename azure-pipelines/linux/xvfb.init to github-actions + # - run: | + # sudo apt-get update + # sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + # sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + # sudo chmod +x /etc/init.d/xvfb + # sudo update-rc.d xvfb defaults + # sudo service xvfb start + # name: Setup Build Environment + # - uses: actions/setup-node@v1 + # with: + # node-version: 10 + # # TODO: cache node modules + # - run: yarn --frozen-lockfile + # name: Install Dependencies + # - run: yarn electron x64 + # name: Download Electron + # - run: yarn gulp hygiene + # name: Run Hygiene Checks + # - run: yarn monaco-compile-check + # name: Run Monaco Editor Checks + # - run: yarn valid-layers-check + # name: Run Valid Layers Checks + # - run: yarn compile + # name: Compile Sources + # - run: yarn download-builtin-extensions + # name: Download Built-in Extensions + # - run: DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" + # name: Run Unit Tests (Electron) + # - run: DISPLAY=:10 yarn test-browser --browser chromium + # name: Run Unit Tests (Browser) + # - run: DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" + # name: Run Integration Tests (Electron) + + # windows: + # runs-on: windows-2016 + # env: + # CHILD_CONCURRENCY: "1" + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # steps: + # - uses: actions/checkout@v1 + # - uses: actions/setup-node@v1 + # with: + # node-version: 10 + # - uses: actions/setup-python@v1 + # with: + # python-version: '2.x' + # - run: yarn --frozen-lockfile + # name: Install Dependencies + # - run: yarn electron + # name: Download Electron + # - run: yarn gulp hygiene + # name: Run Hygiene Checks + # - run: yarn monaco-compile-check + # name: Run Monaco Editor Checks + # - run: yarn valid-layers-check + # name: Run Valid Layers Checks + # - run: yarn compile + # name: Compile Sources + # - run: yarn download-builtin-extensions + # name: Download Built-in Extensions + # - run: .\scripts\test.bat --tfs "Unit Tests" + # name: Run Unit Tests (Electron) + # - run: yarn test-browser --browser chromium + # name: Run Unit Tests (Browser) + # - run: .\scripts\test-integration.bat --tfs "Integration Tests" + # name: Run Integration Tests (Electron) + + # darwin: + # runs-on: macos-latest + # env: + # CHILD_CONCURRENCY: "1" + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # steps: + # - uses: actions/checkout@v1 + # - uses: actions/setup-node@v1 + # with: + # node-version: 10 + # - run: yarn --frozen-lockfile + # name: Install Dependencies + # - run: yarn electron x64 + # name: Download Electron + # - run: yarn gulp hygiene + # name: Run Hygiene Checks + # - run: yarn monaco-compile-check + # name: Run Monaco Editor Checks + # - run: yarn valid-layers-check + # name: Run Valid Layers Checks + # - run: yarn compile + # name: Compile Sources + # - run: yarn download-builtin-extensions + # name: Download Built-in Extensions + # - run: ./scripts/test.sh --tfs "Unit Tests" + # name: Run Unit Tests (Electron) + # - run: yarn test-browser --browser chromium --browser webkit + # name: Run Unit Tests (Browser) + # - run: ./scripts/test-integration.sh --tfs "Integration Tests" + # name: Run Integration Tests (Electron) + + monaco: runs-on: ubuntu-latest env: CHILD_CONCURRENCY: "1" @@ -21,7 +127,7 @@ jobs: # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - run: | sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb sudo chmod +x /etc/init.d/xvfb sudo update-rc.d xvfb defaults @@ -30,83 +136,9 @@ jobs: - uses: actions/setup-node@v1 with: node-version: 10 - # TODO: cache node modules - run: yarn --frozen-lockfile name: Install Dependencies - - run: yarn electron x64 - name: Download Electron - - run: yarn gulp hygiene - name: Run Hygiene Checks - run: yarn monaco-compile-check name: Run Monaco Editor Checks - - run: yarn valid-layers-check - name: Run Valid Layers Checks - - run: yarn compile - name: Compile Sources - - run: yarn download-builtin-extensions - name: Download Built-in Extensions - - run: DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" - name: Run Unit Tests - - run: DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" - name: Run Integration Tests - - windows: - runs-on: windows-2016 - env: - CHILD_CONCURRENCY: "1" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: 10 - - uses: actions/setup-python@v1 - with: - python-version: '2.x' - - run: yarn --frozen-lockfile - name: Install Dependencies - - run: yarn electron - name: Download Electron - - run: yarn gulp hygiene - name: Run Hygiene Checks - - run: yarn monaco-compile-check - name: Run Monaco Editor Checks - - run: yarn valid-layers-check - name: Run Valid Layers Checks - - run: yarn compile - name: Compile Sources - - run: yarn download-builtin-extensions - name: Download Built-in Extensions - - run: .\scripts\test.bat --tfs "Unit Tests" - name: Run Unit Tests - - run: .\scripts\test-integration.bat --tfs "Integration Tests" - name: Run Integration Tests - - darwin: - runs-on: macos-latest - env: - CHILD_CONCURRENCY: "1" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: 10 - - run: yarn --frozen-lockfile - name: Install Dependencies - - run: yarn electron x64 - name: Download Electron - - run: yarn gulp hygiene - name: Run Hygiene Checks - - run: yarn monaco-compile-check - name: Run Monaco Editor Checks - - run: yarn valid-layers-check - name: Run Valid Layers Checks - - run: yarn compile - name: Compile Sources - - run: yarn download-builtin-extensions - name: Download Built-in Extensions - - run: ./scripts/test.sh --tfs "Unit Tests" - name: Run Unit Tests - - run: ./scripts/test-integration.sh --tfs "Integration Tests" - name: Run Integration Tests + - run: yarn gulp editor-esm-bundle + name: Editor Distro & ESM Bundle diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..017708844a4 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,49 @@ +name: "Code Scanning" + +on: + schedule: + - cron: '0 0 * * 2' + +jobs: + CodeQL-Build: + + # CodeQL runs on ubuntu-latest and windows-latest + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: javascript + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml new file mode 100644 index 00000000000..e538b86d549 --- /dev/null +++ b/.github/workflows/commands.yml @@ -0,0 +1,24 @@ +name: Commands +on: + issue_comment: + types: [created] + +# also make changes in ./on-label.yml +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + path: ./actions + ref: v39 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run Commands + uses: ./actions/commands + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + config-path: commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml new file mode 100644 index 00000000000..7d8a3c6f20f --- /dev/null +++ b/.github/workflows/deep-classifier-monitor.yml @@ -0,0 +1,23 @@ +name: "Deep Classifier: Monitor" +on: + issues: + types: [unassigned] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: "Run Classifier: Monitor" + uses: ./actions/classifier-deep/monitor + with: + botName: vscode-triage-bot + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml new file mode 100644 index 00000000000..7c666c6f76c --- /dev/null +++ b/.github/workflows/deep-classifier-runner.yml @@ -0,0 +1,50 @@ +name: "Deep Classifier: Runner" +on: + schedule: + - cron: 0 * * * * + repository_dispatch: + types: [trigger-deep-classifier-runner] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Install Additional Dependencies + # Pulls in a bunch of other packages that arent needed for the rest of the actions + run: npm install @azure/storage-blob@12.1.1 + - name: "Run Classifier: Scraper" + uses: ./actions/classifier-deep/apply/fetch-sources + with: + # slightly overlapping to protect against issues slipping through the cracks if a run is delayed + from: 80 + until: 5 + configPath: classifier + blobContainerName: vscode-issue-classifier + blobStorageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade numpy scipy scikit-learn joblib nltk simpletransformers torch torchvision + - name: "Run Classifier: Generator" + run: python ./actions/classifier-deep/apply/generate-labels/main.py + - name: "Run Classifier: Labeler" + uses: ./actions/classifier-deep/apply/apply-labels + with: + configPath: classifier + allowLabels: "needs more info|new release" + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml new file mode 100644 index 00000000000..822e58ff8b1 --- /dev/null +++ b/.github/workflows/deep-classifier-scraper.yml @@ -0,0 +1,27 @@ +name: "Deep Classifier: Scraper" +on: + repository_dispatch: + types: [trigger-deep-classifier-scraper] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Install Additional Dependencies + # Pulls in a bunch of other packages that arent needed for the rest of the actions + run: npm install @azure/storage-blob@12.1.1 + - name: "Run Classifier: Scraper" + uses: ./actions/classifier-deep/train/fetch-issues + with: + blobContainerName: vscode-issue-classifier + blobStorageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} + token: ${{secrets.ISSUE_SCRAPER_TOKEN}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} diff --git a/.github/workflows/devcontainer-cache.yml b/.github/workflows/devcontainer-cache.yml new file mode 100644 index 00000000000..82290a475ee --- /dev/null +++ b/.github/workflows/devcontainer-cache.yml @@ -0,0 +1,41 @@ +name: VS Code Repo Dev Container Cache Image Generation + +on: + push: + # Currently doing this for master, but could be done for PRs as well + branches: + - 'master' + + # Only updates to these files result in changes to installed packages, so skip otherwise + paths: + - '**/package-lock.json' + - '**/yarn.lock' + +jobs: + devcontainer: + name: Generate cache image + runs-on: ubuntu-latest + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v2 + + - name: Azure CLI login + id: az_login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZ_ACR_CREDS }} + + - name: Build and push + id: build_and_push + run: | + set -e + + ACR_REGISTRY_NAME=$(echo ${{ secrets.CONTAINER_IMAGE_REGISTRY }} | grep -oP '(.+)(?=\.azurecr\.io)') + az acr login --name $ACR_REGISTRY_NAME + + GIT_BRANCH=$(echo "${{ github.ref }}" | grep -oP 'refs/(heads|tags)/\K(.+)') + if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=master; fi + + .devcontainer/cache/build-cache-image.sh "${{ secrets.CONTAINER_IMAGE_REGISTRY }}/public/vscode/devcontainers/repos/microsoft/vscode" "${GIT_BRANCH}" + diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml new file mode 100644 index 00000000000..b1fc4b1f0ae --- /dev/null +++ b/.github/workflows/english-please.yml @@ -0,0 +1,31 @@ +name: English Please +on: + issues: + types: [edited] + +# also make changes in ./on-label.yml and ./on-open.yml +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + if: contains(github.event.issue.labels.*.name, '*english-please') + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + if: contains(github.event.issue.labels.*.name, '*english-please') + run: npm install --production --prefix ./actions + - name: Run English Please + if: contains(github.event.issue.labels.*.name, '*english-please') + uses: ./actions/english-please + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + cognitiveServicesAPIKey: ${{secrets.AZURE_TEXT_TRANSLATOR_KEY}} + nonEnglishLabel: "*english-please" + needsMoreInfoLabel: "needs more info" + translatorRequestedLabelPrefix: "translation-required-" + translatorRequestedLabelColor: "c29cff" diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml new file mode 100644 index 00000000000..0e641ae8d32 --- /dev/null +++ b/.github/workflows/feature-request.yml @@ -0,0 +1,43 @@ +name: Feature Request Manager +on: + repository_dispatch: + types: [trigger-feature-request-manager] + issues: + types: [milestoned] + schedule: + - cron: 20 2 * * * # 4:20am Zurich + +# also make changes in ./on-label.yml +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + path: ./actions + ref: v39 + - name: Install Actions + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') + run: npm install --production --prefix ./actions + - name: Run Feature Request Manager + if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') + uses: ./actions/feature-request + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + candidateMilestoneID: 107 + candidateMilestoneName: Backlog Candidates + backlogMilestoneID: 8 + featureRequestLabel: feature-request + upvotesRequired: 20 + numCommentsOverride: 20 + initComment: "This feature request is now a candidate for our backlog. The community has 60 days to [upvote](https://github.com/microsoft/vscode/wiki/Issues-Triaging#up-voting-a-feature-request) the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + warnComment: "This feature request has not yet received the 20 community [upvotes](https://github.com/microsoft/vscode/wiki/Issues-Triaging#up-voting-a-feature-request) it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + acceptComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + rejectComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + warnDays: 10 + closeDays: 60 + milestoneDelaySeconds: 60 diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml new file mode 100644 index 00000000000..c47e2a97b1b --- /dev/null +++ b/.github/workflows/latest-release-monitor.yml @@ -0,0 +1,27 @@ +name: Latest Release Monitor +on: + schedule: + - cron: 0/5 * * * * + repository_dispatch: + types: [trigger-latest-release-monitor] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + path: ./actions + ref: v39 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Install Storage Module + run: npm install @azure/storage-blob@12.1.1 + - name: Run Latest Release Monitor + uses: ./actions/latest-release-monitor + with: + storageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml new file mode 100644 index 00000000000..8549034111f --- /dev/null +++ b/.github/workflows/locker.yml @@ -0,0 +1,26 @@ +name: Locker +on: + schedule: + - cron: 20 23 * * * # 4:20pm Redmond + repository_dispatch: + types: [trigger-locker] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + path: ./actions + ref: v39 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run Locker + uses: ./actions/locker + with: + daysSinceClose: 45 + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + daysSinceUpdate: 3 + ignoredLabel: "*out-of-scope" diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml new file mode 100644 index 00000000000..bdc16d36615 --- /dev/null +++ b/.github/workflows/needs-more-info-closer.yml @@ -0,0 +1,30 @@ +name: Needs More Info Closer +on: + schedule: + - cron: 20 11 * * * # 4:20am Redmond + repository_dispatch: + types: [trigger-needs-more-info] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + path: ./actions + ref: v39 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run Needs More Info Closer + uses: ./actions/needs-more-info-closer + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + label: needs more info + closeDays: 7 + additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" + closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + pingDays: 80 + pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml new file mode 100644 index 00000000000..1ce9e526488 --- /dev/null +++ b/.github/workflows/on-label.yml @@ -0,0 +1,94 @@ +name: On Label +on: + issues: + types: [labeled] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + + # source of truth in ./author-verified.yml + - name: Checkout Repo + if: contains(github.event.issue.labels.*.name, 'author-verification-requested') + uses: actions/checkout@v2 + with: + path: ./repo + fetch-depth: 0 + - name: Run Author Verified + if: contains(github.event.issue.labels.*.name, 'author-verification-requested') + uses: ./actions/author-verified + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by confirming things are working as expected in the latest Insiders release. If things look good, please leave a comment with the text `/verified` to let us know. If not, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallete to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" + pendingReleaseLabel: awaiting-insiders-release + verifiedLabel: verified + authorVerificationRequestedLabel: author-verification-requested + + # source of truth in ./commands.yml + - name: Run Commands + uses: ./actions/commands + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + config-path: commands + + # only here. + - name: Run Subscribers + uses: ./actions/topic-subscribe + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + config-path: subscribers + + # source of truth in ./feature-request.yml + - name: Run Feature Request Manager + if: contains(github.event.issue.labels.*.name, 'feature-request') + uses: ./actions/feature-request + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + candidateMilestoneID: 107 + candidateMilestoneName: Backlog Candidates + backlogMilestoneID: 8 + featureRequestLabel: feature-request + upvotesRequired: 20 + numCommentsOverride: 20 + initComment: "This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + warnComment: "This feature request has not yet received the 20 community upvotes it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding" + acceptComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + rejectComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!" + warnDays: 10 + closeDays: 60 + milestoneDelaySeconds: 60 + + # source of truth in ./test-plan-item-validator.yml + - name: Run Test Plan Item Validator + if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') + uses: ./actions/test-plan-item-validator + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + label: testplan-item + invalidLabel: invalid-testplan-item + comment: Invalid test plan item. See errors below and the [test plan item spec](https://github.com/microsoft/vscode/wiki/Writing-Test-Plan-Items) for more information. This comment will go away when the issues are resolved. + + # source of truth in ./english-please.yml + - name: Run English Please + if: contains(github.event.issue.labels.*.name, '*english-please') + uses: ./actions/english-please + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + cognitiveServicesAPIKey: ${{secrets.AZURE_TEXT_TRANSLATOR_KEY}} + nonEnglishLabel: "*english-please" + needsMoreInfoLabel: "needs more info" + translatorRequestedLabelPrefix: "translation-required-" + translatorRequestedLabelColor: "c29cff" diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml new file mode 100644 index 00000000000..dcb7ca628a9 --- /dev/null +++ b/.github/workflows/on-open.yml @@ -0,0 +1,69 @@ +name: On Open +on: + issues: + types: [opened] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + + - name: Run CopyCat (JacksonKearl/testissues) + uses: ./actions/copycat + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + owner: JacksonKearl + repo: testissues + - name: Run CopyCat (chrmarti/testissues) + uses: ./actions/copycat + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + owner: chrmarti + repo: testissues + + - name: Run New Release + uses: ./actions/new-release + with: + label: new release + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + labelColor: "006b75" + labelDescription: Issues found in a recent release of VS Code + days: 5 + + - name: Run Clipboard Labeler + uses: ./actions/regex-labeler + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + label: "invalid" + mustNotMatch: "^We have written the needed data into your clipboard because it was too large to send\\. Please paste\\.$" + comment: "It looks like you're using the VS Code Issue Reporter but did not paste the text generated into the created issue. We've closed this issue, please open a new one containing the text we placed in your clipboard.\n\nHappy Coding!" + + - name: Run Clipboard Labeler (Chinese) + uses: ./actions/regex-labeler + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + label: "invalid" + mustNotMatch: "^所需的数据太大,无法直接发送。我们已经将其写入剪贴板,请粘贴。$" + comment: "看起来您正在使用 VS Code 问题报告程序,但是没有将生成的文本粘贴到创建的问题中。我们将关闭这个问题,请使用剪贴板中的内容创建一个新的问题。\n\n祝您使用愉快!" + + # source of truth in ./english-please.yml + - name: Run English Please + uses: ./actions/english-please + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + cognitiveServicesAPIKey: ${{secrets.AZURE_TEXT_TRANSLATOR_KEY}} + nonEnglishLabel: "*english-please" + needsMoreInfoLabel: "needs more info" + translatorRequestedLabelPrefix: "translation-required-" + translatorRequestedLabelColor: "c29cff" diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml new file mode 100644 index 00000000000..e8882500db1 --- /dev/null +++ b/.github/workflows/release-pipeline-labeler.yml @@ -0,0 +1,32 @@ +name: "Release Pipeline Labeler" +on: + issues: + types: [closed, reopened] + repository_dispatch: + types: [released-insider] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v39 + path: ./actions + - name: Checkout Repo + if: github.event_name != 'issues' + uses: actions/checkout@v2 + with: + path: ./repo + fetch-depth: 0 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: "Run Release Pipeline Labeler" + uses: ./actions/release-pipeline + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + notYetReleasedLabel: unreleased + insidersReleasedLabel: insiders-released diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml new file mode 100644 index 00000000000..bd2444b608b --- /dev/null +++ b/.github/workflows/rich-navigation.yml @@ -0,0 +1,32 @@ +name: "Rich Navigation Indexing" +on: + pull_request: + push: + branches: + - master + +jobs: + richnav: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + + - uses: actions/cache@v2 + id: caching-stage + name: Cache VS Code dependencies + with: + path: node_modules + key: ${{ runner.os }}-dependencies-${{ hashfiles('yarn.lock') }} + restore-keys: ${{ runner.os }}-dependencies- + + - name: Install dependencies + if: steps.caching-stage.outputs.cache-hit != 'true' + run: yarn --frozen-lockfile + env: + CHILD_CONCURRENCY: 1 + + - uses: microsoft/RichCodeNavIndexer@v0.1 + with: + languages: typescript + repo-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml new file mode 100644 index 00000000000..d7c10b2f4bf --- /dev/null +++ b/.github/workflows/test-plan-item-validator.yml @@ -0,0 +1,28 @@ +name: Test Plan Item Validator +on: + issues: + types: [edited] + +# also edit in ./on-label.yml +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + path: ./actions + ref: v39 + - name: Install Actions + if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') + run: npm install --production --prefix ./actions + - name: Run Test Plan Item Validator + if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') + uses: ./actions/test-plan-item-validator + with: + label: testplan-item + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + invalidLabel: invalid-testplan-item + comment: Invalid test plan item. See errors below and the [test plan item spec](https://github.com/microsoft/vscode/wiki/Writing-Test-Plan-Items) for more information. This comment will go away when the issues are resolved. diff --git a/.gitignore b/.gitignore index 160c42ed74b..b7f5b58c8ed 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ out-editor/ out-editor-src/ out-editor-build/ out-editor-esm/ +out-editor-esm-bundle/ out-editor-min/ out-monaco-editor-core/ out-vscode/ @@ -23,6 +24,7 @@ out-vscode-reh-web-min/ out-vscode-reh-web-pkg/ out-vscode-web/ out-vscode-web-min/ +out-vscode-web-pkg/ src/vs/server resources/server build/node_modules @@ -30,3 +32,5 @@ coverage/ test_data/ test-results/ yarn-error.log +vscode.lsif +vscode.db diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index f599e28b8ab..00000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 91855cc846d..00000000000 --- a/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "useTabs": true, - "printWidth": 120, - "semi": true, - "singleQuote": true -} diff --git a/.vscode/launch.json b/.vscode/launch.json index 13c87d635f3..646b99c7943 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,19 +14,25 @@ { "type": "node", "request": "attach", - "name": "Attach to Extension Host", - "port": 5870, "restart": true, + "name": "Attach to Extension Host", + "timeout": 30000, + "port": 5870, "outFiles": [ - "${workspaceFolder}/out/**/*.js" + "${workspaceFolder}/out/**/*.js", + "${workspaceFolder}/extensions/*/out/**/*.js" ] }, { - "type": "chrome", + "type": "pwa-chrome", "request": "attach", "name": "Attach to Shared Process", + "timeout": 30000, "port": 9222, - "urlFilter": "*" + "urlFilter": "*sharedProcess.html*", + "presentation": { + "hidden": true + } }, { "type": "node", @@ -53,6 +59,7 @@ "type": "node", "request": "attach", "name": "Attach to Main Process", + "timeout": 30000, "port": 5875, "outFiles": [ "${workspaceFolder}/out/**/*.js" @@ -79,6 +86,24 @@ "order": 6 } }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code Git Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "/tmp/my4g9l", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/git", + "--extensionTestsPath=${workspaceFolder}/extensions/git/out/test" + ], + "outFiles": [ + "${workspaceFolder}/extensions/git/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, { "type": "extensionHost", "request": "launch", @@ -135,13 +160,51 @@ } }, { - "type": "chrome", - "request": "attach", - "name": "Attach to VS Code", - "port": 9222 + "type": "extensionHost", + "request": "launch", + "name": "VS Code Notebook Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/vscode-notebook-tests/test", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-notebook-tests", + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-notebook-tests/out" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } }, { - "type": "chrome", + "type": "extensionHost", + "request": "launch", + "name": "VS Code Custom Editor Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/vscode-custom-editor-tests/test-workspace", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-custom-editor-tests", + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-custom-editor-tests/out/test" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, + { + "type": "pwa-chrome", + "request": "attach", + "name": "Attach to VS Code", + "browserAttachLocation": "workspace", + "port": 9222, + "perScriptSourcemaps": "yes" + }, + { + "type": "pwa-chrome", "request": "launch", "name": "Launch VS Code", "windows": { @@ -156,23 +219,41 @@ "port": 9222, "timeout": 20000, "env": { - "VSCODE_EXTHOST_WILL_SEND_SOCKET": null + "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, + "VSCODE_SKIP_PRELAUNCH": "1" }, - "breakOnLoad": false, + "cleanUp": "wholeBrowser", "urlFilter": "*workbench.html*", "runtimeArgs": [ "--inspect=5875", "--no-cached-data", ], "webRoot": "${workspaceFolder}", - // Settings for js-debug: + "cascadeTerminateToConfigurations": [ + "Attach to Extension Host" + ], + "userDataDir": false, "pauseForSourceMap": false, - "outFiles": ["${workspaceFolder}/out/**/*.js"], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "browserLaunchLocation": "workspace", + "preLaunchTask": "Ensure Prelaunch Dependencies", }, { "type": "node", "request": "launch", - "name": "Launch VS Code (Main Process)", + "name": "VS Code (Web)", + "program": "${workspaceFolder}/resources/web/code-web.js", + "presentation": { + "group": "0_vscode", + "order": 2 + } + }, + { + "type": "node", + "request": "launch", + "name": "Main Process", "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", "windows": { "runtimeExecutable": "${workspaceFolder}/scripts/code.bat", @@ -184,31 +265,34 @@ "${workspaceFolder}/out/**/*.js" ], "presentation": { - "group": "2_launch", + "group": "1_vscode", "order": 1 } }, { - "type": "node", + "type": "pwa-chrome", "request": "launch", - "name": "Launch VS Code (Web)", - "runtimeExecutable": "yarn", - "runtimeArgs": [ - "web" - ], - "presentation": { - "group": "2_launch", - "order": 2 - } - }, - { - "type": "chrome", - "request": "launch", - "name": "Launch VS Code (Web, Chrome)", + "outFiles": [], + "perScriptSourcemaps": "yes", + "name": "VS Code (Web, Chrome)", "url": "http://localhost:8080", "preLaunchTask": "Run web", "presentation": { - "group": "2_launch", + "group": "0_vscode", + "order": 3 + } + }, + { + "type": "pwa-msedge", + "request": "launch", + "outFiles": [], + "perScriptSourcemaps": "yes", + "name": "VS Code (Web, Edge)", + "url": "http://localhost:8080", + "pauseForSourceMap": false, + "preLaunchTask": "Run web", + "presentation": { + "group": "0_vscode", "order": 3 } }, @@ -230,7 +314,7 @@ { "type": "node", "request": "launch", - "name": "HTML Unit Tests", + "name": "HTML Server Unit Tests", "program": "${workspaceFolder}/extensions/html-language-features/server/test/index.js", "stopOnEntry": false, "cwd": "${workspaceFolder}/extensions/html-language-features/server", @@ -242,13 +326,28 @@ "order": 10 } }, + { + "type": "node", + "request": "launch", + "name": "CSS Server Unit Tests", + "program": "${workspaceFolder}/extensions/css-language-features/server/test/index.js", + "stopOnEntry": false, + "cwd": "${workspaceFolder}/extensions/css-language-features/server", + "outFiles": [ + "${workspaceFolder}/extensions/css-language-features/server/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 10 + } + }, { "type": "extensionHost", "request": "launch", "name": "Markdown Extension Tests", "runtimeExecutable": "${execPath}", "args": [ - "${workspaceFolder}/extensions/markdown-language-features/test-fixtures", + "${workspaceFolder}/extensions/markdown-language-features/test-workspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/markdown-language-features", "--extensionTestsPath=${workspaceFolder}/extensions/markdown-language-features/out/test" ], @@ -266,7 +365,7 @@ "name": "TypeScript Extension Tests", "runtimeExecutable": "${execPath}", "args": [ - "${workspaceFolder}/extensions/typescript-language-features/test-fixtures", + "${workspaceFolder}/extensions/typescript-language-features/test-workspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/typescript-language-features", "--extensionTestsPath=${workspaceFolder}/extensions/typescript-language-features/out/test" ], @@ -279,10 +378,10 @@ } }, { - "type": "node", + "type": "pwa-node", "request": "launch", "name": "Run Unit Tests", - "program": "${workspaceFolder}/test/electron/index.js", + "program": "${workspaceFolder}/test/unit/electron/index.js", "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", "windows": { "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe" @@ -298,6 +397,41 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], + "cascadeTerminateToConfigurations": [ + "Attach to VS Code" + ], + "env": { + "MOCHA_COLORS": "true" + }, + "presentation": { + "hidden": true + } + }, + { + "type": "pwa-node", + "request": "launch", + "name": "Run Unit Tests For Current File", + "program": "${workspaceFolder}/test/unit/electron/index.js", + "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", + "windows": { + "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe" + }, + "linux": { + "runtimeExecutable": "${workspaceFolder}/.build/electron/code-oss" + }, + "cascadeTerminateToConfigurations": [ + "Attach to VS Code" + ], + "outputCapture": "std", + "args": [ + "--remote-debugging-port=9222", + "--run", + "${relativeFile}" + ], + "cwd": "${workspaceFolder}", + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], "env": { "MOCHA_COLORS": "true" }, @@ -338,14 +472,17 @@ ], "compounds": [ { - "name": "Debug VS Code Main, Renderer & Extension Host", + "name": "VS Code", + "stopAll": true, "configurations": [ "Launch VS Code", "Attach to Main Process", - "Attach to Extension Host" + "Attach to Extension Host", + "Attach to Shared Process", ], + "preLaunchTask": "Ensure Prelaunch Dependencies", "presentation": { - "group": "1_vscode", + "group": "0_vscode", "order": 1 } }, @@ -381,6 +518,17 @@ "group": "1_vscode", "order": 2 } + }, + { + "name": "Debug Unit Tests (Current File)", + "configurations": [ + "Attach to VS Code", + "Run Unit Tests For Current File" + ], + "presentation": { + "group": "1_vscode", + "order": 2 + } } ] } diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues new file mode 100644 index 00000000000..8ff55e2c6ee --- /dev/null +++ b/.vscode/notebooks/api.github-issues @@ -0,0 +1,38 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "#### Config", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"November 2020\"", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Finalization", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo $milestone label:api-finalization", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Proposals", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo $milestone is:open label:api-proposal ", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues new file mode 100644 index 00000000000..0d6e4533429 --- /dev/null +++ b/.vscode/notebooks/endgame.github-issues @@ -0,0 +1,182 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "# Endgame", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Macros", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github\n\n$MILESTONE=milestone:\"October 2020\"\n\n$MINE=assignee:@me", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Endgame Champion", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE label:testplan-item is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Feature Requests Missing Labels", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Open Issues on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -label:testplan-item -label:iteration-plan -label:endgame-plan is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Testing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Assigned Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE label:testplan-item is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Created Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE author:@me label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Verification", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Issues to Verify", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Issues filed by me", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Issues filed by others", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE -author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Verification Needed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate ", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Issues Needing Verification", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE sort:updated-desc is:closed is:issue -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found\n", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### My Open Issues", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:open is:issue", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Documentation", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Needing Release Notes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode $MILESTONE $MINE is:closed label:feature-request -label:on-release-notes", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues new file mode 100644 index 00000000000..f44b2c71eed --- /dev/null +++ b/.vscode/notebooks/grooming.github-issues @@ -0,0 +1,26 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "### Categorizing Issues\n\nEach issue must have a type label. Most type labels are grey, some are yellow. Bugs are grey with a touch of red.", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode is:open is:issue assignee:@me -label:\"needs more info\" -label:bug -label:feature-request -label:under-discussion -label:debt -label:*question -label:upstream -label:electron -label:engineering -label:plan-item ", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Feature Areas\n\nEach issue should be assigned to a feature area", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode is:open is:issue assignee:@me -label:L10N -label:VIM -label:api -label:api-finalization -label:api-proposal -label:authentication -label:breadcrumbs -label:callhierarchy -label:code-lens -label:color-palette -label:comments -label:config -label:context-keys -label:css-less-scss -label:custom-editors -label:debug -label:debug-console -label:dialogs -label:diff-editor -label:dropdown -label:editor -label:editor-RTL -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-scrollbar -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:error-list -label:explorer-custom -label:extension-host -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-guess-encoding -label:file-io -label:file-watcher -label:font-rendering -label:formatting -label:git -label:github -label:gpu -label:grammar -label:grid-view -label:html -label:i18n -label:icon-brand -label:icons-product -label:install-update -label:integrated-terminal -label:integrated-terminal-conpty -label:integrated-terminal-links -label:integrated-terminal-rendering -label:integrated-terminal-winpty -label:intellisense-config -label:ipc -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:notebook -label:outline -label:output -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-explorer -label:rename -label:sandbox -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview -label:suggest -label:sync-error-handling -label:tasks -label:telemetry -label:themes -label:timeline -label:timeline-git -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree -label:typescript -label:undo-redo -label:uri -label:ux -label:variable-resolving -label:vscode-build -label:vscode-website -label:web -label:webview -label:workbench-actions -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:zoom", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues new file mode 100644 index 00000000000..c7134b3a289 --- /dev/null +++ b/.vscode/notebooks/inbox.github-issues @@ -0,0 +1,50 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "## tl;dr: Triage Inbox\n\nAll inbox issues but not those that need more information. These issues need to be triaged, e.g assigned to a user or ask for more information", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$inbox -label:\"needs more info\"", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "##### `Config`: defines the inbox query", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item ", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Inbox tracking and Issue triage", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/master/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## All Inbox Items\n\nAll issues that have no assignee and that have neither **feature requests** nor **test plan items** nor **plan items**.", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$inbox", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues new file mode 100644 index 00000000000..a74bfd329f1 --- /dev/null +++ b/.vscode/notebooks/my-work.github-issues @@ -0,0 +1,98 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "##### `Config`: This should be changed every month/milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"November 2020\"", + "editable": true + }, + { + "kind": 1, + "language": "github-issues", + "value": "## Milestone Work", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone assignee:@me is:open", + "editable": false + }, + { + "kind": 1, + "language": "github-issues", + "value": "## Bugs, Debt, Features...", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### My Bugs", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:bug", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Debt & Engineering", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Performance 🐌 🔜 🏎", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Feature Requests", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Not Actionable", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:\"needs more info\"", + "editable": false + } +] \ No newline at end of file diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues new file mode 100644 index 00000000000..67db0cb97d4 --- /dev/null +++ b/.vscode/notebooks/verification.github-issues @@ -0,0 +1,56 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "### Bug Verification Queries\n\nBefore shipping we want to verify _all_ bugs. That means when a bug is fixed we check that the fix actually works. It's always best to start with bugs that you have filed and the proceed with bugs that have been filed from users outside the development team. ", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Config: update list of `repos` and the `milestone`", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"October 2020\"", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Bugs You Filed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate author:@me", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "### Bugs From Outside", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "### All", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate", + "editable": false + } +] \ No newline at end of file diff --git a/.vscode/searches/TrustedTypes.code-search b/.vscode/searches/TrustedTypes.code-search new file mode 100644 index 00000000000..85707534c1e --- /dev/null +++ b/.vscode/searches/TrustedTypes.code-search @@ -0,0 +1,101 @@ +# Query: .innerHTML = +# Flags: CaseSensitive WordMatch +# Including: src/vs/**/*.{t,j}s +# Excluding: *.test.ts, **/test/** +# ContextLines: 3 + +12 results - 9 files + +src/vs/base/browser/dom.ts: + 1359 ); + 1360 + 1361 const html = _ttpSafeInnerHtml?.createHTML(value, options) ?? insane(value, options); + 1362: node.innerHTML = html as unknown as string; + 1363 } + +src/vs/base/browser/markdownRenderer.ts: + 272 }; + 273 + 274 if (_ttpInsane) { + 275: element.innerHTML = _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string; + 276 } else { + 277: element.innerHTML = insane(renderedMarkdown, insaneOptions); + 278 } + 279 + 280 // signal that async code blocks can be now be inserted + +src/vs/editor/browser/core/markdownRenderer.ts: + 88 + 89 const element = document.createElement('span'); + 90 + 91: element.innerHTML = MarkdownRenderer._ttpTokenizer + 92 ? MarkdownRenderer._ttpTokenizer.createHTML(value, tokenization) as unknown as string + 93 : tokenizeToString(value, tokenization); + 94 + +src/vs/editor/browser/view/domLineBreaksComputer.ts: + 107 allCharOffsets[i] = tmp[0]; + 108 allVisibleColumns[i] = tmp[1]; + 109 } + 110: containerDomNode.innerHTML = sb.build(); + 111 + 112 containerDomNode.style.position = 'absolute'; + 113 containerDomNode.style.top = '10000'; + +src/vs/editor/browser/view/viewLayer.ts: + 512 } + 513 const lastChild = this.domNode.lastChild; + 514 if (domNodeIsEmpty || !lastChild) { + 515: this.domNode.innerHTML = newLinesHTML; + 516 } else { + 517 lastChild.insertAdjacentHTML('afterend', newLinesHTML); + 518 } + + 533 if (ViewLayerRenderer._ttPolicy) { + 534 invalidLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(invalidLinesHTML) as unknown as string; + 535 } + 536: hugeDomNode.innerHTML = invalidLinesHTML; + 537 + 538 for (let i = 0; i < ctx.linesLength; i++) { + 539 const line = ctx.lines[i]; + +src/vs/editor/browser/widget/diffEditorWidget.ts: + 2157 + 2158 let domNode = document.createElement('div'); + 2159 domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; + 2160: domNode.innerHTML = sb.build(); + 2161 Configuration.applyFontInfoSlow(domNode, fontInfo); + 2162 + 2163 let marginDomNode = document.createElement('div'); + 2164 marginDomNode.className = 'inline-deleted-margin-view-zone'; + 2165: marginDomNode.innerHTML = marginHTML.join(''); + 2166 Configuration.applyFontInfoSlow(marginDomNode, fontInfo); + 2167 + 2168 return { + +src/vs/editor/standalone/browser/colorizer.ts: + 40 let text = domNode.firstChild ? domNode.firstChild.nodeValue : ''; + 41 domNode.className += ' ' + theme; + 42 let render = (str: string) => { + 43: domNode.innerHTML = str; + 44 }; + 45 return this.colorize(modeService, text || '', mimeType, options).then(render, (err) => console.error(err)); + 46 } + +src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts: + 580 const element = DOM.$('div', { style }); + 581 + 582 const linesHtml = this.getRichTextLinesAsHtml(model, modelRange, colorMap); + 583: element.innerHTML = linesHtml as unknown as string; + 584 return element; + 585 } + 586 + +src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts: + 375 addMouseoverListeners(outputNode, outputId); + 376 const content = data.content; + 377 if (content.type === RenderOutputType.Html) { + 378: outputNode.innerHTML = content.htmlContent; + 379 cellOutputContainer.appendChild(outputNode); + 380 domEval(outputNode); + 381 } else if (preloadErrs.some(e => !!e)) { diff --git a/.vscode/searches/ts36031.code-search b/.vscode/searches/ts36031.code-search index fb6cf8a431d..51071b8840a 100644 --- a/.vscode/searches/ts36031.code-search +++ b/.vscode/searches/ts36031.code-search @@ -1,10 +1,53 @@ -# Query: \\w+\\?\\..+![(.[] +# Query: \\w+\\?\\.\\w+![(.[] # Flags: RegExp # ContextLines: 2 +8 results - 4 files + src/vs/base/browser/ui/tree/asyncDataTree.ts: - 270 } : undefined, - 271 isChecked: options.ariaProvider!.isChecked ? (e) => { - 272: return options.ariaProvider?.isChecked!(e.element as T); - 273 } : undefined - 274 }, + 241 } : () => 'treeitem', + 242 isChecked: options.accessibilityProvider!.isChecked ? (e) => { + 243: return !!(options.accessibilityProvider?.isChecked!(e.element as T)); + 244 } : undefined, + 245 getAriaLabel(e) { + +src/vs/platform/list/browser/listService.ts: + 463 + 464 if (typeof options?.openOnSingleClick !== 'boolean' && options?.configurationService) { + 465: this.openOnSingleClick = options?.configurationService!.getValue(openModeSettingKey) !== 'doubleClick'; + 466 this._register(options?.configurationService.onDidChangeConfiguration(() => { + 467: this.openOnSingleClick = options?.configurationService!.getValue(openModeSettingKey) !== 'doubleClick'; + 468 })); + 469 } else { + +src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts: + 1526 + 1527 await this._ensureActiveKernel(); + 1528: await this._activeKernel?.cancelNotebookCell!(this._notebookViewModel!.uri, undefined); + 1529 } + 1530 + + 1535 + 1536 await this._ensureActiveKernel(); + 1537: await this._activeKernel?.executeNotebookCell!(this._notebookViewModel!.uri, undefined); + 1538 } + 1539 + + 1553 + 1554 await this._ensureActiveKernel(); + 1555: await this._activeKernel?.cancelNotebookCell!(this._notebookViewModel!.uri, cell.handle); + 1556 } + 1557 + + 1567 + 1568 await this._ensureActiveKernel(); + 1569: await this._activeKernel?.executeNotebookCell!(this._notebookViewModel!.uri, cell.handle); + 1570 } + 1571 + +src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts: + 89 .then(() => this._resourceRequestManager.ensureReady()) + 90 .then(() => { + 91: this.element?.contentWindow!.postMessage({ channel, args: data }, '*'); + 92 }); + 93 } diff --git a/.vscode/settings.json b/.vscode/settings.json index e0fd4e23428..4b2a9059553 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,8 @@ "**/.DS_Store": true, "build/**/*.js": { "when": "$(basename).ts" - } + }, + "src/vs/server": false }, "files.associations": { "cglicenses.json": "jsonc" @@ -22,7 +23,11 @@ "i18n/**": true, "extensions/**/out/**": true, "test/smoke/out/**": true, - "src/vs/base/test/node/uri.test.data.txt": true + "test/automation/out/**": true, + "test/integration/browser/out/**": true, + "src/vs/base/test/node/uri.test.data.txt": true, + "src/vs/workbench/test/browser/api/extHostDocumentData.test.perf-data.ts": true, + "src/vs/server": false }, "lcov.path": [ "./.build/coverage/lcov.info", @@ -38,9 +43,7 @@ } ], "eslint.options": { - "rulePaths": [ - "./build/lib/eslint" - ] + "rulePaths": ["./build/lib/eslint"] }, "typescript.tsdk": "node_modules/typescript/lib", "npm.exclude": "**/extensions/**", @@ -50,15 +53,11 @@ "typescript.preferences.quoteStyle": "single", "json.schemas": [ { - "fileMatch": [ - "cgmanifest.json" - ], + "fileMatch": ["cgmanifest.json"], "url": "./.vscode/cgmanifest.schema.json" }, { - "fileMatch": [ - "cglicenses.json" - ], + "fileMatch": ["cglicenses.json"], "url": "./.vscode/cglicenses.schema.json" } ], @@ -67,5 +66,15 @@ "msjsdiag.debugger-for-chrome": "workspace" }, "gulp.autoDetect": "off", - "files.insertFinalNewline": true + "files.insertFinalNewline": true, + "[plaintext]": { + "files.insertFinalNewline": false, + }, + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "[javascript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "typescript.tsc.autoDetect": "off" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index aa94be61126..943ae5bd536 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,12 +3,8 @@ "tasks": [ { "type": "npm", - "script": "watch", - "label": "Build VS Code", - "group": { - "kind": "build", - "isDefault": true - }, + "script": "watch-clientd", + "label": "Build VS Code Core", "isBackground": true, "presentation": { "reveal": "never" @@ -33,18 +29,106 @@ }, { "type": "npm", - "script": "strict-function-types-watch", - "label": "TS - Strict Function Types", + "script": "watch-extensionsd", + "label": "Build VS Code Extensions", "isBackground": true, "presentation": { "reveal": "never" }, "problemMatcher": { - "base": "$tsc-watch", - "owner": "typescript-function-types", - "applyTo": "allDocuments" + "owner": "typescript", + "applyTo": "closedDocuments", + "fileLocation": [ + "absolute" + ], + "pattern": { + "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "beginsPattern": "Starting compilation", + "endsPattern": "Finished compilation" + } } }, + { + "label": "Build VS Code", + "dependsOn": [ + "Build VS Code Core", + "Build VS Code Extensions" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "kill-watch-clientd", + "label": "Kill Build VS Code Core", + "group": "build", + "presentation": { + "reveal": "never" + }, + "problemMatcher": "$tsc" + }, + { + "type": "npm", + "script": "kill-watch-extensionsd", + "label": "Kill Build VS Code Extensions", + "group": "build", + "presentation": { + "reveal": "never" + }, + "problemMatcher": "$tsc" + }, + { + "label": "Kill Build VS Code", + "dependsOn": [ + "Kill Build VS Code Core", + "Kill Build VS Code Extensions" + ], + "group": "build" + }, + { + "type": "npm", + "script": "watch-webd", + "label": "Build Web Extensions", + "group": "build", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "problemMatcher": { + "owner": "typescript", + "applyTo": "closedDocuments", + "fileLocation": [ + "absolute" + ], + "pattern": { + "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "beginsPattern": "Starting compilation", + "endsPattern": "Finished compilation" + } + } + }, + { + "type": "npm", + "script": "kill-watch-webd", + "label": "Kill Build Web Extensions", + "group": "build", + "presentation": { + "reveal": "never" + }, + "problemMatcher": "$tsc" + }, { "label": "Run tests", "type": "shell", @@ -79,7 +163,7 @@ }, { "type": "shell", - "command": "yarn web -- --no-launch", + "command": "yarn web --no-launch", "label": "Run web", "isBackground": true, "problemMatcher": { @@ -102,6 +186,28 @@ "source": "eslint", "base": "$eslint-stylish" } + }, + { + "type": "shell", + "command": "node build/lib/preLaunch.js", + "label": "Ensure Prelaunch Dependencies", + "presentation": { + "reveal": "silent" + } + }, + { + "type": "npm", + "script": "tsec-compile-check", + "problemMatcher": [ + { + "base": "$tsc", + "applyTo": "allDocuments", + "owner": "tsec" + }, + ], + "group": "build", + "label": "npm: tsec-compile-check", + "detail": "node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit" } ] } diff --git a/.yarnrc b/.yarnrc index 2c769cfba18..d97527dab46 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ -disturl "https://atom.io/download/electron" -target "7.1.7" +disturl "https://electronjs.org/headers" +target "9.3.3" runtime "electron" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 34703fae665..5d547535187 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ The active community will be eager to assist you. Your well-worded question will Your comments and feedback are welcome, and the development team is available via a handful of different channels. -See the [Feedback Channels](https://github.com/Microsoft/vscode/wiki/Feedback-Channels) wiki page for details on how to share your thoughts. +See the [Feedback Channels](https://github.com/microsoft/vscode/wiki/Feedback-Channels) wiki page for details on how to share your thoughts. ## Reporting Issues @@ -22,15 +22,15 @@ Have you identified a reproducible problem in VS Code? Have a feature request? W ### Identify Where to Report -The VS Code project is distributed across multiple repositories. Try to file the issue against the correct repository. Check the list of [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) if you aren't sure which repo is correct. +The VS Code project is distributed across multiple repositories. Try to file the issue against the correct repository. Check the list of [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) if you aren't sure which repo is correct. Can you recreate the issue even after [disabling all extensions](https://code.visualstudio.com/docs/editor/extension-gallery#_disable-an-extension)? If you find the issue is caused by an extension you have installed, please file an issue on the extension's repo directly. ### Look For an Existing Issue -Before you create a new issue, please do a search in [open issues](https://github.com/Microsoft/vscode/issues) to see if the issue or feature request has already been filed. +Before you create a new issue, please do a search in [open issues](https://github.com/microsoft/vscode/issues) to see if the issue or feature request has already been filed. -Be sure to scan through the [most popular](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) feature requests. +Be sure to scan through the [most popular](https://github.com/microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) feature requests. If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment: @@ -51,9 +51,9 @@ The built-in tool for reporting an issue, which you can access by using `Report Please include the following with each issue: -* Version of VS Code +* Version of VS Code -* Your operating system +* Your operating system * List of extensions that you have installed @@ -83,21 +83,22 @@ Don't feel bad if the developers can't reproduce the issue right away. They will ### Follow Your Issue -Once submitted, your report will go into the [issue tracking](https://github.com/Microsoft/vscode/wiki/Issue-Tracking) workflow. Be sure to understand what will happen next, so you know what to expect, and how to continue to assist throughout the process. +Once submitted, your report will go into the [issue tracking](https://github.com/microsoft/vscode/wiki/Issue-Tracking) workflow. Be sure to understand what will happen next, so you know what to expect, and how to continue to assist throughout the process. ## Automated Issue Management -We use a bot to help us manage issues. This bot currently: +We use GitHub Actions to help us manage issues. These Actions and their descriptions can be [viewed here](https://github.com/microsoft/vscode-github-triage-actions). Some examples of what these Actions do are: * Automatically closes any issue marked `needs-more-info` if there has been no response in the past 7 days. -* Automatically locks issues 45 days after they are closed. +* Automatically lock issues 45 days after they are closed. +* Automatically implement the VS Code [feature request pipeline](https://github.com/microsoft/vscode/wiki/Issues-Triaging#managing-feature-requests). If you believe the bot got something wrong, please open a new issue and let us know. ## Contributing Fixes If you are interested in writing code to fix issues, -please see [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute) in the wiki. +please see [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) in the wiki. # Thank You! diff --git a/LICENSE.txt b/LICENSE.txt index 69be21bd8ed..0ac28ee234d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,8 +2,6 @@ MIT License Copyright (c) 2015 - present Microsoft Corporation -All rights reserved. - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/README.md b/README.md index 0de5f2f71da..ce1a7132ef8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Visual Studio Code - Open Source ("Code - OSS") - - -[![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://dev.azure.com/vscode/VSCode/_build/latest?definitionId=12) -[![Feature Requests](https://img.shields.io/github/issues/Microsoft/vscode/feature-request.svg)](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) -[![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) +[![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://aka.ms/vscode-builds) +[![Feature Requests](https://img.shields.io/github/issues/microsoft/vscode/feature-request.svg)](https://github.com/microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) +[![Bugs](https://img.shields.io/github/issues/microsoft/vscode/bug.svg)](https://github.com/microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://gitter.im/Microsoft/vscode) ## The Repository @@ -31,12 +29,12 @@ There are many ways in which you can participate in the project, for example: * Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content If you are interested in fixing issues and contributing directly to the code base, -please see the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute), which covers the following: +please see the document [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute), which covers the following: -* [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run) -* [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) -* [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) -* [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) +* [How to build and run from source](https://github.com/microsoft/vscode/wiki/How-to-Contribute#build-and-run) +* [The development workflow, including debugging and running tests](https://github.com/microsoft/vscode/wiki/How-to-Contribute#debugging) +* [Coding guidelines](https://github.com/microsoft/vscode/wiki/Coding-Guidelines) +* [Submitting pull requests](https://github.com/microsoft/vscode/wiki/How-to-Contribute#pull-requests) * [Finding an issue to work on](https://github.com/microsoft/vscode/wiki/How-to-Contribute#where-to-contribute) * [Contributing to translations](https://aka.ms/vscodeloc) @@ -44,18 +42,29 @@ please see the document [How to Contribute](https://github.com/Microsoft/vscode/ * Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) * [Request a new feature](CONTRIBUTING.md) -* Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) -* [File an issue](https://github.com/Microsoft/vscode/issues) +* Upvote [popular feature requests](https://github.com/microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) +* [File an issue](https://github.com/microsoft/vscode/issues) * Follow [@code](https://twitter.com/code) and let us know what you think! +See our [wiki](https://github.com/microsoft/vscode/wiki/Feedback-Channels) for a description of each of these channels and information on some other available community-driven channels. + ## Related Projects -Many of the core components and extensions to VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/Microsoft/vscode/wiki). +Many of the core components and extensions to VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki). ## Bundled Extensions VS Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many languages. Extensions that provide rich language support (code completion, Go to Definition) for a language have the suffix `language-features`. For example, the `json` extension provides coloring for `JSON` and the `json-language-features` provides rich language support for `JSON`. +## Development Container + +This repository includes a Visual Studio Code Remote - Containers / Codespaces development container. + +- For [Remote - Containers](https://aka.ms/vscode-remote/download/containers), use the **Remote-Containers: Open Repository in Container...** command which creates a Docker volume for better disk I/O on macOS and Windows. +- For Codespaces, install the [Visual Studio Codespaces](https://aka.ms/vscs-ext-vscode) extension in VS Code, and use the **Codespaces: Create New Codespace** command. + +Docker / the Codespace should have at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. See the [development container README](.devcontainer/README.md) for more information. + ## Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 7a3dd5cf7b3..074844d813e 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -7,66 +7,65 @@ This project incorporates components from the projects listed below. The origina 1. atom/language-clojure version 0.22.7 (https://github.com/atom/language-clojure) 2. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script) -3. atom/language-java version 0.31.3 (https://github.com/atom/language-java) +3. atom/language-java version 0.32.0 (https://github.com/atom/language-java) 4. atom/language-sass version 0.62.1 (https://github.com/atom/language-sass) 5. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript) 6. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) -7. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) -8. daaain/Handlebars version 1.8.0 (https://github.com/daaain/Handlebars) -9. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) -10. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) -11. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) -12. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) -13. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) -14. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) -15. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) -16. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) -17. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) -18. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R) -19. insane version 2.6.2 (https://github.com/bevacqua/insane) -20. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) -21. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) -22. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) -23. jeff-hykin/cpp-textmate-grammar version 1.14.13 (https://github.com/jeff-hykin/cpp-textmate-grammar) -24. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) -25. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) -26. language-docker (https://github.com/moby/moby) -27. language-go version 0.44.3 (https://github.com/atom/language-go) +7. better-go-syntax version 1.0.0 (https://github.com/jeff-hykin/better-go-syntax/ ) +8. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) +9. daaain/Handlebars version 1.8.0 (https://github.com/daaain/Handlebars) +10. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) +11. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) +12. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) +13. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) +14. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) +15. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) +16. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) +17. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) +18. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) +19. Ikuyadeu/vscode-R version 1.3.0 (https://github.com/Ikuyadeu/vscode-R) +20. insane version 2.6.2 (https://github.com/bevacqua/insane) +21. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) +22. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) +23. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) +24. jeff-hykin/cpp-textmate-grammar version 1.14.15 (https://github.com/jeff-hykin/cpp-textmate-grammar) +25. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) +26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) +27. language-docker (https://github.com/moby/moby) 28. language-less version 0.34.2 (https://github.com/atom/language-less) -29. language-php version 0.44.3 (https://github.com/atom/language-php) +29. language-php version 0.44.5 (https://github.com/atom/language-php) 30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) 31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) 32. marked version 0.6.2 (https://github.com/markedjs/marked) 33. mdn-data version 1.1.12 (https://github.com/mdn/data) -34. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage) -35. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) -36. Microsoft/vscode-mssql version 1.6.0 (https://github.com/Microsoft/vscode-mssql) +34. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) +35. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +36. microsoft/vscode-mssql version 1.9.0 (https://github.com/microsoft/vscode-mssql) 37. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile) 38. octref/language-css version 0.42.11 (https://github.com/octref/language-css) 39. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) -40. promise-polyfill version 8.0.0 (https://github.com/taylorhakes/promise-polyfill) -41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -42. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -43. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -44. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -45. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -46. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -47. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -48. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -49. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -50. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -51. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) -52. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -53. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -54. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -55. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -56. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) -57. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage) -58. Unicode version 12.0.0 (http://www.unicode.org/) -59. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) -60. vscode-logfile-highlighter version 2.5.0 (https://github.com/emilast/vscode-logfile-highlighter) -61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -62. Web Background Synchronization (https://github.com/WICG/BackgroundSync) +40. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +41. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) +42. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) +43. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) +44. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) +45. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) +46. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) +47. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) +48. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) +49. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) +50. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) +51. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) +52. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) +53. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) +54. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +55. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) +56. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) +57. Unicode version 12.0.0 (https://home.unicode.org/) +58. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) +59. vscode-logfile-highlighter version 2.8.0 (https://github.com/emilast/vscode-logfile-highlighter) +60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +61. Web Background Synchronization (https://github.com/WICG/background-sync) %% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE @@ -340,6 +339,32 @@ suitability for any purpose. ========================================= END OF atom/language-xml NOTICES AND INFORMATION +%% better-go-syntax NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Jeff Hykin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF better-go-syntax NOTICES AND INFORMATION + %% Colorsublime-Themes NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2015 Colorsublime.com @@ -987,83 +1012,6 @@ Apache License ========================================= END OF language-docker NOTICES AND INFORMATION -%% language-go NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2014 GitHub Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -This package was derived from a TextMate bundle located at -https://github.com/rsms/Go.tmbundle and distributed under the following -license, located in `LICENSE`: - -Copyright (c) 2009 Rasmus Andersson - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - -The Go Template grammar was derived from GoSublime located at -https://github.com/DisposaBoy/GoSublime and distributed under the following -license, located in `LICENSE.md`: - -Copyright (c) 2012 The GoSublime Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -========================================= -END OF language-go NOTICES AND INFORMATION - %% language-less NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) @@ -1633,7 +1581,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice ========================================= END OF mdn-data NOTICES AND INFORMATION -%% Microsoft/TypeScript-TmLanguage NOTICES AND INFORMATION BEGIN HERE +%% microsoft/TypeScript-TmLanguage NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) Microsoft Corporation All rights reserved. @@ -1658,9 +1606,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF Microsoft/TypeScript-TmLanguage NOTICES AND INFORMATION +END OF microsoft/TypeScript-TmLanguage NOTICES AND INFORMATION -%% Microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION BEGIN HERE +%% microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION BEGIN HERE ========================================= vscode-JSON.tmLanguage @@ -1682,9 +1630,9 @@ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF Microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION +END OF microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION -%% Microsoft/vscode-mssql NOTICES AND INFORMATION BEGIN HERE +%% microsoft/vscode-mssql NOTICES AND INFORMATION BEGIN HERE ========================================= ------------------------------------------ START OF LICENSE ----------------------------------------- vscode-mssql @@ -1692,12 +1640,12 @@ Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -Copyright (c) 2016 Sanjay Nagamangalam +Copyright (c) 2016 Microsoft The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------ END OF LICENSE ------------------------------------------ +----------------------------------------------- END OF LICENSE ----------------------------------------- ========================================= -END OF Microsoft/vscode-mssql NOTICES AND INFORMATION +END OF microsoft/vscode-mssql NOTICES AND INFORMATION %% mmims/language-batchfile NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -1790,33 +1738,6 @@ SOFTWARE. ========================================= END OF PowerShell/EditorSyntax NOTICES AND INFORMATION -%% promise-polyfill NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2014 Taylor Hakes -Copyright (c) 2014 Forbes Lindesay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -========================================= -END OF promise-polyfill NOTICES AND INFORMATION - %% seti-ui NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2014 Jesse Weed diff --git a/build/.cachesalt b/build/.cachesalt index 339d2d379fa..3f2ee542ad5 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2019-08-30T20:24:23.714Z +2020-10-05T20:24:23.714Z diff --git a/build/.gitattributes b/build/.gitattributes new file mode 100644 index 00000000000..d2690a8f26f --- /dev/null +++ b/build/.gitattributes @@ -0,0 +1,3 @@ +* text eol=lf +*.exe binary +*.dll binary diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 00000000000..01e8737ef58 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,3 @@ +azure-pipelines/**/*.js +darwin/**/*.js +lib/**/*.js diff --git a/build/.moduleignore b/build/.moduleignore new file mode 100644 index 00000000000..489d4709f0a --- /dev/null +++ b/build/.moduleignore @@ -0,0 +1,38 @@ + +# additional cleanup rules for node modules, .gitignore style + +**/docs/** +**/example/** +**/examples/** +**/test/** +**/tests/** + +**/History.md +**/CHANGELOG.md +**/README.md +**/readme.md +**/readme.markdown + +**/*.ts +!typescript/**/*.d.ts + +jschardet/dist/** + +es6-promise/lib/** + +vscode-textmate/webpack.config.js + +zone.js/dist/** +!zone.js/dist/zone-node.js + +# https://github.com/xtermjs/xterm.js/issues/3137 +xterm/src/** +xterm/tsconfig.all.json + +# https://github.com/xtermjs/xterm.js/issues/3138 +xterm-addon-*/src/** +xterm-addon-*/fixtures/** +xterm-addon-*/out/** +xterm-addon-*/out-test/** + + diff --git a/build/.nativeignore b/build/.nativeignore index a4823bf719c..a27530745f3 100644 --- a/build/.nativeignore +++ b/build/.nativeignore @@ -19,13 +19,6 @@ vscode-sqlite3/build/** vscode-sqlite3/src/** !vscode-sqlite3/build/Release/*.node -oniguruma/binding.gyp -oniguruma/build/** -oniguruma/src/** -oniguruma/deps/** -!oniguruma/build/Release/*.node -!oniguruma/src/*.js - windows-mutex/binding.gyp windows-mutex/build/** windows-mutex/src/** @@ -53,6 +46,7 @@ spdlog/build/** spdlog/deps/** spdlog/src/** spdlog/test/** +spdlog/*.yml !spdlog/build/Release/*.node jschardet/dist/** @@ -79,17 +73,18 @@ node-pty/build/** node-pty/src/** node-pty/tools/** node-pty/deps/** +node-pty/scripts/** !node-pty/build/Release/*.exe !node-pty/build/Release/*.dll !node-pty/build/Release/*.node -nsfw/binding.gyp -nsfw/build/** -nsfw/src/** -nsfw/openpa/** -nsfw/includes/** -!nsfw/build/Release/*.node -!nsfw/**/*.a +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a vsda/build/** vsda/ci/** diff --git a/build/.webignore b/build/.webignore new file mode 100644 index 00000000000..553d445f3f6 --- /dev/null +++ b/build/.webignore @@ -0,0 +1,31 @@ +# cleanup rules for web node modules, .gitignore style + +**/*.txt +**/*.json +**/*.md +**/*.d.ts +**/*.js.map +**/LICENSE +**/CONTRIBUTORS + +**/docs/** +**/example/** +**/examples/** + +jschardet/index.js +jschardet/src/** +jschardet/dist/jschardet.js + +vscode-textmate/webpack.config.js + +xterm/src/** + +xterm-addon-search/src/** +xterm-addon-search/out/** +xterm-addon-search/fixtures/** + +xterm-addon-unicode11/src/** +xterm-addon-unicode11/out/** + +xterm-addon-webgl/src/** +xterm-addon-webgl/out/** diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index 619c92a0412..d7e62629cb8 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -44,15 +44,16 @@ async function doesAssetExist(blobService: azure.BlobService, quality: string, b return existsResult.exists; } -async function uploadBlob(blobService: azure.BlobService, quality: string, blobName: string, file: string): Promise { +async function uploadBlob(blobService: azure.BlobService, quality: string, blobName: string, filePath: string, fileName: string): Promise { const blobOptions: azure.BlobService.CreateBlockBlobRequestOptions = { contentSettings: { - contentType: mime.lookup(file), + contentType: mime.lookup(filePath), + contentDisposition: `attachment; filename="${fileName}"`, cacheControl: 'max-age=31536000, public' } }; - await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, file, blobOptions, err => err ? e(err) : c())); + await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, filePath, blobOptions, err => err ? e(err) : c())); } function getEnv(name: string): string { @@ -66,24 +67,24 @@ function getEnv(name: string): string { } async function main(): Promise { - const [, , platform, type, name, file] = process.argv; + const [, , platform, type, fileName, filePath] = process.argv; const quality = getEnv('VSCODE_QUALITY'); const commit = getEnv('BUILD_SOURCEVERSION'); console.log('Creating asset...'); - const stat = await new Promise((c, e) => fs.stat(file, (err, stat) => err ? e(err) : c(stat))); + const stat = await new Promise((c, e) => fs.stat(filePath, (err, stat) => err ? e(err) : c(stat))); const size = stat.size; console.log('Size:', size); - const stream = fs.createReadStream(file); + const stream = fs.createReadStream(filePath); const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); console.log('SHA1:', sha1hash); console.log('SHA256:', sha256hash); - const blobName = commit + '/' + name; + const blobName = commit + '/' + fileName; const storageAccount = process.env['AZURE_STORAGE_ACCOUNT_2']!; const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']!) @@ -98,7 +99,7 @@ async function main(): Promise { console.log('Uploading blobs to Azure storage...'); - await uploadBlob(blobService, quality, blobName, file); + await uploadBlob(blobService, quality, blobName, filePath, fileName); console.log('Blobs successfully uploaded.'); diff --git a/build/azure-pipelines/common/extract-telemetry.sh b/build/azure-pipelines/common/extract-telemetry.sh index 84bbd9c537c..03d80c1bbbd 100755 --- a/build/azure-pipelines/common/extract-telemetry.sh +++ b/build/azure-pipelines/common/extract-telemetry.sh @@ -4,16 +4,16 @@ set -e cd $BUILD_STAGINGDIRECTORY mkdir extraction cd extraction -git clone --depth 1 https://github.com/Microsoft/vscode-extension-telemetry.git -git clone --depth 1 https://github.com/Microsoft/vscode-chrome-debug-core.git -git clone --depth 1 https://github.com/Microsoft/vscode-node-debug2.git -git clone --depth 1 https://github.com/Microsoft/vscode-node-debug.git -git clone --depth 1 https://github.com/Microsoft/vscode-html-languageservice.git -git clone --depth 1 https://github.com/Microsoft/vscode-json-languageservice.git -$BUILD_SOURCESDIRECTORY/build/node_modules/.bin/vscode-telemetry-extractor --sourceDir $BUILD_SOURCESDIRECTORY --excludedDir $BUILD_SOURCESDIRECTORY/extensions --outputDir . --applyEndpoints -$BUILD_SOURCESDIRECTORY/build/node_modules/.bin/vscode-telemetry-extractor --config $BUILD_SOURCESDIRECTORY/build/azure-pipelines/common/telemetry-config.json -o . +git clone --depth 1 https://github.com/microsoft/vscode-extension-telemetry.git +git clone --depth 1 https://github.com/microsoft/vscode-chrome-debug-core.git +git clone --depth 1 https://github.com/microsoft/vscode-node-debug2.git +git clone --depth 1 https://github.com/microsoft/vscode-node-debug.git +git clone --depth 1 https://github.com/microsoft/vscode-html-languageservice.git +git clone --depth 1 https://github.com/microsoft/vscode-json-languageservice.git +node $BUILD_SOURCESDIRECTORY/build/node_modules/.bin/vscode-telemetry-extractor --sourceDir $BUILD_SOURCESDIRECTORY --excludedDir $BUILD_SOURCESDIRECTORY/extensions --outputDir . --applyEndpoints +node $BUILD_SOURCESDIRECTORY/build/node_modules/.bin/vscode-telemetry-extractor --config $BUILD_SOURCESDIRECTORY/build/azure-pipelines/common/telemetry-config.json -o . mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json mv config-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json cd .. -rm -rf extraction \ No newline at end of file +rm -rf extraction diff --git a/build/azure-pipelines/common/publish-webview.ts b/build/azure-pipelines/common/publish-webview.ts index 143b61bb61a..b1947ebc024 100644 --- a/build/azure-pipelines/common/publish-webview.ts +++ b/build/azure-pipelines/common/publish-webview.ts @@ -17,7 +17,7 @@ const fileNames = [ ]; async function assertContainer(blobService: azure.BlobService, container: string): Promise { - await new Promise((c, e) => blobService.createContainerIfNotExists(container, { publicAccessLevel: 'blob' }, err => err ? e(err) : c())); + await new Promise((c, e) => blobService.createContainerIfNotExists(container, { publicAccessLevel: 'blob' }, err => err ? e(err) : c())); } async function doesBlobExist(blobService: azure.BlobService, container: string, blobName: string): Promise { @@ -33,7 +33,7 @@ async function uploadBlob(blobService: azure.BlobService, container: string, blo } }; - await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(container, blobName, file, blobOptions, err => err ? e(err) : c())); + await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(container, blobName, file, blobOptions, err => err ? e(err) : c())); } async function publish(commit: string, files: readonly string[]): Promise { diff --git a/build/azure-pipelines/common/symbols.ts b/build/azure-pipelines/common/symbols.ts deleted file mode 100644 index 153be4f25b1..00000000000 --- a/build/azure-pipelines/common/symbols.ts +++ /dev/null @@ -1,228 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as request from 'request'; -import { createReadStream, createWriteStream, unlink, mkdir } from 'fs'; -import * as github from 'github-releases'; -import { join } from 'path'; -import { tmpdir } from 'os'; -import { promisify } from 'util'; - -const BASE_URL = 'https://rink.hockeyapp.net/api/2/'; -const HOCKEY_APP_TOKEN_HEADER = 'X-HockeyAppToken'; - -export interface IVersions { - app_versions: IVersion[]; -} - -export interface IVersion { - id: number; - version: string; -} - -export interface IApplicationAccessor { - accessToken: string; - appId: string; -} - -export interface IVersionAccessor extends IApplicationAccessor { - id: string; -} - -enum Platform { - WIN_32 = 'win32-ia32', - WIN_64 = 'win32-x64', - LINUX_64 = 'linux-x64', - MAC_OS = 'darwin-x64' -} - -function symbolsZipName(platform: Platform, electronVersion: string, insiders: boolean): string { - return `${insiders ? 'insiders' : 'stable'}-symbols-v${electronVersion}-${platform}.zip`; -} - -const SEED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -async function tmpFile(name: string): Promise { - let res = ''; - for (let i = 0; i < 8; i++) { - res += SEED.charAt(Math.floor(Math.random() * SEED.length)); - } - - const tmpParent = join(tmpdir(), res); - - await promisify(mkdir)(tmpParent); - - return join(tmpParent, name); -} - -function getVersions(accessor: IApplicationAccessor): Promise { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions`, - method: 'GET', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - } - }); -} - -function createVersion(accessor: IApplicationAccessor, version: string): Promise { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/new`, - method: 'POST', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - bundle_version: version - } - }); -} - -function updateVersion(accessor: IVersionAccessor, symbolsPath: string) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/${accessor.id}`, - method: 'PUT', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - dsym: createReadStream(symbolsPath) - } - }); -} - -function asyncRequest(options: request.UrlOptions & request.CoreOptions): Promise { - return new Promise((resolve, reject) => { - request(options, (error, _response, body) => { - if (error) { - reject(error); - } else { - resolve(JSON.parse(body)); - } - }); - }); -} - -function downloadAsset(repository: any, assetName: string, targetPath: string, electronVersion: string) { - return new Promise((resolve, reject) => { - repository.getReleases({ tag_name: `v${electronVersion}` }, (err: any, releases: any) => { - if (err) { - reject(err); - } else { - const asset = releases[0].assets.filter((asset: any) => asset.name === assetName)[0]; - if (!asset) { - reject(new Error(`Asset with name ${assetName} not found`)); - } else { - repository.downloadAsset(asset, (err: any, reader: any) => { - if (err) { - reject(err); - } else { - const writer = createWriteStream(targetPath); - writer.on('error', reject); - writer.on('close', resolve); - reader.on('error', reject); - - reader.pipe(writer); - } - }); - } - } - }); - }); -} - -interface IOptions { - repository: string; - platform: Platform; - versions: { code: string; insiders: boolean; electron: string; }; - access: { hockeyAppToken: string; hockeyAppId: string; githubToken: string }; -} - -async function ensureVersionAndSymbols(options: IOptions) { - - // Check version does not exist - console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); - const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); - if (!Array.isArray(versions.app_versions)) { - throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); - } - - if (versions.app_versions.some(v => v.version === options.versions.code)) { - console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); - return; - } - - // Download symbols for platform and electron version - const symbolsName = symbolsZipName(options.platform, options.versions.electron, options.versions.insiders); - const symbolsPath = await tmpFile('symbols.zip'); - console.log(`HockeyApp: downloading symbols ${symbolsName} for electron ${options.versions.electron} (${options.platform}) into ${symbolsPath}`); - await downloadAsset(new (github as any)({ repo: options.repository, token: options.access.githubToken }), symbolsName, symbolsPath, options.versions.electron); - - // Create version - console.log(`HockeyApp: creating new version ${options.versions.code} (${options.platform})`); - const version = await createVersion({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, options.versions.code); - - // Upload symbols - console.log(`HockeyApp: uploading symbols for version ${options.versions.code} (${options.platform})`); - await updateVersion({ id: String(version.id), accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, symbolsPath); - - // Cleanup - await promisify(unlink)(symbolsPath); -} - -// Environment -const pakage = require('../../../package.json'); -const product = require('../../../product.json'); -const repository = product.electronRepository; -const electronVersion = require('../../lib/electron').getElectronVersion(); -const insiders = product.quality !== 'stable'; -let codeVersion = pakage.version; -if (insiders) { - codeVersion = `${codeVersion}-insider`; -} -const githubToken = process.argv[2]; -const hockeyAppToken = process.argv[3]; -const is64 = process.argv[4] === 'x64'; -const hockeyAppId = process.argv[5]; - -if (process.argv.length !== 6) { - throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); -} - -let platform: Platform; -if (process.platform === 'darwin') { - platform = Platform.MAC_OS; -} else if (process.platform === 'win32') { - platform = is64 ? Platform.WIN_64 : Platform.WIN_32; -} else { - platform = Platform.LINUX_64; -} - -// Create version and upload symbols in HockeyApp -if (repository && codeVersion && electronVersion && (product.quality === 'stable' || product.quality === 'insider')) { - ensureVersionAndSymbols({ - repository, - platform, - versions: { - code: codeVersion, - insiders, - electron: electronVersion - }, - access: { - githubToken, - hockeyAppToken, - hockeyAppId - } - }).then(() => { - console.log('HockeyApp: done'); - }).catch(error => { - console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); - - return process.exit(1); - }); -} else { - console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); -} \ No newline at end of file diff --git a/build/azure-pipelines/darwin/app-entitlements.plist b/build/azure-pipelines/darwin/app-entitlements.plist new file mode 100644 index 00000000000..90031d937be --- /dev/null +++ b/build/azure-pipelines/darwin/app-entitlements.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + + diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index d0aafb2a3e6..954e5bca636 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -1,49 +1,76 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.x" + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'vscode-build-cache' + - script: | CHILD_CONCURRENCY=1 yarn --frozen-lockfile displayName: Install Dependencies condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'vscode-build-cache' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | yarn electron x64 displayName: Download Electron -- script: | - yarn gulp hygiene - displayName: Run Hygiene Checks + - script: | yarn monaco-compile-check displayName: Run Monaco Editor Checks + - script: | yarn valid-layers-check displayName: Run Valid Layers Checks + - script: | yarn compile displayName: Compile Sources + - script: | yarn download-builtin-extensions displayName: Download Built-in Extensions + - script: | ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests + displayName: Run Unit Tests (Electron) + +- script: | + yarn test-browser --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) + - script: | ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests + displayName: Run Integration Tests (Electron) + +- task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos + targetPath: .build/crashes + displayName: 'Publish Crash Reports' + continueOnError: true + condition: failed() + - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist new file mode 100644 index 00000000000..4efe1ce508f --- /dev/null +++ b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.allow-jit + + + diff --git a/build/azure-pipelines/darwin/helper-plugin-entitlements.plist b/build/azure-pipelines/darwin/helper-plugin-entitlements.plist new file mode 100644 index 00000000000..7cd9df032bd --- /dev/null +++ b/build/azure-pipelines/darwin/helper-plugin-entitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/build/azure-pipelines/darwin/helper-renderer-entitlements.plist b/build/azure-pipelines/darwin/helper-renderer-entitlements.plist new file mode 100644 index 00000000000..be8b7163da7 --- /dev/null +++ b/build/azure-pipelines/darwin/helper-renderer-entitlements.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + + diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 273a728927f..0500f84accc 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -96,9 +96,13 @@ steps: - script: | set -e ./scripts/test.sh --build --tfs "Unit Tests" - # APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`" - # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME" - displayName: Run unit tests + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | @@ -111,23 +115,72 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests + displayName: Run integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -# Web Smoke Tests disabled due to https://github.com/microsoft/vscode/issues/80308 -# - script: | -# set -e -# cd test/smoke -# yarn compile -# cd - -# yarn smoketest --web --headless -# continueOnError: true -# displayName: Run web smoke tests -# condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | set -e - pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + ./resources/server/test/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ + ./resources/server/test/test-remote-integration.sh + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin + APP_NAME="`ls $APP_ROOT | head -n 1`" + yarn smoketest --build "$APP_ROOT/$APP_NAME" + continueOnError: true + displayName: Run smoke tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + yarn smoketest --web --headless + continueOnError: true + displayName: Run smoke tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos + targetPath: .build/crashes + displayName: 'Publish Crash Reports' + continueOnError: true + condition: failed() + +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() + +- script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements + +- script: | + set -e + pushd $(agent.builddirectory)/VSCode-darwin && zip -r -X -y $(agent.builddirectory)/VSCode-darwin.zip * && popd displayName: Archive build - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 @@ -141,24 +194,77 @@ steps: { "keyCode": "CP-401337-Apple", "operationSetCode": "MacAppDeveloperSign", - "parameters": [ ], + "parameters": [ + { + "parameterName": "Hardening", + "parameterValue": "--options=runtime" + } + ], "toolName": "sign", "toolVersion": "1.0" } ] - SessionTimeout: 120 + SessionTimeout: 60 displayName: Codesign +- script: | + zip -d $(agent.builddirectory)/VSCode-darwin.zip "*.pkg" + displayName: Clean Archive + +- script: | + APP_ROOT=$(agent.builddirectory)/VSCode-darwin + APP_NAME="`ls $APP_ROOT | head -n 1`" + BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier") + echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER" + displayName: Export bundle identifier + +- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: 'ESRP CodeSign' + FolderPath: '$(agent.builddirectory)' + Pattern: 'VSCode-darwin.zip' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401337-Apple", + "operationSetCode": "MacAppNotarize", + "parameters": [ + { + "parameterName": "BundleId", + "parameterValue": "$(BundleIdentifier)" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 60 + displayName: Notarization + +- script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin + APP_NAME="`ls $APP_ROOT | head -n 1`" + "$APP_ROOT/$APP_NAME/Contents/Resources/app/bin/code" --export-default-configuration=.build + displayName: Verify start after signing (export configuration) + - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ ./build/azure-pipelines/darwin/publish.sh displayName: Publish +- script: | + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + yarn gulp upload-vscode-configuration + displayName: Upload configuration (for Bing settings search) + continueOnError: true + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' continueOnError: true diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh index a8067a5eefb..51ac2e6355d 100755 --- a/build/azure-pipelines/darwin/publish.sh +++ b/build/azure-pipelines/darwin/publish.sh @@ -1,9 +1,6 @@ #!/usr/bin/env bash set -e -# remove pkg from archive -zip -d ../VSCode-darwin.zip "*.pkg" - # publish the build node build/azure-pipelines/common/createAsset.js \ darwin \ @@ -11,18 +8,14 @@ node build/azure-pipelines/common/createAsset.js \ "VSCode-darwin-$VSCODE_QUALITY.zip" \ ../VSCode-darwin.zip -# package Remote Extension Host -pushd .. && mv vscode-reh-darwin vscode-server-darwin && zip -Xry vscode-server-darwin.zip vscode-server-darwin && popd +if [ "$VSCODE_ARCH" == "x64" ]; then + # package Remote Extension Host + pushd .. && mv vscode-reh-darwin vscode-server-darwin && zip -Xry vscode-server-darwin.zip vscode-server-darwin && popd -# publish Remote Extension Host -node build/azure-pipelines/common/createAsset.js \ - server-darwin \ - archive-unsigned \ - "vscode-server-darwin.zip" \ - ../vscode-server-darwin.zip - -# publish hockeyapp symbols -node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" x64 "$VSCODE_HOCKEYAPP_ID_MACOS" - -# upload configuration -yarn gulp upload-vscode-configuration + # publish Remote Extension Host + node build/azure-pipelines/common/createAsset.js \ + server-darwin \ + archive-unsigned \ + "vscode-server-darwin.zip" \ + ../vscode-server-darwin.zip +fi diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 4689451b54e..f9bdf7fef8e 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -8,7 +8,7 @@ pr: steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index b91b01138d0..cf1ced09dc8 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,13 +1,17 @@ pool: vmImage: 'Ubuntu-16.04' -trigger: none -pr: none +trigger: + branches: + include: ['master'] +pr: + branches: + include: ['master'] steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' @@ -27,10 +31,10 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git checkout origin/electron-6.0.x + git checkout origin/electron-11.x.y git merge origin/master # Push master branch into exploration branch - git push origin HEAD:electron-6.0.x + git push origin HEAD:electron-11.x.y displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 24e05537d9b..58e14ab0dcd 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -7,51 +7,83 @@ steps: sudo chmod +x /etc/init.d/xvfb sudo update-rc.d xvfb defaults sudo service xvfb start + - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.x" + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'vscode-build-cache' + - script: | CHILD_CONCURRENCY=1 yarn --frozen-lockfile displayName: Install Dependencies condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'vscode-build-cache' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | yarn electron x64 displayName: Download Electron + - script: | yarn gulp hygiene displayName: Run Hygiene Checks + - script: | yarn monaco-compile-check displayName: Run Monaco Editor Checks + - script: | yarn valid-layers-check displayName: Run Valid Layers Checks + - script: | yarn compile displayName: Compile Sources + - script: | yarn download-builtin-extensions displayName: Download Built-in Extensions + - script: | DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests + displayName: Run Unit Tests (Electron) + +- script: | + DISPLAY=:10 yarn test-browser --browser chromium --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) + - script: | DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests + displayName: Run Integration Tests (Electron) + +- task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-linux + targetPath: .build/crashes + displayName: 'Publish Crash Reports' + continueOnError: true + condition: failed() + - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml index 68ae4ee8b67..258f87ea3d2 100644 --- a/build/azure-pipelines/linux/product-build-linux-multiarch.yml +++ b/build/azure-pipelines/linux/product-build-linux-multiarch.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -107,7 +107,6 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/publish.sh displayName: Publish diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 573d7c7d4c2..031a491648a 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -52,21 +52,25 @@ steps: git merge $(node -p "require('./package.json').distro") displayName: Merge distro +- script: | + echo -n $VSCODE_ARCH > .build/arch + displayName: Prepare arch cache flag + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'npm-vscode' - script: | set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile + CHILD_CONCURRENCY=1 npm_config_arch=$(NPM_ARCH) yarn --frozen-lockfile displayName: Install dependencies condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'npm-vscode' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) @@ -85,50 +89,98 @@ steps: - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-linux-x64-min-ci + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-linux-x64-min-ci + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-linux-x64-min-ci + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci displayName: Build - script: | set -e service xvfb start displayName: Start xvfb - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + DISPLAY=:10 yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | # Figure out the full absolute path of the product we just built # including the remote server and configure the integration tests # to run with these builds instead of running out of sources. set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-x64 + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-x64" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests" - # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-x64" - displayName: Run integration tests - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - yarn gulp "vscode-linux-x64-build-deb" - yarn gulp "vscode-linux-x64-build-rpm" - yarn gulp "vscode-linux-x64-prepare-snap" - displayName: Build packages + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- task: PublishPipelineArtifact@0 + inputs: + artifactName: 'crash-dump-linux-$(VSCODE_ARCH)' + targetPath: .build/crashes + displayName: 'Publish Crash Reports' + continueOnError: true + condition: failed() + +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + displayName: Build deb, rpm packages + +- script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + displayName: Prepare snap package + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + +# needed for code signing +- task: UseDotNet@2 + displayName: 'Install .NET Core SDK 2.x' + inputs: + version: 2.x - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 inputs: ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '.build/linux/rpm/x86_64' + FolderPath: '.build/linux/rpm' Pattern: '*.rpm' signConfigType: inlineSignParams inlineOperation: | @@ -149,15 +201,16 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ ./build/azure-pipelines/linux/publish.sh displayName: Publish - task: PublishPipelineArtifact@0 displayName: 'Publish Pipeline Artifact' inputs: - artifactName: snap-x64 + artifactName: 'snap-$(VSCODE_ARCH)' targetPath: .build/linux/snap-tarball + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh index 3da6ea3eed6..72fe2ad7b30 100755 --- a/build/azure-pipelines/linux/publish.sh +++ b/build/azure-pipelines/linux/publish.sh @@ -4,11 +4,10 @@ REPO="$(pwd)" ROOT="$REPO/.." # Publish tarball -PLATFORM_LINUX="linux-x64" +PLATFORM_LINUX="linux-$VSCODE_ARCH" BUILDNAME="VSCode-$PLATFORM_LINUX" -BUILD="$ROOT/$BUILDNAME" BUILD_VERSION="$(date +%s)" -[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.tar.gz" +[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$VSCODE_ARCH-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$VSCODE_ARCH-$BUILD_VERSION.tar.gz" TARBALL_PATH="$ROOT/$TARBALL_FILENAME" rm -rf $ROOT/code-*.tar.* @@ -27,28 +26,37 @@ rm -rf $ROOT/vscode-server-*.tar.* node build/azure-pipelines/common/createAsset.js "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$SERVER_TARBALL_PATH" -# Publish hockeyapp symbols -node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "x64" "$VSCODE_HOCKEYAPP_ID_LINUX64" - # Publish DEB -PLATFORM_DEB="linux-deb-x64" -DEB_ARCH="amd64" +case $VSCODE_ARCH in + x64) DEB_ARCH="amd64" ;; + *) DEB_ARCH="$VSCODE_ARCH" ;; +esac + +PLATFORM_DEB="linux-deb-$VSCODE_ARCH" DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" node build/azure-pipelines/common/createAsset.js "$PLATFORM_DEB" package "$DEB_FILENAME" "$DEB_PATH" # Publish RPM -PLATFORM_RPM="linux-rpm-x64" -RPM_ARCH="x86_64" +case $VSCODE_ARCH in + x64) RPM_ARCH="x86_64" ;; + armhf) RPM_ARCH="armv7hl" ;; + arm64) RPM_ARCH="aarch64" ;; + *) RPM_ARCH="$VSCODE_ARCH" ;; +esac + +PLATFORM_RPM="linux-rpm-$VSCODE_ARCH" RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" node build/azure-pipelines/common/createAsset.js "$PLATFORM_RPM" package "$RPM_FILENAME" "$RPM_PATH" -# Publish Snap -# Pack snap tarball artifact, in order to preserve file perms -mkdir -p $REPO/.build/linux/snap-tarball -SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-x64.tar.gz" -rm -rf $SNAP_TARBALL_PATH -(cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) +if [ "$VSCODE_ARCH" == "x64" ]; then + # Publish Snap + # Pack snap tarball artifact, in order to preserve file perms + mkdir -p $REPO/.build/linux/snap-tarball + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" + rm -rf $SNAP_TARBALL_PATH + (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) +fi diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index a530499b313..39c39e86c9e 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/linux/xvfb.init b/build/azure-pipelines/linux/xvfb.init index 4d77d253a26..2365c09f3a4 100644 --- a/build/azure-pipelines/linux/xvfb.init +++ b/build/azure-pipelines/linux/xvfb.init @@ -19,7 +19,7 @@ [ "${NETWORKING}" = "no" ] && exit 0 PROG="/usr/bin/Xvfb" -PROG_OPTIONS=":10 -ac" +PROG_OPTIONS=":10 -ac -screen 0 1024x768x24" PROG_OUTPUT="/tmp/Xvfb.out" case "$1" in @@ -50,4 +50,4 @@ case "$1" in exit 1 esac -exit $RETVAL \ No newline at end of file +exit $RETVAL diff --git a/build/azure-pipelines/mixin.js b/build/azure-pipelines/mixin.js deleted file mode 100644 index efb7d4d1ca9..00000000000 --- a/build/azure-pipelines/mixin.js +++ /dev/null @@ -1,41 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -const json = require('gulp-json-editor'); -const buffer = require('gulp-buffer'); -const filter = require('gulp-filter'); -const es = require('event-stream'); -const vfs = require('vinyl-fs'); -const fancyLog = require('fancy-log'); -const ansiColors = require('ansi-colors'); - -function main() { - const quality = process.env['VSCODE_QUALITY']; - - if (!quality) { - console.log('Missing VSCODE_QUALITY, skipping mixin'); - return; - } - - const productJsonFilter = filter('product.json', { restore: true }); - - fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); - return vfs - .src(`quality/${quality}/**`, { base: `quality/${quality}` }) - .pipe(filter(f => !f.isDirectory())) - .pipe(productJsonFilter) - .pipe(buffer()) - .pipe(json(o => Object.assign({}, require('../product.json'), o))) - .pipe(productJsonFilter.restore) - .pipe(es.mapSync(function (f) { - fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); - return f; - })) - .pipe(vfs.dest('.')); -} - -main(); \ No newline at end of file diff --git a/build/azure-pipelines/mixin.ts b/build/azure-pipelines/mixin.ts new file mode 100644 index 00000000000..b6818d6957d --- /dev/null +++ b/build/azure-pipelines/mixin.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as json from 'gulp-json-editor'; +const buffer = require('gulp-buffer'); +import * as filter from 'gulp-filter'; +import * as es from 'event-stream'; +import * as Vinyl from 'vinyl'; +import * as vfs from 'vinyl-fs'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; +import * as fs from 'fs'; +import * as path from 'path'; + +interface IBuiltInExtension { + readonly name: string; + readonly version: string; + readonly repo: string; + readonly metadata: any; +} + +interface OSSProduct { + readonly builtInExtensions: IBuiltInExtension[]; + readonly webBuiltInExtensions?: IBuiltInExtension[]; +} + +interface Product { + readonly builtInExtensions?: IBuiltInExtension[] | { 'include'?: IBuiltInExtension[], 'exclude'?: string[] }; + readonly webBuiltInExtensions?: IBuiltInExtension[]; +} + +function main() { + const quality = process.env['VSCODE_QUALITY']; + + if (!quality) { + console.log('Missing VSCODE_QUALITY, skipping mixin'); + return; + } + + const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true }); + + fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); + return vfs + .src(`quality/${quality}/**`, { base: `quality/${quality}` }) + .pipe(filter(f => !f.isDirectory())) + .pipe(productJsonFilter) + .pipe(buffer()) + .pipe(json((o: Product) => { + const ossProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')) as OSSProduct; + let builtInExtensions = ossProduct.builtInExtensions; + + if (Array.isArray(o.builtInExtensions)) { + fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name)); + + builtInExtensions = o.builtInExtensions; + } else if (o.builtInExtensions) { + const include = o.builtInExtensions['include'] || []; + const exclude = o.builtInExtensions['exclude'] || []; + + fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name)); + fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name)); + fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude); + + builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name)); + builtInExtensions = [...builtInExtensions, ...include]; + + fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name)); + } else { + fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name)); + } + + return { webBuiltInExtensions: ossProduct.webBuiltInExtensions, ...o, builtInExtensions }; + })) + .pipe(productJsonFilter.restore) + .pipe(es.mapSync(function (f: Vinyl) { + fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); + return f; + })) + .pipe(vfs.dest('.')); +} + +main(); diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index a98b5f4f77e..f4ff9400178 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,146 +1,3 @@ -resources: - containers: - - container: vscode-x64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - endpoint: VSCodeHub - - container: snapcraft - image: snapcore/snapcraft:stable - -jobs: -- job: Compile - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - steps: - - template: product-compile.yml - -- job: Windows - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32'], 'true')) - pool: - vmImage: VS2017-Win2016 - variables: - VSCODE_ARCH: x64 - dependsOn: - - Compile - steps: - - template: win32/product-build-win32.yml - -- job: Windows32 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) - pool: - vmImage: VS2017-Win2016 - variables: - VSCODE_ARCH: ia32 - dependsOn: - - Compile - steps: - - template: win32/product-build-win32.yml - -- job: Linux - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - dependsOn: - - Compile - steps: - - template: linux/product-build-linux.yml - -- job: LinuxSnap - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - container: snapcraft - dependsOn: Linux - steps: - - template: linux/snap-build-linux.yml - -- job: LinuxArmhf - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: armhf - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml - -- job: LinuxArm64 - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: arm64 - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml - -- job: LinuxAlpine - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: alpine - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml - -- job: LinuxWeb - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WEB'], 'true')) - pool: - vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: x64 - dependsOn: - - Compile - steps: - - template: web/product-build-web.yml - -- job: macOS - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) - pool: - vmImage: macOS-latest - dependsOn: - - Compile - steps: - - template: darwin/product-build-darwin.yml - -- job: Release - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) - pool: - vmImage: 'Ubuntu-16.04' - dependsOn: - - Windows - - Windows32 - - Linux - - LinuxSnap - - LinuxArmhf - - LinuxArm64 - - LinuxAlpine - - macOS - steps: - - template: release.yml - -- job: Mooncake - pool: - vmImage: 'Ubuntu-16.04' - condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - dependsOn: - - Windows - - Windows32 - - Linux - - LinuxSnap - - LinuxArmhf - - LinuxArm64 - - LinuxAlpine - - LinuxWeb - - macOS - steps: - - template: sync-mooncake.yml - trigger: none pr: none @@ -150,3 +7,159 @@ schedules: branches: include: - master + +resources: + containers: + - container: vscode-x64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 + endpoint: VSCodeHub + - container: vscode-arm64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-arm64 + endpoint: VSCodeHub + - container: vscode-armhf + image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-armhf + endpoint: VSCodeHub + - container: snapcraft + image: snapcore/snapcraft:stable + +stages: +- stage: Compile + jobs: + - job: Compile + pool: + vmImage: 'Ubuntu-16.04' + container: vscode-x64 + steps: + - template: product-compile.yml + +- stage: Windows + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: VS2017-Win2016 + jobs: + - job: Windows + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + + - job: Windows32 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: ia32 + steps: + - template: win32/product-build-win32.yml + + - job: WindowsARM64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: arm64 + steps: + - template: win32/product-build-win32-arm64.yml + +- stage: Linux + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: 'Ubuntu-16.04' + jobs: + - job: Linux + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: vscode-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + steps: + - template: linux/product-build-linux.yml + + - job: LinuxSnap + dependsOn: + - Linux + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: x64 + steps: + - template: linux/snap-build-linux.yml + + - job: LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + container: vscode-armhf + variables: + VSCODE_ARCH: armhf + NPM_ARCH: armv7l + steps: + - template: linux/product-build-linux.yml + + - job: LinuxArm64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + container: vscode-arm64 + variables: + VSCODE_ARCH: arm64 + NPM_ARCH: arm64 + steps: + - template: linux/product-build-linux.yml + + - job: LinuxAlpine + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) + variables: + VSCODE_ARCH: alpine + steps: + - template: linux/product-build-linux-multiarch.yml + + - job: LinuxWeb + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true')) + variables: + VSCODE_ARCH: x64 + steps: + - template: web/product-build-web.yml + +- stage: macOS + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: macOS-latest + jobs: + - job: macOS + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml + +- stage: Mooncake + dependsOn: + - Windows + - Linux + - macOS + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: 'Ubuntu-16.04' + jobs: + - job: SyncMooncake + displayName: Sync Mooncake + steps: + - template: sync-mooncake.yml + +- stage: Publish + dependsOn: + - Windows + - Linux + - macOS + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) + pool: + vmImage: 'Ubuntu-16.04' + jobs: + - job: BuildService + displayName: Build Service + steps: + - template: release.yml diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index db6524be03b..2eff40b53d1 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -16,7 +16,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 @@ -52,9 +52,13 @@ steps: displayName: Merge distro condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) +- script: | + echo -n $VSCODE_ARCH > .build/arch + displayName: Prepare arch cache flag + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'npm-vscode' condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) @@ -67,7 +71,7 @@ steps: - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'npm-vscode' condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 1d4ab83e1ac..10b6aa4e16a 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -9,7 +9,7 @@ pr: none steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -63,7 +63,7 @@ steps: MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." - MESSAGE2="[@octref, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." + MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ -H 'Content-type: application/json; charset=utf-8' \ diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index a5ef449b77b..bbce67221da 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -36,6 +36,18 @@ function updateDTSFile(outPath: string, tag: string) { fs.writeFileSync(outPath, newContent); } +function repeat(str: string, times: number): string { + const result = new Array(times); + for (let i = 0; i < times; i++) { + result[i] = str; + } + return result.join(''); +} + +function convertTabsToSpaces(str: string): string { + return str.replace(/\t/gm, value => repeat(' ', value.length)); +} + function getNewFileContent(content: string, tag: string) { const oldheader = [ `/*---------------------------------------------------------------------------------------------`, @@ -44,7 +56,7 @@ function getNewFileContent(content: string, tag: string) { ` *--------------------------------------------------------------------------------------------*/` ].join('\n'); - return getNewFileHeader(tag) + content.slice(oldheader.length); + return convertTabsToSpaces(getNewFileHeader(tag) + content.slice(oldheader.length)); } function getNewFileHeader(tag: string) { @@ -54,13 +66,13 @@ function getNewFileHeader(tag: string) { const header = [ `// Type definitions for Visual Studio Code ${shorttag}`, `// Project: https://github.com/microsoft/vscode`, - `// Definitions by: Visual Studio Code Team, Microsoft `, + `// Definitions by: Visual Studio Code Team, Microsoft `, `// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`, ``, `/*---------------------------------------------------------------------------------------------`, ` * Copyright (c) Microsoft Corporation. All rights reserved.`, ` * Licensed under the MIT License.`, - ` * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.`, + ` * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information.`, ` *--------------------------------------------------------------------------------------------*/`, ``, `/**`, diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index 2641830a413..49dfc9ced80 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: diff --git a/build/azure-pipelines/upload-cdn.ts b/build/azure-pipelines/upload-cdn.ts new file mode 100644 index 00000000000..71589033867 --- /dev/null +++ b/build/azure-pipelines/upload-cdn.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as es from 'event-stream'; +import * as Vinyl from 'vinyl'; +import * as vfs from 'vinyl-fs'; +import * as util from '../lib/util'; +import * as filter from 'gulp-filter'; +import * as gzip from 'gulp-gzip'; +const azure = require('gulp-azure-storage'); + +const root = path.dirname(path.dirname(__dirname)); +const commit = util.getVersion(root); + +function main() { + return vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) + .pipe(filter(f => !f.isDirectory())) + .pipe(gzip({ append: false })) + .pipe(es.through(function (data: Vinyl) { + console.log('Uploading CDN file:', data.relative); // debug + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + key: process.env.AZURE_STORAGE_ACCESS_KEY, + container: process.env.VSCODE_QUALITY, + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' + } + })); +} + +main(); diff --git a/build/azure-pipelines/upload-sourcemaps.js b/build/azure-pipelines/upload-sourcemaps.ts similarity index 59% rename from build/azure-pipelines/upload-sourcemaps.js rename to build/azure-pipelines/upload-sourcemaps.ts index b70335efe5b..6a13aaf172d 100644 --- a/build/azure-pipelines/upload-sourcemaps.js +++ b/build/azure-pipelines/upload-sourcemaps.ts @@ -5,44 +5,54 @@ 'use strict'; -const path = require('path'); -const es = require('event-stream'); +import * as path from 'path'; +import * as es from 'event-stream'; +import * as Vinyl from 'vinyl'; +import * as vfs from 'vinyl-fs'; +import * as util from '../lib/util'; +// @ts-ignore +import * as deps from '../dependencies'; const azure = require('gulp-azure-storage'); -const vfs = require('vinyl-fs'); -const util = require('../lib/util'); + const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); // optionally allow to pass in explicit base/maps to upload const [, , base, maps] = process.argv; -const fetch = function (base, maps = `${base}/**/*.map`) { +function src(base: string, maps = `${base}/**/*.map`) { return vfs.src(maps, { base }) - .pipe(es.mapSync(f => { + .pipe(es.mapSync((f: Vinyl) => { f.path = `${f.base}/core/${f.relative}`; return f; })); -}; +} function main() { const sources = []; // vscode client maps (default) if (!base) { - const vs = fetch('out-vscode-min'); // client source-maps only + const vs = src('out-vscode-min'); // client source-maps only sources.push(vs); + const productionDependencies: { name: string, path: string, version: string }[] = deps.getProductionDependencies(root); + const productionDependenciesSrc = productionDependencies.map(d => path.relative(root, d.path)).map(d => `./${d}/**/*.map`); + const nodeModules = vfs.src(productionDependenciesSrc, { base: '.' }) + .pipe(util.cleanNodeModules(path.join(root, 'build', '.moduleignore'))); + sources.push(nodeModules); + const extensionsOut = vfs.src(['.build/extensions/**/*.js.map', '!**/node_modules/**'], { base: '.build' }); sources.push(extensionsOut); } // specific client base/maps else { - sources.push(fetch(base, maps)); + sources.push(src(base, maps)); } return es.merge(...sources) - .pipe(es.through(function (data) { + .pipe(es.through(function (data: Vinyl) { console.log('Uploading Sourcemap', data.relative); // debug this.emit('data', data); })) diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 0c338203b4d..aded67174f4 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -88,6 +88,13 @@ steps: yarn gulp vscode-web-min-ci displayName: Build +- script: | + set -e + AZURE_STORAGE_ACCOUNT="$(web-storage-account)" \ + AZURE_STORAGE_ACCESS_KEY="$(web-storage-key)" \ + node build/azure-pipelines/upload-cdn.js + displayName: Upload to CDN + # upload only the workbench.web.api.js source maps because # we just compiled these bits in the previous step and the # general task to upload source maps has already been run diff --git a/build/azure-pipelines/win32/ESRPClient/packages.config b/build/azure-pipelines/win32/ESRPClient/packages.config index d7a6f144f47..ef586de9762 100644 --- a/build/azure-pipelines/win32/ESRPClient/packages.config +++ b/build/azure-pipelines/win32/ESRPClient/packages.config @@ -1,4 +1,4 @@ - + diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 8e55440c3a8..6107eacc66c 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -1,54 +1,84 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.x" + - task: UsePythonVersion@0 inputs: versionSpec: '2.x' addToPath: true + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'vscode-build-cache' + - powershell: | yarn --frozen-lockfile env: CHILD_CONCURRENCY: "1" displayName: Install Dependencies condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: 'vscode-build-cache' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - powershell: | yarn electron -- script: | - yarn gulp hygiene - displayName: Run Hygiene Checks + displayName: Download Electron + - powershell: | yarn monaco-compile-check displayName: Run Monaco Editor Checks + - script: | yarn valid-layers-check displayName: Run Valid Layers Checks + - powershell: | yarn compile displayName: Compile Sources + - powershell: | yarn download-builtin-extensions displayName: Download Built-in Extensions + - powershell: | .\scripts\test.bat --tfs "Unit Tests" - displayName: Run Unit Tests + displayName: Run Unit Tests (Electron) + +- powershell: | + yarn test-browser --browser chromium --browser firefox --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) + - powershell: | .\scripts\test-integration.bat --tfs "Integration Tests" - displayName: Run Integration Tests + displayName: Run Integration Tests (Electron) + +- task: PublishPipelineArtifact@0 + displayName: 'Publish Crash Reports' + inputs: + artifactName: crash-dump-windows + targetPath: .build\crashes + continueOnError: true + condition: failed() + - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/build/azure-pipelines/win32/import-esrp-auth-cert.ps1 b/build/azure-pipelines/win32/import-esrp-auth-cert.ps1 index c345c780231..f11f878c83f 100644 --- a/build/azure-pipelines/win32/import-esrp-auth-cert.ps1 +++ b/build/azure-pipelines/win32/import-esrp-auth-cert.ps1 @@ -1,14 +1,14 @@ -Param( - [string]$AuthCertificateBase64, - [string]$AuthCertificateKey -) +param ($CertBase64) +$ErrorActionPreference = "Stop" -# Import auth certificate -$AuthCertificateFileName = [System.IO.Path]::GetTempFileName() -$AuthCertificateBytes = [Convert]::FromBase64String($AuthCertificateBase64) -[IO.File]::WriteAllBytes($AuthCertificateFileName, $AuthCertificateBytes) -$AuthCertificate = Import-PfxCertificate -FilePath $AuthCertificateFileName -CertStoreLocation Cert:\LocalMachine\My -Password (ConvertTo-SecureString $AuthCertificateKey -AsPlainText -Force) -rm $AuthCertificateFileName -$ESRPAuthCertificateSubjectName = $AuthCertificate.Subject +$CertBytes = [System.Convert]::FromBase64String($CertBase64) +$CertCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection +$CertCollection.Import($CertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) -Write-Output ("##vso[task.setvariable variable=ESRPAuthCertificateSubjectName;]$ESRPAuthCertificateSubjectName") \ No newline at end of file +$CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") +$CertStore.Open("ReadWrite") +$CertStore.AddRange($CertCollection) +$CertStore.Close() + +$ESRPAuthCertificateSubjectName = $CertCollection[0].Subject +Write-Output ("##vso[task.setvariable variable=ESRPAuthCertificateSubjectName;]$ESRPAuthCertificateSubjectName") diff --git a/build/azure-pipelines/win32/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml new file mode 100644 index 00000000000..2e53167e613 --- /dev/null +++ b/build/azure-pipelines/win32/product-build-win32-arm64.yml @@ -0,0 +1,192 @@ +steps: +- powershell: | + mkdir .build -ea 0 + "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit + "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit, .build/quality' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- powershell: | + $ErrorActionPreference = "Stop" + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: NodeTool@0 + inputs: + versionSpec: "12.14.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + +- task: UsePythonVersion@0 + inputs: + versionSpec: '2.x' + addToPath: true + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + + exec { git config user.email "vscode@microsoft.com" } + exec { git config user.name "VSCode" } + + mkdir .build -ea 0 + "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch + displayName: Prepare tooling + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } + exec { git fetch distro } + exec { git merge $(node -p "require('./package.json').distro") } + displayName: Merge distro + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:npm_config_arch="$(VSCODE_ARCH)" + $env:CHILD_CONCURRENCY="1" + exec { yarn --frozen-lockfile } + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/mixin } + displayName: Mix in quality + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" } + exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" } + exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } + displayName: Build + +- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: 'ESRP CodeSign' + FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)' + Pattern: '*.dll,*.exe,*.node' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "VS Code" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "https://code.visualstudio.com/" + }, + { + "parameterName": "Append", + "parameterValue": "/as" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolVerify", + "parameters": [ + { + "parameterName": "VerifyAll", + "parameterValue": "/all" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 + +- task: NuGetCommand@2 + displayName: Install ESRPClient.exe + inputs: + restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' + feedsToUse: config + nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' + externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b + restoreDirectory: packages + +- task: ESRPImportCertTask@1 + displayName: Import ESRP Request Signing Certificate + inputs: + ESRP: 'ESRP CodeSign' + +- task: PowerShell@2 + inputs: + targetType: filePath + filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 + arguments: "$(ESRP-SSL-AADAuth)" + displayName: Import ESRP Auth Certificate + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + .\build\azure-pipelines\win32\publish.ps1 + displayName: Publish + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + continueOnError: true diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 4c9336c6c1c..43bd2479a4e 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -21,7 +21,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "12.13.0" + versionSpec: "12.14.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -109,7 +109,14 @@ steps: $ErrorActionPreference = "Stop" exec { yarn electron $(VSCODE_ARCH) } exec { .\scripts\test.bat --build --tfs "Unit Tests" } - displayName: Run unit tests + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - powershell: | @@ -122,9 +129,41 @@ steps: $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json $AppNameShort = $AppProductJson.nameShort exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } - displayName: Run integration tests + displayName: Run integration tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-windows-$(VSCODE_ARCH) + targetPath: .build\crashes + displayName: 'Publish Crash Reports' + continueOnError: true + condition: failed() + +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: succeededOrFailed() + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 inputs: ConnectedServiceName: 'ESRP CodeSign' @@ -186,7 +225,7 @@ steps: restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' feedsToUse: config nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' - externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b + externalFeedCredentials: 'ESRP Nuget' restoreDirectory: packages - task: ESRPImportCertTask@1 @@ -194,9 +233,11 @@ steps: inputs: ESRP: 'ESRP CodeSign' -- powershell: | - $ErrorActionPreference = "Stop" - .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key) +- task: PowerShell@2 + inputs: + targetType: filePath + filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 + arguments: "$(ESRP-SSL-AADAuth)" displayName: Import ESRP Auth Certificate - powershell: | @@ -204,7 +245,6 @@ steps: $ErrorActionPreference = "Stop" $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" - $env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)" $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" .\build\azure-pipelines\win32\publish.ps1 displayName: Publish diff --git a/build/azure-pipelines/win32/publish.ps1 b/build/azure-pipelines/win32/publish.ps1 index 5a22d4749cf..a225f9d5fdf 100644 --- a/build/azure-pipelines/win32/publish.ps1 +++ b/build/azure-pipelines/win32/publish.ps1 @@ -11,26 +11,26 @@ $SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe" $UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe" $Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip" $LegacyServer = "$Root\vscode-reh-win32-$Arch" -$ServerName = "vscode-server-win32-$Arch" -$Server = "$Root\$ServerName" +$Server = "$Root\vscode-server-win32-$Arch" $ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip" $Build = "$Root\VSCode-win32-$Arch" # Create server archive -exec { Rename-Item -Path $LegacyServer -NewName $ServerName } -exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r } +if ("$Arch" -ne "arm64") { + exec { xcopy $LegacyServer $Server /H /E /I } + exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r } +} # get version $PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json $Version = $PackageJson.version -$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" } +$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-$Arch" } exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Zip } exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $SystemExe } exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $UserExe } -exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip } -# publish hockeyapp symbols -$hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" } -exec { node build/azure-pipelines/common/symbols.js "$env:VSCODE_MIXIN_PASSWORD" "$env:VSCODE_HOCKEYAPP_TOKEN" "$Arch" $hockeyAppId } +if ("$Arch" -ne "arm64") { + exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip } +} diff --git a/build/azure-pipelines/win32/sign.ps1 b/build/azure-pipelines/win32/sign.ps1 index 00c4d42d9db..b73db31207f 100644 --- a/build/azure-pipelines/win32/sign.ps1 +++ b/build/azure-pipelines/win32/sign.ps1 @@ -12,6 +12,7 @@ $Auth = Create-TmpJson @{ SubjectName = $env:ESRPAuthCertificateSubjectName StoreLocation = "LocalMachine" StoreName = "My" + SendX5c = "true" } RequestSigningCert = @{ SubjectName = $env:ESRPCertificateSubjectName @@ -67,4 +68,4 @@ $Input = Create-TmpJson @{ $Output = [System.IO.Path]::GetTempFileName() $ScriptPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent -& "$ScriptPath\ESRPClient\packages\EsrpClient.1.0.27\tools\ESRPClient.exe" Sign -a $Auth -p $Policy -i $Input -o $Output \ No newline at end of file +& "$ScriptPath\ESRPClient\packages\Microsoft.ESRPClient.*\tools\ESRPClient.exe" Sign -a $Auth -p $Policy -i $Input -o $Output diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json deleted file mode 100644 index 6a9ef0b745e..00000000000 --- a/build/builtInExtensions.json +++ /dev/null @@ -1,47 +0,0 @@ -[ - { - "name": "ms-vscode.node-debug", - "version": "1.42.0", - "repo": "https://github.com/Microsoft/vscode-node-debug", - "metadata": { - "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", - "publisherId": { - "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", - "publisherName": "ms-vscode", - "displayName": "Microsoft", - "flags": "verified" - }, - "publisherDisplayName": "Microsoft" - } - }, - { - "name": "ms-vscode.node-debug2", - "version": "1.42.0", - "repo": "https://github.com/Microsoft/vscode-node-debug2", - "metadata": { - "id": "36d19e17-7569-4841-a001-947eb18602b2", - "publisherId": { - "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", - "publisherName": "ms-vscode", - "displayName": "Microsoft", - "flags": "verified" - }, - "publisherDisplayName": "Microsoft" - } - }, - { - "name": "ms-vscode.references-view", - "version": "0.0.47", - "repo": "https://github.com/Microsoft/vscode-reference-view", - "metadata": { - "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", - "publisherId": { - "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", - "publisherName": "ms-vscode", - "displayName": "Microsoft", - "flags": "verified" - }, - "publisherDisplayName": "Microsoft" - } - } -] diff --git a/build/builtin/browser-main.js b/build/builtin/browser-main.js index 60b30655c0c..e5956179567 100644 --- a/build/builtin/browser-main.js +++ b/build/builtin/browser-main.js @@ -6,11 +6,10 @@ const fs = require('fs'); const path = require('path'); const os = require('os'); -// @ts-ignore review const { remote } = require('electron'); const dialog = remote.dialog; -const builtInExtensionsPath = path.join(__dirname, '..', 'builtInExtensions.json'); +const builtInExtensionsPath = path.join(__dirname, '..', '..', 'product.json'); const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); function readJson(filePath) { @@ -111,7 +110,7 @@ function render(el, state) { function main() { const el = document.getElementById('extensions'); - const builtin = readJson(builtInExtensionsPath); + const builtin = readJson(builtInExtensionsPath).builtInExtensions; let control; try { @@ -123,4 +122,4 @@ function main() { render(el, { builtin, control }); } -window.onload = main; \ No newline at end of file +window.onload = main; diff --git a/build/builtin/main.js b/build/builtin/main.js index b094a67cac5..7379de7a93d 100644 --- a/build/builtin/main.js +++ b/build/builtin/main.js @@ -10,11 +10,11 @@ const path = require('path'); let window = null; app.once('ready', () => { - window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true } }); + window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true, enableWebSQL: false, nativeWindowOpen: true } }); window.setMenuBarVisibility(false); window.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true })); // window.webContents.openDevTools(); window.once('closed', () => window = null); }); -app.on('window-all-closed', () => app.quit()); \ No newline at end of file +app.on('window-all-closed', () => app.quit()); diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts new file mode 100644 index 00000000000..ee5d2eeb17b --- /dev/null +++ b/build/darwin/sign.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as codesign from 'electron-osx-sign'; +import * as path from 'path'; +import * as util from '../lib/util'; +import * as product from '../../product.json'; + +async function main(): Promise { + const buildDir = process.env['AGENT_BUILDDIRECTORY']; + const tempDir = process.env['AGENT_TEMPDIRECTORY']; + + if (!buildDir) { + throw new Error('$AGENT_BUILDDIRECTORY not set'); + } + + if (!tempDir) { + throw new Error('$AGENT_TEMPDIRECTORY not set'); + } + + const baseDir = path.dirname(__dirname); + const appRoot = path.join(buildDir, 'VSCode-darwin'); + const appName = product.nameLong + '.app'; + const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks'); + const helperAppBaseName = product.nameShort; + const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app'; + const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app'; + const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app'; + + const defaultOpts: codesign.SignOptions = { + app: path.join(appRoot, appName), + platform: 'darwin', + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), + hardenedRuntime: true, + 'pre-auto-entitlements': false, + 'pre-embed-provisioning-profile': false, + keychain: path.join(tempDir, 'buildagent.keychain'), + version: util.getElectronVersion(), + identity: '99FM488X57', + 'gatekeeper-assess': false + }; + + const appOpts = { + ...defaultOpts, + // TODO(deepak1556): Incorrectly declared type in electron-osx-sign + ignore: (filePath: string) => { + return filePath.includes(gpuHelperAppName) || + filePath.includes(pluginHelperAppName) || + filePath.includes(rendererHelperAppName); + } + }; + + const gpuHelperOpts: codesign.SignOptions = { + ...defaultOpts, + app: path.join(appFrameworkPath, gpuHelperAppName), + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), + }; + + const pluginHelperOpts: codesign.SignOptions = { + ...defaultOpts, + app: path.join(appFrameworkPath, pluginHelperAppName), + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), + }; + + const rendererHelperOpts: codesign.SignOptions = { + ...defaultOpts, + app: path.join(appFrameworkPath, rendererHelperAppName), + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), + }; + + await codesign.signAsync(gpuHelperOpts); + await codesign.signAsync(pluginHelperOpts); + await codesign.signAsync(rendererHelperOpts); + await codesign.signAsync(appOpts as any); +} + +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index b686d280189..aa0282bff57 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -14,8 +14,10 @@ const i18n = require('./lib/i18n'); const standalone = require('./lib/standalone'); const cp = require('child_process'); const compilation = require('./lib/compilation'); -const monacoapi = require('./monaco/api'); +const monacoapi = require('./lib/monaco-api'); const fs = require('fs'); +const webpack = require('webpack'); +const webpackGulp = require('webpack-stream'); let root = path.dirname(__dirname); let sha1 = util.getVersion(root); @@ -41,7 +43,7 @@ let editorEntryPoints = [ ]; let editorResources = [ - 'out-editor-build/vs/base/browser/ui/codiconLabel/**/*.ttf' + 'out-editor-build/vs/base/browser/ui/codicons/**/*.ttf' ]; let BUNDLED_FILE_HEADER = [ @@ -49,7 +51,7 @@ let BUNDLED_FILE_HEADER = [ ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Version: ' + headerVersion, ' * Released under the MIT license', - ' * https://github.com/Microsoft/vscode/blob/master/LICENSE.txt', + ' * https://github.com/microsoft/vscode/blob/master/LICENSE.txt', ' *-----------------------------------------------------------*/', '' ].join('\n'); @@ -70,13 +72,8 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { apiusages, extrausages ], - libs: [ - `lib.es5.d.ts`, - `lib.dom.d.ts`, - `lib.webworker.importscripts.d.ts` - ], shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers - importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/, + importIgnorePattern: /(^vs\/css!)/, destRoot: path.join(root, 'out-editor-src'), redirects: [] }); @@ -129,6 +126,8 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => }); const compileEditorESMTask = task.define('compile-editor-esm', () => { + const KEEP_PREV_ANALYSIS = false; + const FAIL_ON_PURPOSE = false; console.log(`Launching the TS compiler at ${path.join(__dirname, '../out-editor-esm')}...`); let result; if (process.platform === 'win32') { @@ -144,44 +143,48 @@ const compileEditorESMTask = task.define('compile-editor-esm', () => { console.log(result.stdout.toString()); console.log(result.stderr.toString()); - if (result.status !== 0) { + if (FAIL_ON_PURPOSE || result.status !== 0) { console.log(`The TS Compilation failed, preparing analysis folder...`); const destPath = path.join(__dirname, '../../vscode-monaco-editor-esm-analysis'); - return util.rimraf(destPath)().then(() => { - fs.mkdirSync(destPath); - - // initialize a new repository - cp.spawnSync(`git`, [`init`], { - cwd: destPath - }); - + const keepPrevAnalysis = (KEEP_PREV_ANALYSIS && fs.existsSync(destPath)); + const cleanDestPath = (keepPrevAnalysis ? Promise.resolve() : util.rimraf(destPath)()); + return cleanDestPath.then(() => { // build a list of files to copy const files = util.rreddir(path.join(__dirname, '../out-editor-esm')); - // copy files from src - for (const file of files) { - const srcFilePath = path.join(__dirname, '../src', file); - const dstFilePath = path.join(destPath, file); - if (fs.existsSync(srcFilePath)) { - util.ensureDir(path.dirname(dstFilePath)); - const contents = fs.readFileSync(srcFilePath).toString().replace(/\r\n|\r|\n/g, '\n'); - fs.writeFileSync(dstFilePath, contents); + if (!keepPrevAnalysis) { + fs.mkdirSync(destPath); + + // initialize a new repository + cp.spawnSync(`git`, [`init`], { + cwd: destPath + }); + + // copy files from src + for (const file of files) { + const srcFilePath = path.join(__dirname, '../src', file); + const dstFilePath = path.join(destPath, file); + if (fs.existsSync(srcFilePath)) { + util.ensureDir(path.dirname(dstFilePath)); + const contents = fs.readFileSync(srcFilePath).toString().replace(/\r\n|\r|\n/g, '\n'); + fs.writeFileSync(dstFilePath, contents); + } } + + // create an initial commit to diff against + cp.spawnSync(`git`, [`add`, `.`], { + cwd: destPath + }); + + // create the commit + cp.spawnSync(`git`, [`commit`, `-m`, `"original sources"`, `--no-gpg-sign`], { + cwd: destPath + }); } - // create an initial commit to diff against - cp.spawnSync(`git`, [`add`, `.`], { - cwd: destPath - }); - - // create the commit - cp.spawnSync(`git`, [`commit`, `-m`, `"original sources"`, `--no-gpg-sign`], { - cwd: destPath - }); - - // copy files from esm + // copy files from tree shaken src for (const file of files) { - const srcFilePath = path.join(__dirname, '../out-editor-esm', file); + const srcFilePath = path.join(__dirname, '../out-editor-src', file); const dstFilePath = path.join(destPath, file); if (fs.existsSync(srcFilePath)) { util.ensureDir(path.dirname(dstFilePath)); @@ -227,8 +230,13 @@ function toExternalDTS(contents) { if (line.indexOf('declare namespace monaco.') === 0) { lines[i] = line.replace('declare namespace monaco.', 'export namespace '); } + + if (line.indexOf('declare let MonacoEnvironment') === 0) { + lines[i] = `declare global {\n let MonacoEnvironment: Environment | undefined;\n}`; + // lines[i] = line.replace('declare namespace monaco.', 'export namespace '); + } } - return lines.join('\n'); + return lines.join('\n').replace(/\n\n\n+/g, '\n\n'); } function filterStream(testFunc) { @@ -273,7 +281,7 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { // version.txt gulp.src('build/monaco/version.txt') .pipe(es.through(function (data) { - data.contents = Buffer.from(`monaco-editor-core: https://github.com/Microsoft/vscode/tree/${sha1}`); + data.contents = Buffer.from(`monaco-editor-core: https://github.com/microsoft/vscode/tree/${sha1}`); this.emit('data', data); })) .pipe(gulp.dest('out-monaco-editor-core')), @@ -327,6 +335,13 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { ); }); +gulp.task('extract-editor-src', + task.series( + util.rimraf('out-editor-src'), + extractEditorSrcTask + ) +); + gulp.task('editor-distro', task.series( task.parallel( @@ -353,6 +368,49 @@ gulp.task('editor-distro', ) ); +const bundleEditorESMTask = task.define('editor-esm-bundle-webpack', () => { + const result = es.through(); + + const webpackConfigPath = path.join(root, 'build/monaco/monaco.webpack.config.js'); + + const webpackConfig = { + ...require(webpackConfigPath), + ...{ mode: 'production' } + }; + + const webpackDone = (err, stats) => { + if (err) { + result.emit('error', err); + return; + } + const { compilation } = stats; + if (compilation.errors.length > 0) { + result.emit('error', compilation.errors.join('\n')); + } + if (compilation.warnings.length > 0) { + result.emit('data', compilation.warnings.join('\n')); + } + }; + + return webpackGulp(webpackConfig, webpack, webpackDone) + .pipe(gulp.dest('out-editor-esm-bundle')); +}); + +gulp.task('editor-esm-bundle', + task.series( + task.parallel( + util.rimraf('out-editor-src'), + util.rimraf('out-editor-esm'), + util.rimraf('out-monaco-editor-core'), + util.rimraf('out-editor-esm-bundle'), + ), + extractEditorSrcTask, + createESMSourcesAndResourcesTask, + compileEditorESMTask, + bundleEditorESMTask, + ) +); + gulp.task('monacodts', task.define('monacodts', () => { const result = monacoapi.execute(); fs.writeFileSync(result.filePath, result.content); @@ -398,10 +456,8 @@ function createTscCompileTask(watch) { // e.g. src/vs/base/common/strings.ts(663,5): error TS2322: Type '1234' is not assignable to type 'string'. let fullpath = path.join(root, match[1]); let message = match[3]; - // @ts-ignore reporter(fullpath + message); } else { - // @ts-ignore reporter(str); } } diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 25cccf6d8e5..6ae72e4cf06 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -8,9 +8,11 @@ require('events').EventEmitter.defaultMaxListeners = 100; const gulp = require('gulp'); const path = require('path'); +const nodeUtil = require('util'); const tsb = require('gulp-tsb'); const es = require('event-stream'); const filter = require('gulp-filter'); +const webpack = require('webpack'); const util = require('./lib/util'); const task = require('./lib/task'); const watcher = require('./lib/watch'); @@ -21,6 +23,8 @@ const nlsDev = require('vscode-nls-dev'); const root = path.dirname(__dirname); const commit = util.getVersion(root); const plumber = require('gulp-plumber'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); const ext = require('./lib/extensions'); const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); @@ -161,9 +165,84 @@ gulp.task(compileExtensionsBuildLegacyTask); const cleanExtensionsBuildTask = task.define('clean-extensions-build', util.rimraf('.build/extensions')); const compileExtensionsBuildTask = task.define('compile-extensions-build', task.series( cleanExtensionsBuildTask, - task.define('bundle-extensions-build', () => ext.packageLocalExtensionsStream().pipe(gulp.dest('.build'))), - task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream().pipe(gulp.dest('.build'))), + task.define('bundle-extensions-build', () => ext.packageLocalExtensionsStream(false).pipe(gulp.dest('.build'))), + task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream(false).pipe(gulp.dest('.build'))), )); gulp.task(compileExtensionsBuildTask); exports.compileExtensionsBuildTask = compileExtensionsBuildTask; + +const compileWebExtensionsTask = task.define('compile-web', () => buildWebExtensions(false)); +gulp.task(compileWebExtensionsTask); +exports.compileWebExtensionsTask = compileWebExtensionsTask; + +const watchWebExtensionsTask = task.define('watch-web', () => buildWebExtensions(true)); +gulp.task(watchWebExtensionsTask); +exports.watchWebExtensionsTask = watchWebExtensionsTask; + +async function buildWebExtensions(isWatch) { + + const webpackConfigLocations = await nodeUtil.promisify(glob)( + path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'), + { ignore: ['**/node_modules'] } + ); + + const webpackConfigs = []; + + for (const webpackConfigPath of webpackConfigLocations) { + const configOrFnOrArray = require(webpackConfigPath); + function addConfig(configOrFn) { + if (typeof configOrFn === 'function') { + webpackConfigs.push(configOrFn({}, {})); + } else { + webpackConfigs.push(configOrFn); + } + } + addConfig(configOrFnOrArray); + } + function reporter(fullStats) { + if (Array.isArray(fullStats.children)) { + for (const stats of fullStats.children) { + const outputPath = stats.outputPath; + if (outputPath) { + const relativePath = path.relative(extensionsPath, outputPath).replace(/\\/g, '/'); + const match = relativePath.match(/[^\/]+(\/server|\/client)?/); + fancyLog(`Finished ${ansiColors.green('packaging web extension')} ${ansiColors.cyan(match[0])} with ${stats.errors.length} errors.`); + } + if (Array.isArray(stats.errors)) { + stats.errors.forEach(error => { + fancyLog.error(error); + }); + } + if (Array.isArray(stats.warnings)) { + stats.warnings.forEach(warning => { + fancyLog.warn(warning); + }); + } + } + } + } + return new Promise((resolve, reject) => { + if (isWatch) { + webpack(webpackConfigs).watch({}, (err, stats) => { + if (err) { + reject(); + } else { + reporter(stats.toJson()); + } + }); + } else { + webpack(webpackConfigs).run((err, stats) => { + if (err) { + fancyLog.error(err); + reject(); + } else { + reporter(stats.toJson()); + resolve(); + } + }); + } + }); +} + + diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 9af81603c8a..7f890e86098 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -3,176 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - const gulp = require('gulp'); const filter = require('gulp-filter'); const es = require('event-stream'); const gulpeslint = require('gulp-eslint'); -const tsfmt = require('typescript-formatter'); -const VinylFile = require('vinyl'); const vfs = require('vinyl-fs'); const path = require('path'); -const fs = require('fs'); -const pall = require('p-all'); const task = require('./lib/task'); - -/** - * Hygiene works by creating cascading subsets of all our files and - * passing them through a sequence of checks. Here are the current subsets, - * named according to the checks performed on them. Each subset contains - * the following one, as described in mathematical notation: - * - * all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript - */ - -const all = [ - '*', - 'build/**/*', - 'extensions/**/*', - 'scripts/**/*', - 'src/**/*', - 'test/**/*', - '!**/node_modules/**' -]; - -const indentationFilter = [ - '**', - - // except specific files - '!ThirdPartyNotices.txt', - '!LICENSE.{txt,rtf}', - '!LICENSES.chromium.html', - '!**/LICENSE', - '!src/vs/nls.js', - '!src/vs/nls.build.js', - '!src/vs/css.js', - '!src/vs/css.build.js', - '!src/vs/loader.js', - '!src/vs/base/common/insane/insane.js', - '!src/vs/base/common/marked/marked.js', - '!src/vs/base/node/terminateProcess.sh', - '!src/vs/base/node/cpuUsage.sh', - '!test/assert.js', - - // except specific folders - '!test/automation/out/**', - '!test/smoke/out/**', - '!extensions/vscode-api-tests/testWorkspace/**', - '!extensions/vscode-api-tests/testWorkspace2/**', - '!build/monaco/**', - '!build/win32/**', - - // except multiple specific files - '!**/package.json', - '!**/yarn.lock', - '!**/yarn-error.log', - - // except multiple specific folders - '!**/codicon/**', - '!**/fixtures/**', - '!**/lib/**', - '!extensions/**/out/**', - '!extensions/**/snippets/**', - '!extensions/**/syntaxes/**', - '!extensions/**/themes/**', - '!extensions/**/colorize-fixtures/**', - - // except specific file types - '!src/vs/*/**/*.d.ts', - '!src/typings/**/*.d.ts', - '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns}', - '!build/{lib,download}/**/*.js', - '!build/**/*.sh', - '!build/azure-pipelines/**/*.js', - '!build/azure-pipelines/**/*.config', - '!**/Dockerfile', - '!**/Dockerfile.*', - '!**/*.Dockerfile', - '!**/*.dockerfile', - '!extensions/markdown-language-features/media/*.js' -]; - -const copyrightFilter = [ - '**', - '!**/*.desktop', - '!**/*.json', - '!**/*.html', - '!**/*.template', - '!**/*.md', - '!**/*.bat', - '!**/*.cmd', - '!**/*.ico', - '!**/*.icns', - '!**/*.xml', - '!**/*.sh', - '!**/*.txt', - '!**/*.xpm', - '!**/*.opts', - '!**/*.disabled', - '!**/*.code-workspace', - '!**/*.js.map', - '!**/promise-polyfill/polyfill.js', - '!build/**/*.init', - '!resources/linux/snap/snapcraft.yaml', - '!resources/linux/snap/electron-launch', - '!resources/win32/bin/code.js', - '!resources/completions/**', - '!extensions/markdown-language-features/media/highlight.css', - '!extensions/html-language-features/server/src/modes/typescript/*', - '!extensions/*/server/bin/*', - '!src/vs/editor/test/node/classification/typescript-test.ts', - '!scripts/code-web.js' -]; - -const jsHygieneFilter = [ - 'src/**/*.js', - 'build/gulpfile.*.js', - '!src/vs/loader.js', - '!src/vs/css.js', - '!src/vs/nls.js', - '!src/vs/css.build.js', - '!src/vs/nls.build.js', - '!src/**/insane.js', - '!src/**/marked.js', - '!**/test/**' -]; - -const tsHygieneFilter = [ - 'src/**/*.ts', - 'test/**/*.ts', - 'extensions/**/*.ts', - '!**/fixtures/**', - '!**/typings/**', - '!**/node_modules/**', - '!extensions/typescript-basics/test/colorize-fixtures/**', - '!extensions/vscode-api-tests/testWorkspace/**', - '!extensions/vscode-api-tests/testWorkspace2/**', - '!extensions/**/*.test.ts', - '!extensions/html-language-features/server/lib/jquery.d.ts' -]; - -const copyrightHeaderLines = [ - '/*---------------------------------------------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' * Licensed under the MIT License. See License.txt in the project root for license information.', - ' *--------------------------------------------------------------------------------------------*/' -]; +const { all, jsHygieneFilter, tsHygieneFilter, hygiene } = require('./hygiene'); gulp.task('eslint', () => { - return vfs.src(all, { base: '.', follow: true, allowEmpty: true }) + return vfs + .src(all, { base: '.', follow: true, allowEmpty: true }) .pipe(filter(jsHygieneFilter.concat(tsHygieneFilter))) - .pipe(gulpeslint({ - configFile: '.eslintrc.json', - rulePaths: ['./build/lib/eslint'] - })) + .pipe( + gulpeslint({ + configFile: '.eslintrc.json', + rulePaths: ['./build/lib/eslint'], + }) + ) .pipe(gulpeslint.formatEach('compact')) - .pipe(gulpeslint.results(results => { - if (results.warningCount > 0 || results.errorCount > 0) { - throw new Error('eslint failed with warnings and/or errors'); - } - })); + .pipe( + gulpeslint.results((results) => { + if (results.warningCount > 0 || results.errorCount > 0) { + throw new Error('eslint failed with warnings and/or errors'); + } + }) + ); }); function checkPackageJSON(actualPath) { @@ -187,233 +44,25 @@ function checkPackageJSON(actualPath) { continue; } if (depVersion !== rootDepVersion) { - this.emit('error', `The dependency ${depName} in '${actualPath}' (${depVersion}) is different than in the root package.json (${rootDepVersion})`); + this.emit( + 'error', + `The dependency ${depName} in '${actualPath}' (${depVersion}) is different than in the root package.json (${rootDepVersion})` + ); } } } const checkPackageJSONTask = task.define('check-package-json', () => { - return gulp.src('package.json') - .pipe(es.through(function () { + return gulp.src('package.json').pipe( + es.through(function () { checkPackageJSON.call(this, 'remote/package.json'); checkPackageJSON.call(this, 'remote/web/package.json'); - })); + }) + ); }); gulp.task(checkPackageJSONTask); - -function hygiene(some) { - let errorCount = 0; - - const productJson = es.through(function (file) { - const product = JSON.parse(file.contents.toString('utf8')); - - if (product.extensionsGallery) { - console.error('product.json: Contains "extensionsGallery"'); - errorCount++; - } - - this.emit('data', file); - }); - - const indentation = es.through(function (file) { - const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/); - file.__lines = lines; - - lines - .forEach((line, i) => { - if (/^\s*$/.test(line)) { - // empty or whitespace lines are OK - } else if (/^[\t]*[^\s]/.test(line)) { - // good indent - } else if (/^[\t]* \*/.test(line)) { - // block comment using an extra space - } else { - console.error(file.relative + '(' + (i + 1) + ',1): Bad whitespace indentation'); - errorCount++; - } - }); - - this.emit('data', file); - }); - - const copyrights = es.through(function (file) { - const lines = file.__lines; - - for (let i = 0; i < copyrightHeaderLines.length; i++) { - if (lines[i] !== copyrightHeaderLines[i]) { - console.error(file.relative + ': Missing or bad copyright statement'); - errorCount++; - break; - } - } - - this.emit('data', file); - }); - - const formatting = es.map(function (file, cb) { - tsfmt.processString(file.path, file.contents.toString('utf8'), { - verify: false, - tsfmt: true, - // verbose: true, - // keep checkJS happy - editorconfig: undefined, - replace: undefined, - tsconfig: undefined, - tsconfigFile: undefined, - tsfmtFile: undefined, - vscode: undefined, - vscodeFile: undefined - }).then(result => { - let original = result.src.replace(/\r\n/gm, '\n'); - let formatted = result.dest.replace(/\r\n/gm, '\n'); - - if (original !== formatted) { - console.error('File not formatted. Run the \'Format Document\' command to fix it:', file.relative); - errorCount++; - } - cb(null, file); - - }, err => { - cb(err); - }); - }); - - let input; - - if (Array.isArray(some) || typeof some === 'string' || !some) { - const options = { base: '.', follow: true, allowEmpty: true }; - if (some) { - input = vfs.src(some, options).pipe(filter(all)); // split this up to not unnecessarily filter all a second time - } else { - input = vfs.src(all, options); - } - } else { - input = some; - } - - const productJsonFilter = filter('product.json', { restore: true }); - - const result = input - .pipe(filter(f => !f.stat.isDirectory())) - .pipe(productJsonFilter) - .pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson) - .pipe(productJsonFilter.restore) - .pipe(filter(indentationFilter)) - .pipe(indentation) - .pipe(filter(copyrightFilter)) - .pipe(copyrights); - - const typescript = result - .pipe(filter(tsHygieneFilter)) - .pipe(formatting); - - const javascript = result - .pipe(filter(jsHygieneFilter.concat(tsHygieneFilter))) - .pipe(gulpeslint({ - configFile: '.eslintrc.json', - rulePaths: ['./build/lib/eslint'] - })) - .pipe(gulpeslint.formatEach('compact')) - .pipe(gulpeslint.results(results => { - errorCount += results.warningCount; - errorCount += results.errorCount; - })); - - let count = 0; - return es.merge(typescript, javascript) - .pipe(es.through(function (data) { - count++; - if (process.env['TRAVIS'] && count % 10 === 0) { - process.stdout.write('.'); - } - this.emit('data', data); - }, function () { - process.stdout.write('\n'); - if (errorCount > 0) { - this.emit('error', 'Hygiene failed with ' + errorCount + ' errors. Check \'build/gulpfile.hygiene.js\'.'); - } else { - this.emit('end'); - } - })); -} - -function createGitIndexVinyls(paths) { - const cp = require('child_process'); - const repositoryPath = process.cwd(); - - const fns = paths.map(relativePath => () => new Promise((c, e) => { - const fullPath = path.join(repositoryPath, relativePath); - - fs.stat(fullPath, (err, stat) => { - if (err && err.code === 'ENOENT') { // ignore deletions - return c(null); - } else if (err) { - return e(err); - } - - cp.exec(`git show :${relativePath}`, { maxBuffer: 2000 * 1024, encoding: 'buffer' }, (err, out) => { - if (err) { - return e(err); - } - - c(new VinylFile({ - path: fullPath, - base: repositoryPath, - contents: out, - stat - })); - }); - }); - })); - - return pall(fns, { concurrency: 4 }) - .then(r => r.filter(p => !!p)); -} - -gulp.task('hygiene', task.series(checkPackageJSONTask, () => hygiene())); - -// this allows us to run hygiene as a git pre-commit hook -if (require.main === module) { - const cp = require('child_process'); - - process.on('unhandledRejection', (reason, p) => { - console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); - process.exit(1); - }); - - if (process.argv.length > 2) { - hygiene(process.argv.slice(2)).on('error', err => { - console.error(); - console.error(err); - process.exit(1); - }); - } else { - cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => { - if (err) { - console.error(); - console.error(err); - process.exit(1); - return; - } - - const some = out - .split(/\r?\n/) - .filter(l => !!l); - - if (some.length > 0) { - console.log('Reading git index versions...'); - - createGitIndexVinyls(some) - .then(vinyls => new Promise((c, e) => hygiene(es.readArray(vinyls)) - .on('end', () => c()) - .on('error', e))) - .catch(err => { - console.error(); - console.error(err); - process.exit(1); - }); - } - }); - } -} +gulp.task( + 'hygiene', + task.series(checkPackageJSONTask, () => hygiene()) +); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index f2ea1bd3701..5f367d1f077 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -37,19 +37,11 @@ const BUILD_TARGETS = [ const noop = () => { return Promise.resolve(); }; -gulp.task('vscode-reh-win32-ia32-min', noop); -gulp.task('vscode-reh-win32-x64-min', noop); -gulp.task('vscode-reh-darwin-min', noop); -gulp.task('vscode-reh-linux-x64-min', noop); -gulp.task('vscode-reh-linux-armhf-min', noop); -gulp.task('vscode-reh-linux-arm64-min', noop); -gulp.task('vscode-reh-linux-alpine-min', noop); - -gulp.task('vscode-reh-web-win32-ia32-min', noop); -gulp.task('vscode-reh-web-win32-x64-min', noop); -gulp.task('vscode-reh-web-darwin-min', noop); -gulp.task('vscode-reh-web-linux-x64-min', noop); -gulp.task('vscode-reh-web-linux-alpine-min', noop); +BUILD_TARGETS.forEach(({ platform, arch }) => { + for (const target of ['reh', 'reh-web']) { + gulp.task(`vscode-${target}-${platform}${ arch ? `-${arch}` : '' }-min`, noop); + } +}); function getNodeVersion() { const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8'); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 4906bfdb1a2..3c8bbe5b87f 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -37,18 +37,13 @@ const { compileBuildTask } = require('./gulpfile.compile'); const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); -// @ts-ignore -const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n)); -const nodeModules = ['electron', 'original-fs'] - // @ts-ignore JSON checking: dependencies property is optional - .concat(Object.keys(product.dependencies || {})) - .concat(_.uniq(productionDependencies.map(d => d.name))) - .concat(baseModules); // Build const vscodeEntryPoints = _.flatten([ buildfile.entrypoint('vs/workbench/workbench.desktop.main'), buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, buildfile.workbenchDesktop, buildfile.code ]); @@ -60,28 +55,32 @@ const vscodeResources = [ 'out-build/bootstrap.js', 'out-build/bootstrap-fork.js', 'out-build/bootstrap-amd.js', + 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', 'out-build/paths.js', 'out-build/vs/**/*.{svg,png,html}', '!out-build/vs/code/browser/**/*.html', + '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', - 'out-build/vs/base/browser/ui/codiconLabel/codicon/**', + 'out-build/vs/base/browser/ui/codicons/codicon/**', + 'out-build/vs/base/parts/sandbox/electron-browser/preload.js', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', 'out-build/vs/workbench/contrib/webview/electron-browser/pre/*.js', + 'out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', 'out-build/vs/platform/files/**/*.exe', 'out-build/vs/platform/files/**/*.md', 'out-build/vs/code/electron-browser/workbench/**', 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', - 'out-build/vs/code/electron-browser/issue/issueReporter.js', - 'out-build/vs/code/electron-browser/processExplorer/processExplorer.js', - 'out-build/vs/platform/auth/common/auth.css', + 'out-build/vs/code/electron-sandbox/issue/issueReporter.js', + 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.js', + 'out-build/vs/code/electron-sandbox/proxy/auth.js', '!**/test/**' ]; @@ -91,9 +90,8 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series( src: 'out-build', entryPoints: vscodeEntryPoints, resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), + loaderConfig: common.loaderConfig(), out: 'out-vscode', - inlineAmdImages: true, bundleInfo: undefined }) )); @@ -103,12 +101,6 @@ const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${ const minifyVSCodeTask = task.define('minify-vscode', task.series( optimizeVSCodeTask, util.rimraf('out-vscode-min'), - () => { - const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); - const contents = fs.readFileSync(fullpath).toString(); - const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); - fs.writeFileSync(fullpath, newContents); - }, common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) )); gulp.task(minifyVSCodeTask); @@ -157,8 +149,10 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const out = sourceFolderName; const checksums = computeChecksums(out, [ + 'vs/base/parts/sandbox/electron-browser/preload.js', 'vs/workbench/workbench.desktop.main.js', 'vs/workbench/workbench.desktop.main.css', + 'vs/workbench/services/extensions/node/extensionHostProcess.js', 'vs/code/electron-browser/workbench/workbench.html', 'vs/code/electron-browser/workbench/workbench.js' ]); @@ -207,13 +201,18 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true }); + const jsFilter = util.filter(data => !data.isDirectory() &&/\.js$/.test(data.path)); const root = path.resolve(path.join(__dirname, '..')); const dependenciesSrc = _.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])); const deps = gulp.src(dependenciesSrc, { base: '.', dot: true }) - .pipe(filter(['**', '!**/package-lock.json'])) + .pipe(filter(['**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.js.map'])) .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) - .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar')); + .pipe(util.cleanNodeModules(path.join(__dirname, '.moduleignore'))) + .pipe(jsFilter) + .pipe(util.rewriteSourceMappingURL(sourceMappingURLBase)) + .pipe(jsFilter.restore) + .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*', '**/*.wasm'], 'node_modules.asar')); let all = es.merge( packageJsonStream, @@ -269,7 +268,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op let result = all .pipe(util.skipDirectories()) .pipe(util.fixWin32DirectoryPermissions()) - .pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true }))) + .pipe(electron(_.extend({}, config, { platform, arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: true }))) .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true })); if (platform === 'linux') { @@ -326,10 +325,11 @@ const buildRoot = path.dirname(root); const BUILD_TARGETS = [ { platform: 'win32', arch: 'ia32' }, { platform: 'win32', arch: 'x64' }, + { platform: 'win32', arch: 'arm64' }, { platform: 'darwin', arch: null, opts: { stats: true } }, { platform: 'linux', arch: 'ia32' }, { platform: 'linux', arch: 'x64' }, - { platform: 'linux', arch: 'arm' }, + { platform: 'linux', arch: 'armhf' }, { platform: 'linux', arch: 'arm64' }, ]; BUILD_TARGETS.forEach(buildTarget => { @@ -458,20 +458,30 @@ const generateVSCodeConfigurationTask = task.define('generate-vscode-configurati const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; const appPath = path.join(buildDir, `VSCode-darwin/${appName}/Contents/Resources/app/bin/code`); - const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`); + const codeProc = cp.exec( + `${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, + (err, stdout, stderr) => { + clearTimeout(timer); + if (err) { + console.log(`err: ${err} ${err.message} ${err.toString()}`); + reject(err); + } + if (stdout) { + console.log(`stdout: ${stdout}`); + } + + if (stderr) { + console.log(`stderr: ${stderr}`); + } + + resolve(); + } + ); const timer = setTimeout(() => { codeProc.kill(); reject(new Error('export-default-configuration process timed out')); - }, 10 * 1000); - - codeProc.stdout.on('data', d => console.log(d.toString())); - codeProc.stderr.on('data', d => console.log(d.toString())); - - codeProc.on('exit', () => { - clearTimeout(timer); - resolve(); - }); + }, 12 * 1000); codeProc.on('error', err => { clearTimeout(timer); diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 51c7002f5b1..1d8a09e4fe6 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -23,7 +23,7 @@ const commit = util.getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); function getDebPackageArch(arch) { - return { x64: 'amd64', arm: 'armhf', arm64: 'arm64' }[arch]; + return { x64: 'amd64', armhf: 'armhf', arm64: 'arm64' }[arch]; } function prepareDebPackage(arch) { @@ -43,7 +43,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@EXEC@@', `/usr/share/${product.applicationName}/${product.applicationName}`)) - .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) @@ -52,6 +52,11 @@ function prepareDebPackage(arch) { .pipe(replace('@@LICENSE@@', product.licenseName)) .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); + const workspaceMime = gulp.src('resources/linux/code-workspace.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('usr/share/mime/packages/' + product.applicationName + '-workspace.xml')); + const icon = gulp.src('resources/linux/code.png', { base: '.' }) .pipe(rename('usr/share/pixmaps/' + product.linuxIconName + '.png')); @@ -91,13 +96,11 @@ function prepareDebPackage(arch) { const postinst = gulp.src('resources/linux/debian/postinst.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@ARCHITECTURE@@', debArch)) - // @ts-ignore JSON checking: quality is optional .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) - // @ts-ignore JSON checking: updateUrl is optional .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) .pipe(rename('DEBIAN/postinst')); - const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, icon, bash_completion, zsh_completion, code); + const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); return all.pipe(vfs.dest(destination)); }; @@ -117,7 +120,7 @@ function getRpmBuildPath(rpmArch) { } function getRpmPackageArch(arch) { - return { x64: 'x86_64', arm: 'armhf', arm64: 'arm64' }[arch]; + return { x64: 'x86_64', armhf: 'armv7hl', arm64: 'aarch64' }[arch]; } function prepareRpmPackage(arch) { @@ -145,6 +148,11 @@ function prepareRpmPackage(arch) { .pipe(replace('@@LICENSE@@', product.licenseName)) .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); + const workspaceMime = gulp.src('resources/linux/code-workspace.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('BUILD/usr/share/mime/packages/' + product.applicationName + '-workspace.xml')); + const icon = gulp.src('resources/linux/code.png', { base: '.' }) .pipe(rename('BUILD/usr/share/pixmaps/' + product.linuxIconName + '.png')); @@ -167,9 +175,7 @@ function prepareRpmPackage(arch) { .pipe(replace('@@RELEASE@@', linuxPackageRevision)) .pipe(replace('@@ARCHITECTURE@@', rpmArch)) .pipe(replace('@@LICENSE@@', product.licenseName)) - // @ts-ignore JSON checking: quality is optional .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) - // @ts-ignore JSON checking: updateUrl is optional .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) .pipe(replace('@@DEPENDENCIES@@', rpmDependencies[rpmArch].join(', '))) .pipe(rename('SPECS/' + product.applicationName + '.spec')); @@ -177,7 +183,7 @@ function prepareRpmPackage(arch) { const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) .pipe(rename('SOURCES/' + product.applicationName + '.xpm')); - const all = es.merge(code, desktops, appdata, icon, bash_completion, zsh_completion, spec, specIcon); + const all = es.merge(code, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, spec, specIcon); return all.pipe(vfs.dest(getRpmBuildPath(rpmArch))); }; @@ -250,33 +256,23 @@ function buildSnapPackage(arch) { const BUILD_TARGETS = [ { arch: 'x64' }, - { arch: 'arm' }, + { arch: 'armhf' }, { arch: 'arm64' }, ]; -BUILD_TARGETS.forEach((buildTarget) => { - const arch = buildTarget.arch; +BUILD_TARGETS.forEach(({ arch }) => { + const debArch = getDebPackageArch(arch); + const prepareDebTask = task.define(`vscode-linux-${arch}-prepare-deb`, task.series(util.rimraf(`.build/linux/deb/${debArch}`), prepareDebPackage(arch))); + const buildDebTask = task.define(`vscode-linux-${arch}-build-deb`, task.series(prepareDebTask, buildDebPackage(arch))); + gulp.task(buildDebTask); - { - const debArch = getDebPackageArch(arch); - const prepareDebTask = task.define(`vscode-linux-${arch}-prepare-deb`, task.series(util.rimraf(`.build/linux/deb/${debArch}`), prepareDebPackage(arch))); - // gulp.task(prepareDebTask); - const buildDebTask = task.define(`vscode-linux-${arch}-build-deb`, task.series(prepareDebTask, buildDebPackage(arch))); - gulp.task(buildDebTask); - } + const rpmArch = getRpmPackageArch(arch); + const prepareRpmTask = task.define(`vscode-linux-${arch}-prepare-rpm`, task.series(util.rimraf(`.build/linux/rpm/${rpmArch}`), prepareRpmPackage(arch))); + const buildRpmTask = task.define(`vscode-linux-${arch}-build-rpm`, task.series(prepareRpmTask, buildRpmPackage(arch))); + gulp.task(buildRpmTask); - { - const rpmArch = getRpmPackageArch(arch); - const prepareRpmTask = task.define(`vscode-linux-${arch}-prepare-rpm`, task.series(util.rimraf(`.build/linux/rpm/${rpmArch}`), prepareRpmPackage(arch))); - // gulp.task(prepareRpmTask); - const buildRpmTask = task.define(`vscode-linux-${arch}-build-rpm`, task.series(prepareRpmTask, buildRpmPackage(arch))); - gulp.task(buildRpmTask); - } - - { - const prepareSnapTask = task.define(`vscode-linux-${arch}-prepare-snap`, task.series(util.rimraf(`.build/linux/snap/${arch}`), prepareSnapPackage(arch))); - gulp.task(prepareSnapTask); - const buildSnapTask = task.define(`vscode-linux-${arch}-build-snap`, task.series(prepareSnapTask, buildSnapPackage(arch))); - gulp.task(buildSnapTask); - } + const prepareSnapTask = task.define(`vscode-linux-${arch}-prepare-snap`, task.series(util.rimraf(`.build/linux/snap/${arch}`), prepareSnapPackage(arch))); + gulp.task(prepareSnapTask); + const buildSnapTask = task.define(`vscode-linux-${arch}-build-snap`, task.series(prepareSnapTask, buildSnapPackage(arch))); + gulp.task(buildSnapTask); }); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 497fc553c03..2027dc350cf 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -54,7 +54,13 @@ function packageInnoSetup(iss, options, cb) { cp.spawn(innoSetupPath, args, { stdio: ['ignore', 'inherit', 'inherit'] }) .on('error', cb) - .on('exit', () => cb(null)); + .on('exit', code => { + if (code === 0) { + cb(null); + } else { + cb(new Error(`InnoSetup returned exit code: ${code}`)); + } + }); } function buildWin32Setup(arch, target) { @@ -65,6 +71,7 @@ function buildWin32Setup(arch, target) { return cb => { const ia32AppId = target === 'system' ? product.win32AppId : product.win32UserAppId; const x64AppId = target === 'system' ? product.win32x64AppId : product.win32x64UserAppId; + const arm64AppId = target === 'system' ? product.win32arm64AppId : product.win32arm64UserAppId; const sourcePath = buildPath(arch); const outputPath = setupDir(arch, target); @@ -88,12 +95,12 @@ function buildWin32Setup(arch, target) { ShellNameShort: product.win32ShellNameShort, AppMutex: product.win32MutexName, Arch: arch, - AppId: arch === 'ia32' ? ia32AppId : x64AppId, - IncompatibleTargetAppId: arch === 'ia32' ? product.win32AppId : product.win32x64AppId, - IncompatibleArchAppId: arch === 'ia32' ? x64AppId : ia32AppId, + AppId: { 'ia32': ia32AppId, 'x64': x64AppId, 'arm64': arm64AppId }[arch], + IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch], + IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch], AppUserId: product.win32AppUserModelId, - ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64', - ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64', + ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch], + ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch], SourceDir: sourcePath, RepoDir: repoPath, OutputDir: outputPath, @@ -112,8 +119,10 @@ function defineWin32SetupTasks(arch, target) { defineWin32SetupTasks('ia32', 'system'); defineWin32SetupTasks('x64', 'system'); +defineWin32SetupTasks('arm64', 'system'); defineWin32SetupTasks('ia32', 'user'); defineWin32SetupTasks('x64', 'user'); +defineWin32SetupTasks('arm64', 'user'); function archiveWin32Setup(arch) { return cb => { @@ -127,6 +136,7 @@ function archiveWin32Setup(arch) { gulp.task(task.define('vscode-win32-ia32-archive', task.series(util.rimraf(zipDir('ia32')), archiveWin32Setup('ia32')))); gulp.task(task.define('vscode-win32-x64-archive', task.series(util.rimraf(zipDir('x64')), archiveWin32Setup('x64')))); +gulp.task(task.define('vscode-win32-arm64-archive', task.series(util.rimraf(zipDir('arm64')), archiveWin32Setup('arm64')))); function copyInnoUpdater(arch) { return () => { @@ -144,8 +154,10 @@ function updateIcon(executablePath) { gulp.task(task.define('vscode-win32-ia32-inno-updater', task.series(copyInnoUpdater('ia32'), updateIcon(path.join(buildPath('ia32'), 'tools', 'inno_updater.exe'))))); gulp.task(task.define('vscode-win32-x64-inno-updater', task.series(copyInnoUpdater('x64'), updateIcon(path.join(buildPath('x64'), 'tools', 'inno_updater.exe'))))); +gulp.task(task.define('vscode-win32-arm64-inno-updater', task.series(copyInnoUpdater('arm64'), updateIcon(path.join(buildPath('arm64'), 'tools', 'inno_updater.exe'))))); // CodeHelper.exe icon gulp.task(task.define('vscode-win32-ia32-code-helper', task.series(updateIcon(path.join(buildPath('ia32'), 'resources', 'app', 'out', 'vs', 'platform', 'files', 'node', 'watcher', 'win32', 'CodeHelper.exe'))))); gulp.task(task.define('vscode-win32-x64-code-helper', task.series(updateIcon(path.join(buildPath('x64'), 'resources', 'app', 'out', 'vs', 'platform', 'files', 'node', 'watcher', 'win32', 'CodeHelper.exe'))))); +gulp.task(task.define('vscode-win32-arm64-code-helper', task.series(updateIcon(path.join(buildPath('arm64'), 'resources', 'app', 'out', 'vs', 'platform', 'files', 'node', 'watcher', 'win32', 'CodeHelper.exe'))))); diff --git a/build/hygiene.js b/build/hygiene.js new file mode 100644 index 00000000000..b95b4ee9824 --- /dev/null +++ b/build/hygiene.js @@ -0,0 +1,412 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const filter = require('gulp-filter'); +const es = require('event-stream'); +const gulpeslint = require('gulp-eslint'); +const tsfmt = require('typescript-formatter'); +const VinylFile = require('vinyl'); +const vfs = require('vinyl-fs'); +const path = require('path'); +const fs = require('fs'); +const pall = require('p-all'); + +/** + * Hygiene works by creating cascading subsets of all our files and + * passing them through a sequence of checks. Here are the current subsets, + * named according to the checks performed on them. Each subset contains + * the following one, as described in mathematical notation: + * + * all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript + */ + +const all = [ + '*', + 'build/**/*', + 'extensions/**/*', + 'scripts/**/*', + 'src/**/*', + 'test/**/*', + '!test/**/out/**', + '!**/node_modules/**', +]; +module.exports.all = all; + +const indentationFilter = [ + '**', + + // except specific files + '!**/ThirdPartyNotices.txt', + '!**/LICENSE.{txt,rtf}', + '!LICENSES.chromium.html', + '!**/LICENSE', + '!src/vs/nls.js', + '!src/vs/nls.build.js', + '!src/vs/css.js', + '!src/vs/css.build.js', + '!src/vs/loader.js', + '!src/vs/base/common/insane/insane.js', + '!src/vs/base/common/marked/marked.js', + '!src/vs/base/common/semver/semver.js', + '!src/vs/base/node/terminateProcess.sh', + '!src/vs/base/node/cpuUsage.sh', + '!test/unit/assert.js', + '!resources/linux/snap/electron-launch', + + // except specific folders + '!test/automation/out/**', + '!test/smoke/out/**', + '!extensions/typescript-language-features/test-workspace/**', + '!extensions/vscode-api-tests/testWorkspace/**', + '!extensions/vscode-api-tests/testWorkspace2/**', + '!build/monaco/**', + '!build/win32/**', + + // except multiple specific files + '!**/package.json', + '!**/yarn.lock', + '!**/yarn-error.log', + + // except multiple specific folders + '!**/codicon/**', + '!**/fixtures/**', + '!**/lib/**', + '!extensions/**/out/**', + '!extensions/**/snippets/**', + '!extensions/**/syntaxes/**', + '!extensions/**/themes/**', + '!extensions/**/colorize-fixtures/**', + + // except specific file types + '!src/vs/*/**/*.d.ts', + '!src/typings/**/*.d.ts', + '!extensions/**/*.d.ts', + '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', + '!build/{lib,download,darwin}/**/*.js', + '!build/**/*.sh', + '!build/azure-pipelines/**/*.js', + '!build/azure-pipelines/**/*.config', + '!**/Dockerfile', + '!**/Dockerfile.*', + '!**/*.Dockerfile', + '!**/*.dockerfile', + '!extensions/markdown-language-features/media/*.js', +]; + +const copyrightFilter = [ + '**', + '!**/*.desktop', + '!**/*.json', + '!**/*.html', + '!**/*.template', + '!**/*.md', + '!**/*.bat', + '!**/*.cmd', + '!**/*.ico', + '!**/*.icns', + '!**/*.xml', + '!**/*.sh', + '!**/*.txt', + '!**/*.xpm', + '!**/*.opts', + '!**/*.disabled', + '!**/*.code-workspace', + '!**/*.js.map', + '!build/**/*.init', + '!resources/linux/snap/snapcraft.yaml', + '!resources/win32/bin/code.js', + '!resources/web/code-web.js', + '!resources/completions/**', + '!extensions/markdown-language-features/media/highlight.css', + '!extensions/html-language-features/server/src/modes/typescript/*', + '!extensions/*/server/bin/*', + '!src/vs/editor/test/node/classification/typescript-test.ts', +]; + +const jsHygieneFilter = [ + 'src/**/*.js', + 'build/gulpfile.*.js', + '!src/vs/loader.js', + '!src/vs/css.js', + '!src/vs/nls.js', + '!src/vs/css.build.js', + '!src/vs/nls.build.js', + '!src/**/insane.js', + '!src/**/marked.js', + '!src/**/semver.js', + '!**/test/**', +]; +module.exports.jsHygieneFilter = jsHygieneFilter; + +const tsHygieneFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + 'extensions/**/*.ts', + '!**/fixtures/**', + '!**/typings/**', + '!**/node_modules/**', + '!extensions/typescript-basics/test/colorize-fixtures/**', + '!extensions/vscode-api-tests/testWorkspace/**', + '!extensions/vscode-api-tests/testWorkspace2/**', + '!extensions/**/*.test.ts', + '!extensions/html-language-features/server/lib/jquery.d.ts', +]; +module.exports.tsHygieneFilter = tsHygieneFilter; + +const copyrightHeaderLines = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the MIT License. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', +]; + +function hygiene(some) { + let errorCount = 0; + + const productJson = es.through(function (file) { + const product = JSON.parse(file.contents.toString('utf8')); + + if (product.extensionsGallery) { + console.error(`product.json: Contains 'extensionsGallery'`); + errorCount++; + } + + this.emit('data', file); + }); + + const indentation = es.through(function (file) { + const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/); + file.__lines = lines; + + lines.forEach((line, i) => { + if (/^\s*$/.test(line)) { + // empty or whitespace lines are OK + } else if (/^[\t]*[^\s]/.test(line)) { + // good indent + } else if (/^[\t]* \*/.test(line)) { + // block comment using an extra space + } else { + console.error( + file.relative + '(' + (i + 1) + ',1): Bad whitespace indentation' + ); + errorCount++; + } + }); + + this.emit('data', file); + }); + + const copyrights = es.through(function (file) { + const lines = file.__lines; + + for (let i = 0; i < copyrightHeaderLines.length; i++) { + if (lines[i] !== copyrightHeaderLines[i]) { + console.error(file.relative + ': Missing or bad copyright statement'); + errorCount++; + break; + } + } + + this.emit('data', file); + }); + + const formatting = es.map(function (file, cb) { + tsfmt + .processString(file.path, file.contents.toString('utf8'), { + verify: false, + tsfmt: true, + // verbose: true, + // keep checkJS happy + editorconfig: undefined, + replace: undefined, + tsconfig: undefined, + tsconfigFile: undefined, + tsfmtFile: undefined, + vscode: undefined, + vscodeFile: undefined, + }) + .then( + (result) => { + let original = result.src.replace(/\r\n/gm, '\n'); + let formatted = result.dest.replace(/\r\n/gm, '\n'); + + if (original !== formatted) { + console.error( + `File not formatted. Run the 'Format Document' command to fix it:`, + file.relative + ); + errorCount++; + } + cb(null, file); + }, + (err) => { + cb(err); + } + ); + }); + + let input; + + if (Array.isArray(some) || typeof some === 'string' || !some) { + const options = { base: '.', follow: true, allowEmpty: true }; + if (some) { + input = vfs.src(some, options).pipe(filter(all)); // split this up to not unnecessarily filter all a second time + } else { + input = vfs.src(all, options); + } + } else { + input = some; + } + + const productJsonFilter = filter('product.json', { restore: true }); + + const result = input + .pipe(filter((f) => !f.stat.isDirectory())) + .pipe(productJsonFilter) + .pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson) + .pipe(productJsonFilter.restore) + .pipe(filter(indentationFilter)) + .pipe(indentation) + .pipe(filter(copyrightFilter)) + .pipe(copyrights); + + const typescript = result.pipe(filter(tsHygieneFilter)).pipe(formatting); + + const javascript = result + .pipe(filter(jsHygieneFilter.concat(tsHygieneFilter))) + .pipe( + gulpeslint({ + configFile: '.eslintrc.json', + rulePaths: ['./build/lib/eslint'], + }) + ) + .pipe(gulpeslint.formatEach('compact')) + .pipe( + gulpeslint.results((results) => { + errorCount += results.warningCount; + errorCount += results.errorCount; + }) + ); + + let count = 0; + return es.merge(typescript, javascript).pipe( + es.through( + function (data) { + count++; + if (process.env['TRAVIS'] && count % 10 === 0) { + process.stdout.write('.'); + } + this.emit('data', data); + }, + function () { + process.stdout.write('\n'); + if (errorCount > 0) { + this.emit( + 'error', + 'Hygiene failed with ' + + errorCount + + ` errors. Check 'build / gulpfile.hygiene.js'.` + ); + } else { + this.emit('end'); + } + } + ) + ); +} + +module.exports.hygiene = hygiene; + +function createGitIndexVinyls(paths) { + const cp = require('child_process'); + const repositoryPath = process.cwd(); + + const fns = paths.map((relativePath) => () => + new Promise((c, e) => { + const fullPath = path.join(repositoryPath, relativePath); + + fs.stat(fullPath, (err, stat) => { + if (err && err.code === 'ENOENT') { + // ignore deletions + return c(null); + } else if (err) { + return e(err); + } + + cp.exec( + `git show :${relativePath}`, + { maxBuffer: 2000 * 1024, encoding: 'buffer' }, + (err, out) => { + if (err) { + return e(err); + } + + c( + new VinylFile({ + path: fullPath, + base: repositoryPath, + contents: out, + stat, + }) + ); + } + ); + }); + }) + ); + + return pall(fns, { concurrency: 4 }).then((r) => r.filter((p) => !!p)); +} + +// this allows us to run hygiene as a git pre-commit hook +if (require.main === module) { + const cp = require('child_process'); + + process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); + process.exit(1); + }); + + if (process.argv.length > 2) { + hygiene(process.argv.slice(2)).on('error', (err) => { + console.error(); + console.error(err); + process.exit(1); + }); + } else { + cp.exec( + 'git diff --cached --name-only', + { maxBuffer: 2000 * 1024 }, + (err, out) => { + if (err) { + console.error(); + console.error(err); + process.exit(1); + } + + const some = out.split(/\r?\n/).filter((l) => !!l); + + if (some.length > 0) { + console.log('Reading git index versions...'); + + createGitIndexVinyls(some) + .then( + (vinyls) => + new Promise((c, e) => + hygiene(es.readArray(vinyls)) + .on('end', () => c()) + .on('error', e) + ) + ) + .catch((err) => { + console.error(); + console.error(err); + process.exit(1); + }); + } + } + ); + } +} diff --git a/build/lib/asar.js b/build/lib/asar.js deleted file mode 100644 index 21c5f65a45b..00000000000 --- a/build/lib/asar.js +++ /dev/null @@ -1,117 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const es = require("event-stream"); -const pickle = require('chromium-pickle-js'); -const Filesystem = require('asar/lib/filesystem'); -const VinylFile = require("vinyl"); -const minimatch = require("minimatch"); -function createAsar(folderPath, unpackGlobs, destFilename) { - const shouldUnpackFile = (file) => { - for (let i = 0; i < unpackGlobs.length; i++) { - if (minimatch(file.relative, unpackGlobs[i])) { - return true; - } - } - return false; - }; - const filesystem = new Filesystem(folderPath); - const out = []; - // Keep track of pending inserts - let pendingInserts = 0; - let onFileInserted = () => { pendingInserts--; }; - // Do not insert twice the same directory - const seenDir = {}; - const insertDirectoryRecursive = (dir) => { - if (seenDir[dir]) { - return; - } - let lastSlash = dir.lastIndexOf('/'); - if (lastSlash === -1) { - lastSlash = dir.lastIndexOf('\\'); - } - if (lastSlash !== -1) { - insertDirectoryRecursive(dir.substring(0, lastSlash)); - } - seenDir[dir] = true; - filesystem.insertDirectory(dir); - }; - const insertDirectoryForFile = (file) => { - let lastSlash = file.lastIndexOf('/'); - if (lastSlash === -1) { - lastSlash = file.lastIndexOf('\\'); - } - if (lastSlash !== -1) { - insertDirectoryRecursive(file.substring(0, lastSlash)); - } - }; - const insertFile = (relativePath, stat, shouldUnpack) => { - insertDirectoryForFile(relativePath); - pendingInserts++; - filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}, onFileInserted); - }; - return es.through(function (file) { - if (file.stat.isDirectory()) { - return; - } - if (!file.stat.isFile()) { - throw new Error(`unknown item in stream!`); - } - const shouldUnpack = shouldUnpackFile(file); - insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack); - if (shouldUnpack) { - // The file goes outside of xx.asar, in a folder xx.asar.unpacked - const relative = path.relative(folderPath, file.path); - this.queue(new VinylFile({ - cwd: folderPath, - base: folderPath, - path: path.join(destFilename + '.unpacked', relative), - stat: file.stat, - contents: file.contents - })); - } - else { - // The file goes inside of xx.asar - out.push(file.contents); - } - }, function () { - let finish = () => { - { - const headerPickle = pickle.createEmpty(); - headerPickle.writeString(JSON.stringify(filesystem.header)); - const headerBuf = headerPickle.toBuffer(); - const sizePickle = pickle.createEmpty(); - sizePickle.writeUInt32(headerBuf.length); - const sizeBuf = sizePickle.toBuffer(); - out.unshift(headerBuf); - out.unshift(sizeBuf); - } - const contents = Buffer.concat(out); - out.length = 0; - this.queue(new VinylFile({ - cwd: folderPath, - base: folderPath, - path: destFilename, - contents: contents - })); - this.queue(null); - }; - // Call finish() only when all file inserts have finished... - if (pendingInserts === 0) { - finish(); - } - else { - onFileInserted = () => { - pendingInserts--; - if (pendingInserts === 0) { - finish(); - } - }; - } - }); -} -exports.createAsar = createAsar; diff --git a/build/lib/asar.ts b/build/lib/asar.ts index d2823043aab..07b321fd41d 100644 --- a/build/lib/asar.ts +++ b/build/lib/asar.ts @@ -8,10 +8,17 @@ import * as path from 'path'; import * as es from 'event-stream'; const pickle = require('chromium-pickle-js'); -const Filesystem = require('asar/lib/filesystem'); +const Filesystem = require('asar/lib/filesystem'); import * as VinylFile from 'vinyl'; import * as minimatch from 'minimatch'; +declare class AsarFilesystem { + readonly header: unknown; + constructor(src: string); + insertDirectory(path: string, shouldUnpack?: boolean): unknown; + insertFile(path: string, shouldUnpack: boolean, file: { stat: { size: number; mode: number; }; }, options: {}): Promise; +} + export function createAsar(folderPath: string, unpackGlobs: string[], destFilename: string): NodeJS.ReadWriteStream { const shouldUnpackFile = (file: VinylFile): boolean => { @@ -61,7 +68,9 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena const insertFile = (relativePath: string, stat: { size: number; mode: number; }, shouldUnpack: boolean) => { insertDirectoryForFile(relativePath); pendingInserts++; - filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}, onFileInserted); + // Do not pass `onFileInserted` directly because it gets overwritten below. + // Create a closure capturing `onFileInserted`. + filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}).then(() => onFileInserted(), () => onFileInserted()); }; return es.through(function (file) { @@ -78,8 +87,7 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena // The file goes outside of xx.asar, in a folder xx.asar.unpacked const relative = path.relative(folderPath, file.path); this.queue(new VinylFile({ - cwd: folderPath, - base: folderPath, + base: '.', path: path.join(destFilename + '.unpacked', relative), stat: file.stat, contents: file.contents @@ -108,8 +116,7 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena out.length = 0; this.queue(new VinylFile({ - cwd: folderPath, - base: folderPath, + base: '.', path: destFilename, contents: contents })); diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.ts similarity index 62% rename from build/lib/builtInExtensions.js rename to build/lib/builtInExtensions.ts index 3f8dfd11132..b20913f49d1 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.ts @@ -3,36 +3,54 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import * as rimraf from 'rimraf'; +import * as es from 'event-stream'; +import * as rename from 'gulp-rename'; +import * as vfs from 'vinyl-fs'; +import * as ext from './extensions'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; +import { Stream } from 'stream'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); const mkdirp = require('mkdirp'); -const rimraf = require('rimraf'); -const es = require('event-stream'); -const rename = require('gulp-rename'); -const vfs = require('vinyl-fs'); -const ext = require('./extensions'); -const fancyLog = require('fancy-log'); -const ansiColors = require('ansi-colors'); -const root = path.dirname(path.dirname(__dirname)); -const builtInExtensions = require('../builtInExtensions.json'); -const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); -const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE']; - -function log() { - if (ENABLE_LOGGING) { - fancyLog.apply(this, arguments); +interface IExtensionDefinition { + name: string; + version: string; + repo: string; + metadata: { + id: string; + publisherId: { + publisherId: string; + publisherName: string; + displayName: string; + flags: string; + }; + publisherDisplayName: string; } } -function getExtensionPath(extension) { +const root = path.dirname(path.dirname(__dirname)); +const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const builtInExtensions = productjson.builtInExtensions; +const webBuiltInExtensions = productjson.webBuiltInExtensions; +const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); +const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE']; + +function log(...messages: string[]): void { + if (ENABLE_LOGGING) { + fancyLog(...messages); + } +} + +function getExtensionPath(extension: IExtensionDefinition): string { return path.join(root, '.build', 'builtInExtensions', extension.name); } -function isUpToDate(extension) { +function isUpToDate(extension: IExtensionDefinition): boolean { const packagePath = path.join(getExtensionPath(extension), 'package.json'); if (!fs.existsSync(packagePath)) { @@ -49,7 +67,7 @@ function isUpToDate(extension) { } } -function syncMarketplaceExtension(extension) { +function syncMarketplaceExtension(extension: IExtensionDefinition): Stream { if (isUpToDate(extension)) { log(ansiColors.blue('[marketplace]'), `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); return es.readArray([]); @@ -63,7 +81,7 @@ function syncMarketplaceExtension(extension) { .on('end', () => log(ansiColors.blue('[marketplace]'), extension.name, ansiColors.green('✔︎'))); } -function syncExtension(extension, controlState) { +function syncExtension(extension: IExtensionDefinition, controlState: 'disabled' | 'marketplace'): Stream { switch (controlState) { case 'disabled': log(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); @@ -87,7 +105,11 @@ function syncExtension(extension, controlState) { } } -function readControlFile() { +interface IControlFile { + [name: string]: 'disabled' | 'marketplace'; +} + +function readControlFile(): IControlFile { try { return JSON.parse(fs.readFileSync(controlFilePath, 'utf8')); } catch (err) { @@ -95,19 +117,19 @@ function readControlFile() { } } -function writeControlFile(control) { +function writeControlFile(control: IControlFile): void { mkdirp.sync(path.dirname(controlFilePath)); fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } -function main() { +export function getBuiltInExtensions(): Promise { log('Syncronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); - const streams = []; + const streams: Stream[] = []; - for (const extension of builtInExtensions) { + for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) { let controlState = control[extension.name] || 'marketplace'; control[extension.name] = controlState; @@ -116,14 +138,16 @@ function main() { writeControlFile(control); - es.merge(streams) - .on('error', err => { - console.error(err); - process.exit(1); - }) - .on('end', () => { - process.exit(0); - }); + return new Promise((resolve, reject) => { + es.merge(streams) + .on('error', reject) + .on('end', resolve); + }); } -main(); +if (require.main === module) { + getBuiltInExtensions().then(() => process.exit(0)).catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/lib/bundle.js b/build/lib/bundle.js deleted file mode 100644 index 881e8ff6c7f..00000000000 --- a/build/lib/bundle.js +++ /dev/null @@ -1,463 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const vm = require("vm"); -/** - * Bundle `entryPoints` given config `config`. - */ -function bundle(entryPoints, config, callback) { - const entryPointsMap = {}; - entryPoints.forEach((module) => { - entryPointsMap[module.name] = module; - }); - const allMentionedModulesMap = {}; - entryPoints.forEach((module) => { - allMentionedModulesMap[module.name] = true; - (module.include || []).forEach(function (includedModule) { - allMentionedModulesMap[includedModule] = true; - }); - (module.exclude || []).forEach(function (excludedModule) { - allMentionedModulesMap[excludedModule] = true; - }); - }); - const code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); - const r = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); - const loaderModule = { exports: {} }; - r.call({}, require, loaderModule, loaderModule.exports); - const loader = loaderModule.exports; - config.isBuild = true; - config.paths = config.paths || {}; - if (!config.paths['vs/nls']) { - config.paths['vs/nls'] = 'out-build/vs/nls.build'; - } - if (!config.paths['vs/css']) { - config.paths['vs/css'] = 'out-build/vs/css.build'; - } - loader.config(config); - loader(['require'], (localRequire) => { - const resolvePath = (path) => { - const r = localRequire.toUrl(path); - if (!/\.js/.test(r)) { - return r + '.js'; - } - return r; - }; - for (const moduleId in entryPointsMap) { - const entryPoint = entryPointsMap[moduleId]; - if (entryPoint.append) { - entryPoint.append = entryPoint.append.map(resolvePath); - } - if (entryPoint.prepend) { - entryPoint.prepend = entryPoint.prepend.map(resolvePath); - } - } - }); - loader(Object.keys(allMentionedModulesMap), () => { - const modules = loader.getBuildInfo(); - const partialResult = emitEntryPoints(modules, entryPointsMap); - const cssInlinedResources = loader('vs/css').getInlinedResources(); - callback(null, { - files: partialResult.files, - cssInlinedResources: cssInlinedResources, - bundleData: partialResult.bundleData - }); - }, (err) => callback(err, null)); -} -exports.bundle = bundle; -function emitEntryPoints(modules, entryPoints) { - const modulesMap = {}; - modules.forEach((m) => { - modulesMap[m.id] = m; - }); - const modulesGraph = {}; - modules.forEach((m) => { - modulesGraph[m.id] = m.dependencies; - }); - const sortedModules = topologicalSort(modulesGraph); - let result = []; - const usedPlugins = {}; - const bundleData = { - graph: modulesGraph, - bundles: {} - }; - Object.keys(entryPoints).forEach((moduleToBundle) => { - const info = entryPoints[moduleToBundle]; - const rootNodes = [moduleToBundle].concat(info.include || []); - const allDependencies = visit(rootNodes, modulesGraph); - const excludes = ['require', 'exports', 'module'].concat(info.exclude || []); - excludes.forEach((excludeRoot) => { - const allExcludes = visit([excludeRoot], modulesGraph); - Object.keys(allExcludes).forEach((exclude) => { - delete allDependencies[exclude]; - }); - }); - const includedModules = sortedModules.filter((module) => { - return allDependencies[module]; - }); - bundleData.bundles[moduleToBundle] = includedModules; - const res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend || [], info.append || [], info.dest); - result = result.concat(res.files); - for (const pluginName in res.usedPlugins) { - usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; - } - }); - Object.keys(usedPlugins).forEach((pluginName) => { - const plugin = usedPlugins[pluginName]; - if (typeof plugin.finishBuild === 'function') { - const write = (filename, contents) => { - result.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.finishBuild(write); - } - }); - return { - // TODO@TS 2.1.2 - files: extractStrings(removeDuplicateTSBoilerplate(result)), - bundleData: bundleData - }; -} -function extractStrings(destFiles) { - const parseDefineCall = (moduleMatch, depsMatch) => { - const module = moduleMatch.replace(/^"|"$/g, ''); - let deps = depsMatch.split(','); - deps = deps.map((dep) => { - dep = dep.trim(); - dep = dep.replace(/^"|"$/g, ''); - dep = dep.replace(/^'|'$/g, ''); - let prefix = null; - let _path = null; - const pieces = dep.split('!'); - if (pieces.length > 1) { - prefix = pieces[0] + '!'; - _path = pieces[1]; - } - else { - prefix = ''; - _path = pieces[0]; - } - if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { - const res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); - return prefix + res; - } - return prefix + _path; - }); - return { - module: module, - deps: deps - }; - }; - destFiles.forEach((destFile) => { - if (!/\.js$/.test(destFile.dest)) { - return; - } - if (/\.nls\.js$/.test(destFile.dest)) { - return; - } - // Do one pass to record the usage counts for each module id - const useCounts = {}; - destFile.sources.forEach((source) => { - const matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); - if (!matches) { - return; - } - const defineCall = parseDefineCall(matches[1], matches[2]); - useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; - defineCall.deps.forEach((dep) => { - useCounts[dep] = (useCounts[dep] || 0) + 1; - }); - }); - const sortedByUseModules = Object.keys(useCounts); - sortedByUseModules.sort((a, b) => { - return useCounts[b] - useCounts[a]; - }); - const replacementMap = {}; - sortedByUseModules.forEach((module, index) => { - replacementMap[module] = index; - }); - destFile.sources.forEach((source) => { - source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, (_, moduleMatch, depsMatch) => { - const defineCall = parseDefineCall(moduleMatch, depsMatch); - return `define(__m[${replacementMap[defineCall.module]}/*${defineCall.module}*/], __M([${defineCall.deps.map(dep => replacementMap[dep] + '/*' + dep + '*/').join(',')}])`; - }); - }); - destFile.sources.unshift({ - path: null, - contents: [ - '(function() {', - `var __m = ${JSON.stringify(sortedByUseModules)};`, - `var __M = function(deps) {`, - ` var result = [];`, - ` for (var i = 0, len = deps.length; i < len; i++) {`, - ` result[i] = __m[deps[i]];`, - ` }`, - ` return result;`, - `};` - ].join('\n') - }); - destFile.sources.push({ - path: null, - contents: '}).call(this);' - }); - }); - return destFiles; -} -function removeDuplicateTSBoilerplate(destFiles) { - // Taken from typescript compiler => emitFiles - const BOILERPLATE = [ - { start: /^var __extends/, end: /^}\)\(\);$/ }, - { start: /^var __assign/, end: /^};$/ }, - { start: /^var __decorate/, end: /^};$/ }, - { start: /^var __metadata/, end: /^};$/ }, - { start: /^var __param/, end: /^};$/ }, - { start: /^var __awaiter/, end: /^};$/ }, - { start: /^var __generator/, end: /^};$/ }, - ]; - destFiles.forEach((destFile) => { - const SEEN_BOILERPLATE = []; - destFile.sources.forEach((source) => { - const lines = source.contents.split(/\r\n|\n|\r/); - const newLines = []; - let IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (IS_REMOVING_BOILERPLATE) { - newLines.push(''); - if (END_BOILERPLATE.test(line)) { - IS_REMOVING_BOILERPLATE = false; - } - } - else { - for (let j = 0; j < BOILERPLATE.length; j++) { - const boilerplate = BOILERPLATE[j]; - if (boilerplate.start.test(line)) { - if (SEEN_BOILERPLATE[j]) { - IS_REMOVING_BOILERPLATE = true; - END_BOILERPLATE = boilerplate.end; - } - else { - SEEN_BOILERPLATE[j] = true; - } - } - } - if (IS_REMOVING_BOILERPLATE) { - newLines.push(''); - } - else { - newLines.push(line); - } - } - } - source.contents = newLines.join('\n'); - }); - }); - return destFiles; -} -function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, append, dest) { - if (!dest) { - dest = entryPoint + '.js'; - } - const mainResult = { - sources: [], - dest: dest - }, results = [mainResult]; - const usedPlugins = {}; - const getLoaderPlugin = (pluginName) => { - if (!usedPlugins[pluginName]) { - usedPlugins[pluginName] = modulesMap[pluginName].exports; - } - return usedPlugins[pluginName]; - }; - includedModules.forEach((c) => { - const bangIndex = c.indexOf('!'); - if (bangIndex >= 0) { - const pluginName = c.substr(0, bangIndex); - const plugin = getLoaderPlugin(pluginName); - mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); - return; - } - const module = modulesMap[c]; - if (module.path === 'empty:') { - return; - } - const contents = readFileAndRemoveBOM(module.path); - if (module.shim) { - mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } - else { - mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); - } - }); - Object.keys(usedPlugins).forEach((pluginName) => { - const plugin = usedPlugins[pluginName]; - if (typeof plugin.writeFile === 'function') { - const req = (() => { - throw new Error('no-no!'); - }); - req.toUrl = something => something; - const write = (filename, contents) => { - results.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.writeFile(pluginName, entryPoint, req, write, {}); - } - }); - const toIFile = (path) => { - const contents = readFileAndRemoveBOM(path); - return { - path: path, - contents: contents - }; - }; - const toPrepend = (prepend || []).map(toIFile); - const toAppend = (append || []).map(toIFile); - mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); - return { - files: results, - usedPlugins: usedPlugins - }; -} -function readFileAndRemoveBOM(path) { - const BOM_CHAR_CODE = 65279; - let contents = fs.readFileSync(path, 'utf8'); - // Remove BOM - if (contents.charCodeAt(0) === BOM_CHAR_CODE) { - contents = contents.substring(1); - } - return contents; -} -function emitPlugin(entryPoint, plugin, pluginName, moduleName) { - let result = ''; - if (typeof plugin.write === 'function') { - const write = ((what) => { - result += what; - }); - write.getEntryPoint = () => { - return entryPoint; - }; - write.asModule = (moduleId, code) => { - code = code.replace(/^define\(/, 'define("' + moduleId + '",'); - result += code; - }; - plugin.write(pluginName, moduleName, write); - } - return { - path: null, - contents: result - }; -} -function emitNamedModule(moduleId, defineCallPosition, path, contents) { - // `defineCallPosition` is the position in code: |define() - const defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); - // `parensOffset` is the position in code: define|() - const parensOffset = contents.indexOf('(', defineCallOffset); - const insertStr = '"' + moduleId + '", '; - return { - path: path, - contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) - }; -} -function emitShimmedModule(moduleId, myDeps, factory, path, contents) { - const strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); - const strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; - return { - path: path, - contents: contents + '\n;\n' + strDefine - }; -} -/** - * Convert a position (line:col) to (offset) in string `str` - */ -function positionToOffset(str, desiredLine, desiredCol) { - if (desiredLine === 1) { - return desiredCol - 1; - } - let line = 1; - let lastNewLineOffset = -1; - do { - if (desiredLine === line) { - return lastNewLineOffset + 1 + desiredCol - 1; - } - lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); - line++; - } while (lastNewLineOffset >= 0); - return -1; -} -/** - * Return a set of reachable nodes in `graph` starting from `rootNodes` - */ -function visit(rootNodes, graph) { - const result = {}; - const queue = rootNodes; - rootNodes.forEach((node) => { - result[node] = true; - }); - while (queue.length > 0) { - const el = queue.shift(); - const myEdges = graph[el] || []; - myEdges.forEach((toNode) => { - if (!result[toNode]) { - result[toNode] = true; - queue.push(toNode); - } - }); - } - return result; -} -/** - * Perform a topological sort on `graph` - */ -function topologicalSort(graph) { - const allNodes = {}, outgoingEdgeCount = {}, inverseEdges = {}; - Object.keys(graph).forEach((fromNode) => { - allNodes[fromNode] = true; - outgoingEdgeCount[fromNode] = graph[fromNode].length; - graph[fromNode].forEach((toNode) => { - allNodes[toNode] = true; - outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; - inverseEdges[toNode] = inverseEdges[toNode] || []; - inverseEdges[toNode].push(fromNode); - }); - }); - // https://en.wikipedia.org/wiki/Topological_sorting - const S = [], L = []; - Object.keys(allNodes).forEach((node) => { - if (outgoingEdgeCount[node] === 0) { - delete outgoingEdgeCount[node]; - S.push(node); - } - }); - while (S.length > 0) { - // Ensure the exact same order all the time with the same inputs - S.sort(); - const n = S.shift(); - L.push(n); - const myInverseEdges = inverseEdges[n] || []; - myInverseEdges.forEach((m) => { - outgoingEdgeCount[m]--; - if (outgoingEdgeCount[m] === 0) { - delete outgoingEdgeCount[m]; - S.push(m); - } - }); - } - if (Object.keys(outgoingEdgeCount).length > 0) { - throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); - } - return L; -} diff --git a/build/lib/compilation.js b/build/lib/compilation.js deleted file mode 100644 index 59bf1a250f6..00000000000 --- a/build/lib/compilation.js +++ /dev/null @@ -1,169 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const fs = require("fs"); -const gulp = require("gulp"); -const bom = require("gulp-bom"); -const sourcemaps = require("gulp-sourcemaps"); -const tsb = require("gulp-tsb"); -const path = require("path"); -const monacodts = require("../monaco/api"); -const nls = require("./nls"); -const reporter_1 = require("./reporter"); -const util = require("./util"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const watch = require('./watch'); -const reporter = reporter_1.createReporter(); -function getTypeScriptCompilerOptions(src) { - const rootDir = path.join(__dirname, `../../${src}`); - let options = {}; - options.verbose = false; - options.sourceMap = true; - if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry - options.sourceMap = false; - } - options.rootDir = rootDir; - options.baseUrl = rootDir; - options.sourceRoot = util.toFileUri(rootDir); - options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1; - return options; -} -function createCompile(src, build, emitError) { - const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); - const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) }); - const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err)); - function pipeline(token) { - const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path)); - const tsFilter = util.filter(data => /\.ts$/.test(data.path)); - const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path))); - const input = es.through(); - const output = input - .pipe(utf8Filter) - .pipe(bom()) // this is required to preserve BOM in test files that loose it otherwise - .pipe(utf8Filter.restore) - .pipe(tsFilter) - .pipe(util.loadSourcemaps()) - .pipe(compilation(token)) - .pipe(noDeclarationsFilter) - .pipe(build ? nls() : es.through()) - .pipe(noDeclarationsFilter.restore) - .pipe(sourcemaps.write('.', { - addComment: false, - includeContent: !!build, - sourceRoot: overrideOptions.sourceRoot - })) - .pipe(tsFilter.restore) - .pipe(reporter.end(!!emitError)); - return es.duplex(input, output); - } - pipeline.tsProjectSrc = () => { - return compilation.src({ base: src }); - }; - return pipeline; -} -function compileTask(src, out, build) { - return function () { - const compile = createCompile(src, build, true); - const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); - let generator = new MonacoGenerator(false); - if (src === 'src') { - generator.execute(); - } - return srcPipe - .pipe(generator.stream) - .pipe(compile()) - .pipe(gulp.dest(out)); - }; -} -exports.compileTask = compileTask; -function watchTask(out, build) { - return function () { - const compile = createCompile('src', build); - const src = gulp.src('src/**', { base: 'src' }); - const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); - let generator = new MonacoGenerator(true); - generator.execute(); - return watchSrc - .pipe(generator.stream) - .pipe(util.incremental(compile, src, true)) - .pipe(gulp.dest(out)); - }; -} -exports.watchTask = watchTask; -const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); -class MonacoGenerator { - constructor(isWatch) { - this._executeSoonTimer = null; - this._isWatch = isWatch; - this.stream = es.through(); - this._watchedFiles = {}; - let onWillReadFile = (moduleId, filePath) => { - if (!this._isWatch) { - return; - } - if (this._watchedFiles[filePath]) { - return; - } - this._watchedFiles[filePath] = true; - fs.watchFile(filePath, () => { - this._declarationResolver.invalidateCache(moduleId); - this._executeSoon(); - }); - }; - this._fsProvider = new class extends monacodts.FSProvider { - readFileSync(moduleId, filePath) { - onWillReadFile(moduleId, filePath); - return super.readFileSync(moduleId, filePath); - } - }; - this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); - if (this._isWatch) { - fs.watchFile(monacodts.RECIPE_PATH, () => { - this._executeSoon(); - }); - } - } - _executeSoon() { - if (this._executeSoonTimer !== null) { - clearTimeout(this._executeSoonTimer); - this._executeSoonTimer = null; - } - this._executeSoonTimer = setTimeout(() => { - this._executeSoonTimer = null; - this.execute(); - }, 20); - } - _run() { - let r = monacodts.run3(this._declarationResolver); - if (!r && !this._isWatch) { - // The build must always be able to generate the monaco.d.ts - throw new Error(`monaco.d.ts generation error - Cannot continue`); - } - return r; - } - _log(message, ...rest) { - fancyLog(ansiColors.cyan('[monaco.d.ts]'), message, ...rest); - } - execute() { - const startTime = Date.now(); - const result = this._run(); - if (!result) { - // nothing really changed - return; - } - if (result.isTheSame) { - return; - } - fs.writeFileSync(result.filePath, result.content); - fs.writeFileSync(path.join(REPO_SRC_FOLDER, 'vs/editor/common/standalone/standaloneEnums.ts'), result.enums); - this._log(`monaco.d.ts is changed - total time took ${Date.now() - startTime} ms`); - if (!this._isWatch) { - this.stream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); - } - } -} diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 578fae31a19..96e7d59ea16 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -12,12 +12,13 @@ import * as bom from 'gulp-bom'; import * as sourcemaps from 'gulp-sourcemaps'; import * as tsb from 'gulp-tsb'; import * as path from 'path'; -import * as monacodts from '../monaco/api'; +import * as monacodts from './monaco-api'; import * as nls from './nls'; import { createReporter } from './reporter'; import * as util from './util'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; +import * as os from 'os'; import ts = require('typescript'); const watch = require('./watch'); @@ -81,6 +82,11 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { + + if (os.totalmem() < 4_000_000_000) { + throw new Error('compilation requires 4GB of RAM'); + } + const compile = createCompile(src, build, true); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); let generator = new MonacoGenerator(false); diff --git a/build/lib/electron.js b/build/lib/electron.js deleted file mode 100644 index b38a1f6edc9..00000000000 --- a/build/lib/electron.js +++ /dev/null @@ -1,116 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const vfs = require("vinyl-fs"); -const filter = require("gulp-filter"); -const json = require("gulp-json-editor"); -const _ = require("underscore"); -const util = require("./util"); -const electron = require('gulp-atom-electron'); -const root = path.dirname(path.dirname(__dirname)); -const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); -const commit = util.getVersion(root); -function getElectronVersion() { - const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8'); - const target = /^target "(.*)"$/m.exec(yarnrc)[1]; - return target; -} -exports.getElectronVersion = getElectronVersion; -const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); -function darwinBundleDocumentType(extensions, icon) { - return { - name: product.nameLong + ' document', - role: 'Editor', - ostypes: ["TEXT", "utxt", "TUTX", "****"], - extensions: extensions, - iconFile: icon - }; -} -exports.config = { - version: getElectronVersion(), - productAppName: product.nameLong, - companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', - darwinIcon: 'resources/darwin/code.icns', - darwinBundleIdentifier: product.darwinBundleIdentifier, - darwinApplicationCategoryType: 'public.app-category.developer-tools', - darwinHelpBookFolder: 'VS Code HelpBook', - darwinHelpBookName: 'VS Code HelpBook', - darwinBundleDocumentTypes: [ - darwinBundleDocumentType(["bat", "cmd"], 'resources/darwin/bat.icns'), - darwinBundleDocumentType(["bowerrc"], 'resources/darwin/bower.icns'), - darwinBundleDocumentType(["c", "h"], 'resources/darwin/c.icns'), - darwinBundleDocumentType(["config", "editorconfig", "gitattributes", "gitconfig", "gitignore", "ini"], 'resources/darwin/config.icns'), - darwinBundleDocumentType(["cc", "cpp", "cxx", "hh", "hpp", "hxx"], 'resources/darwin/cpp.icns'), - darwinBundleDocumentType(["cs", "csx"], 'resources/darwin/csharp.icns'), - darwinBundleDocumentType(["css"], 'resources/darwin/css.icns'), - darwinBundleDocumentType(["go"], 'resources/darwin/go.icns'), - darwinBundleDocumentType(["asp", "aspx", "cshtml", "htm", "html", "jshtm", "jsp", "phtml", "shtml"], 'resources/darwin/html.icns'), - darwinBundleDocumentType(["jade"], 'resources/darwin/jade.icns'), - darwinBundleDocumentType(["jav", "java"], 'resources/darwin/java.icns'), - darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs", "cjs"], 'resources/darwin/javascript.icns'), - darwinBundleDocumentType(["json"], 'resources/darwin/json.icns'), - darwinBundleDocumentType(["less"], 'resources/darwin/less.icns'), - darwinBundleDocumentType(["markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn"], 'resources/darwin/markdown.icns'), - darwinBundleDocumentType(["php"], 'resources/darwin/php.icns'), - darwinBundleDocumentType(["ps1", "psd1", "psm1"], 'resources/darwin/powershell.icns'), - darwinBundleDocumentType(["py"], 'resources/darwin/python.icns'), - darwinBundleDocumentType(["gemspec", "rb"], 'resources/darwin/ruby.icns'), - darwinBundleDocumentType(["scss"], 'resources/darwin/sass.icns'), - darwinBundleDocumentType(["bash", "bash_login", "bash_logout", "bash_profile", "bashrc", "profile", "rhistory", "rprofile", "sh", "zlogin", "zlogout", "zprofile", "zsh", "zshenv", "zshrc"], 'resources/darwin/shell.icns'), - darwinBundleDocumentType(["sql"], 'resources/darwin/sql.icns'), - darwinBundleDocumentType(["ts"], 'resources/darwin/typescript.icns'), - darwinBundleDocumentType(["tsx", "jsx"], 'resources/darwin/react.icns'), - darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'), - darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'), - darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'), - darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns') - ], - darwinBundleURLTypes: [{ - role: 'Viewer', - name: product.nameLong, - urlSchemes: [product.urlProtocol] - }], - darwinForceDarkModeSupport: true, - darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined, - linuxExecutableName: product.applicationName, - winIcon: 'resources/win32/code.ico', - token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || undefined, - repo: product.electronRepository || undefined -}; -function getElectron(arch) { - return () => { - const electronOpts = _.extend({}, exports.config, { - platform: process.platform, - arch, - ffmpegChromium: true, - keepDefaultApp: true - }); - return vfs.src('package.json') - .pipe(json({ name: product.nameShort })) - .pipe(electron(electronOpts)) - .pipe(filter(['**', '!**/app/package.json'])) - .pipe(vfs.dest('.build/electron')); - }; -} -async function main(arch = process.arch) { - const version = getElectronVersion(); - const electronPath = path.join(root, '.build', 'electron'); - const versionFile = path.join(electronPath, 'version'); - const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; - if (!isUpToDate) { - await util.rimraf(electronPath)(); - await util.streamToPromise(getElectron(arch)()); - } -} -if (require.main === module) { - main(process.argv[2]).catch(err => { - console.error(err); - process.exit(1); - }); -} diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 86c7afcf312..c76fcf3f55b 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -19,26 +19,20 @@ const root = path.dirname(path.dirname(__dirname)); const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); const commit = util.getVersion(root); -export function getElectronVersion(): string { - const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8'); - const target = /^target "(.*)"$/m.exec(yarnrc)![1]; - return target; -} - const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); function darwinBundleDocumentType(extensions: string[], icon: string) { return { name: product.nameLong + ' document', role: 'Editor', - ostypes: ["TEXT", "utxt", "TUTX", "****"], + ostypes: ['TEXT', 'utxt', 'TUTX', '****'], extensions: extensions, iconFile: icon }; } export const config = { - version: getElectronVersion(), + version: util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', @@ -48,34 +42,34 @@ export const config = { darwinHelpBookFolder: 'VS Code HelpBook', darwinHelpBookName: 'VS Code HelpBook', darwinBundleDocumentTypes: [ - darwinBundleDocumentType(["bat", "cmd"], 'resources/darwin/bat.icns'), - darwinBundleDocumentType(["bowerrc"], 'resources/darwin/bower.icns'), - darwinBundleDocumentType(["c", "h"], 'resources/darwin/c.icns'), - darwinBundleDocumentType(["config", "editorconfig", "gitattributes", "gitconfig", "gitignore", "ini"], 'resources/darwin/config.icns'), - darwinBundleDocumentType(["cc", "cpp", "cxx", "hh", "hpp", "hxx"], 'resources/darwin/cpp.icns'), - darwinBundleDocumentType(["cs", "csx"], 'resources/darwin/csharp.icns'), - darwinBundleDocumentType(["css"], 'resources/darwin/css.icns'), - darwinBundleDocumentType(["go"], 'resources/darwin/go.icns'), - darwinBundleDocumentType(["asp", "aspx", "cshtml", "htm", "html", "jshtm", "jsp", "phtml", "shtml"], 'resources/darwin/html.icns'), - darwinBundleDocumentType(["jade"], 'resources/darwin/jade.icns'), - darwinBundleDocumentType(["jav", "java"], 'resources/darwin/java.icns'), - darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs", "cjs"], 'resources/darwin/javascript.icns'), - darwinBundleDocumentType(["json"], 'resources/darwin/json.icns'), - darwinBundleDocumentType(["less"], 'resources/darwin/less.icns'), - darwinBundleDocumentType(["markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn"], 'resources/darwin/markdown.icns'), - darwinBundleDocumentType(["php"], 'resources/darwin/php.icns'), - darwinBundleDocumentType(["ps1", "psd1", "psm1"], 'resources/darwin/powershell.icns'), - darwinBundleDocumentType(["py"], 'resources/darwin/python.icns'), - darwinBundleDocumentType(["gemspec", "rb"], 'resources/darwin/ruby.icns'), - darwinBundleDocumentType(["scss"], 'resources/darwin/sass.icns'), - darwinBundleDocumentType(["bash", "bash_login", "bash_logout", "bash_profile", "bashrc", "profile", "rhistory", "rprofile", "sh", "zlogin", "zlogout", "zprofile", "zsh", "zshenv", "zshrc"], 'resources/darwin/shell.icns'), - darwinBundleDocumentType(["sql"], 'resources/darwin/sql.icns'), - darwinBundleDocumentType(["ts"], 'resources/darwin/typescript.icns'), - darwinBundleDocumentType(["tsx", "jsx"], 'resources/darwin/react.icns'), - darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'), - darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'), - darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'), - darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns') + darwinBundleDocumentType(['bat', 'cmd'], 'resources/darwin/bat.icns'), + darwinBundleDocumentType(['bowerrc'], 'resources/darwin/bower.icns'), + darwinBundleDocumentType(['c', 'h'], 'resources/darwin/c.icns'), + darwinBundleDocumentType(['config', 'editorconfig', 'gitattributes', 'gitconfig', 'gitignore', 'ini'], 'resources/darwin/config.icns'), + darwinBundleDocumentType(['cc', 'cpp', 'cxx', 'c++', 'hh', 'hpp', 'hxx', 'h++'], 'resources/darwin/cpp.icns'), + darwinBundleDocumentType(['cs', 'csx'], 'resources/darwin/csharp.icns'), + darwinBundleDocumentType(['css'], 'resources/darwin/css.icns'), + darwinBundleDocumentType(['go'], 'resources/darwin/go.icns'), + darwinBundleDocumentType(['asp', 'aspx', 'cshtml', 'htm', 'html', 'jshtm', 'jsp', 'phtml', 'shtml'], 'resources/darwin/html.icns'), + darwinBundleDocumentType(['jade'], 'resources/darwin/jade.icns'), + darwinBundleDocumentType(['jav', 'java'], 'resources/darwin/java.icns'), + darwinBundleDocumentType(['js', 'jscsrc', 'jshintrc', 'mjs', 'cjs'], 'resources/darwin/javascript.icns'), + darwinBundleDocumentType(['json'], 'resources/darwin/json.icns'), + darwinBundleDocumentType(['less'], 'resources/darwin/less.icns'), + darwinBundleDocumentType(['markdown', 'md', 'mdoc', 'mdown', 'mdtext', 'mdtxt', 'mdwn', 'mkd', 'mkdn'], 'resources/darwin/markdown.icns'), + darwinBundleDocumentType(['php'], 'resources/darwin/php.icns'), + darwinBundleDocumentType(['ps1', 'psd1', 'psm1'], 'resources/darwin/powershell.icns'), + darwinBundleDocumentType(['py'], 'resources/darwin/python.icns'), + darwinBundleDocumentType(['gemspec', 'rb'], 'resources/darwin/ruby.icns'), + darwinBundleDocumentType(['scss'], 'resources/darwin/sass.icns'), + darwinBundleDocumentType(['bash', 'bash_login', 'bash_logout', 'bash_profile', 'bashrc', 'profile', 'rhistory', 'rprofile', 'sh', 'zlogin', 'zlogout', 'zprofile', 'zsh', 'zshenv', 'zshrc'], 'resources/darwin/shell.icns'), + darwinBundleDocumentType(['sql'], 'resources/darwin/sql.icns'), + darwinBundleDocumentType(['ts'], 'resources/darwin/typescript.icns'), + darwinBundleDocumentType(['tsx', 'jsx'], 'resources/darwin/react.icns'), + darwinBundleDocumentType(['vue'], 'resources/darwin/vue.icns'), + darwinBundleDocumentType(['ascx', 'csproj', 'dtd', 'wxi', 'wxl', 'wxs', 'xml', 'xaml'], 'resources/darwin/xml.icns'), + darwinBundleDocumentType(['eyaml', 'eyml', 'yaml', 'yml'], 'resources/darwin/yaml.icns'), + darwinBundleDocumentType(['clj', 'cljs', 'cljx', 'clojure', 'code-workspace', 'coffee', 'containerfile', 'ctp', 'dockerfile', 'dot', 'edn', 'fs', 'fsi', 'fsscript', 'fsx', 'handlebars', 'hbs', 'lua', 'm', 'makefile', 'ml', 'mli', 'pl', 'pl6', 'pm', 'pm6', 'pod', 'pp', 'properties', 'psgi', 'pug', 'r', 'rs', 'rt', 'svg', 'svgz', 't', 'txt', 'vb', 'xcodeproj', 'xcworkspace'], 'resources/darwin/default.icns') ], darwinBundleURLTypes: [{ role: 'Viewer', @@ -94,7 +88,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream { return () => { const electronOpts = _.extend({}, config, { platform: process.platform, - arch, + arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: true, keepDefaultApp: true }); @@ -108,7 +102,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream { } async function main(arch = process.arch): Promise { - const version = getElectronVersion(); + const version = util.getElectronVersion(); const electronPath = path.join(root, '.build', 'electron'); const versionFile = path.join(electronPath, 'version'); const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; diff --git a/build/lib/eslint/code-import-patterns.js b/build/lib/eslint/code-import-patterns.js deleted file mode 100644 index 0d508d1d00e..00000000000 --- a/build/lib/eslint/code-import-patterns.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const path_1 = require("path"); -const minimatch = require("minimatch"); -const utils_1 = require("./utils"); -module.exports = new class { - constructor() { - this.meta = { - messages: { - badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - }, - docs: { - url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - } - }; - } - create(context) { - const configs = context.options; - for (const config of configs) { - if (minimatch(context.getFilename(), config.target)) { - return utils_1.createImportRuleListener((node, value) => this._checkImport(context, config, node, value)); - } - } - return {}; - } - _checkImport(context, config, node, path) { - // resolve relative paths - if (path[0] === '.') { - path = path_1.join(context.getFilename(), path); - } - let restrictions; - if (typeof config.restrictions === 'string') { - restrictions = [config.restrictions]; - } - else { - restrictions = config.restrictions; - } - let matched = false; - for (const pattern of restrictions) { - if (minimatch(path, pattern)) { - matched = true; - break; - } - } - if (!matched) { - // None of the restrictions matched - context.report({ - loc: node.loc, - messageId: 'badImport', - data: { - restrictions: restrictions.join(' or ') - } - }); - } - } -}; diff --git a/build/lib/eslint/code-layering.js b/build/lib/eslint/code-layering.js deleted file mode 100644 index db591f789c7..00000000000 --- a/build/lib/eslint/code-layering.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const path_1 = require("path"); -const utils_1 = require("./utils"); -module.exports = new class { - constructor() { - this.meta = { - messages: { - layerbreaker: 'Bad layering. You are not allowed to access {{from}} from here, allowed layers are: [{{allowed}}]' - }, - docs: { - url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - } - }; - } - create(context) { - const fileDirname = path_1.dirname(context.getFilename()); - const parts = fileDirname.split(/\\|\//); - const ruleArgs = context.options[0]; - let config; - for (let i = parts.length - 1; i >= 0; i--) { - if (ruleArgs[parts[i]]) { - config = { - allowed: new Set(ruleArgs[parts[i]]).add(parts[i]), - disallowed: new Set() - }; - Object.keys(ruleArgs).forEach(key => { - if (!config.allowed.has(key)) { - config.disallowed.add(key); - } - }); - break; - } - } - if (!config) { - // nothing - return {}; - } - return utils_1.createImportRuleListener((node, path) => { - if (path[0] === '.') { - path = path_1.join(path_1.dirname(context.getFilename()), path); - } - const parts = path_1.dirname(path).split(/\\|\//); - for (let i = parts.length - 1; i >= 0; i--) { - const part = parts[i]; - if (config.allowed.has(part)) { - // GOOD - same layer - break; - } - if (config.disallowed.has(part)) { - // BAD - wrong layer - context.report({ - loc: node.loc, - messageId: 'layerbreaker', - data: { - from: part, - allowed: [...config.allowed.keys()].join(', ') - } - }); - break; - } - } - }); - } -}; diff --git a/build/lib/eslint/code-no-nls-in-standalone-editor.js b/build/lib/eslint/code-no-nls-in-standalone-editor.js deleted file mode 100644 index d8955507bed..00000000000 --- a/build/lib/eslint/code-no-nls-in-standalone-editor.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const path_1 = require("path"); -const utils_1 = require("./utils"); -module.exports = new class NoNlsInStandaloneEditorRule { - constructor() { - this.meta = { - messages: { - noNls: 'Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts' - } - }; - } - create(context) { - const fileName = context.getFilename(); - if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(fileName) - || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName) - || /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName) - || /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName) - || /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName)) { - return utils_1.createImportRuleListener((node, path) => { - // resolve relative paths - if (path[0] === '.') { - path = path_1.join(context.getFilename(), path); - } - if (/vs(\/|\\)nls/.test(path)) { - context.report({ - loc: node.loc, - messageId: 'noNls' - }); - } - }); - } - return {}; - } -}; diff --git a/build/lib/eslint/code-no-standalone-editor.js b/build/lib/eslint/code-no-standalone-editor.js deleted file mode 100644 index d9d6bb55b87..00000000000 --- a/build/lib/eslint/code-no-standalone-editor.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const path_1 = require("path"); -const utils_1 = require("./utils"); -module.exports = new class NoNlsInStandaloneEditorRule { - constructor() { - this.meta = { - messages: { - badImport: 'Not allowed to import standalone editor modules.' - }, - docs: { - url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - } - }; - } - create(context) { - if (/vs(\/|\\)editor/.test(context.getFilename())) { - // the vs/editor folder is allowed to use the standalone editor - return {}; - } - return utils_1.createImportRuleListener((node, path) => { - // resolve relative paths - if (path[0] === '.') { - path = path_1.join(context.getFilename(), path); - } - if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path) - || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path) - || /vs(\/|\\)editor(\/|\\)editor.api/.test(path) - || /vs(\/|\\)editor(\/|\\)editor.main/.test(path) - || /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)) { - context.report({ - loc: node.loc, - messageId: 'badImport' - }); - } - }); - } -}; diff --git a/build/lib/eslint/code-no-unexternalized-strings.js b/build/lib/eslint/code-no-unexternalized-strings.js deleted file mode 100644 index 28fce5b9a18..00000000000 --- a/build/lib/eslint/code-no-unexternalized-strings.js +++ /dev/null @@ -1,111 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -var _a; -const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); -function isStringLiteral(node) { - return !!node && node.type === experimental_utils_1.AST_NODE_TYPES.Literal && typeof node.value === 'string'; -} -function isDoubleQuoted(node) { - return node.raw[0] === '"' && node.raw[node.raw.length - 1] === '"'; -} -module.exports = new (_a = class NoUnexternalizedStrings { - constructor() { - this.meta = { - messages: { - doubleQuoted: 'Only use double-quoted strings for externalized strings.', - badKey: 'The key \'{{key}}\' doesn\'t conform to a valid localize identifier.', - duplicateKey: 'Duplicate key \'{{key}}\' with different message value.', - badMessage: 'Message argument to \'{{message}}\' must be a string literal.' - } - }; - } - create(context) { - const externalizedStringLiterals = new Map(); - const doubleQuotedStringLiterals = new Set(); - function collectDoubleQuotedStrings(node) { - if (isStringLiteral(node) && isDoubleQuoted(node)) { - doubleQuotedStringLiterals.add(node); - } - } - function visitLocalizeCall(node) { - // localize(key, message) - const [keyNode, messageNode] = node.arguments; - // (1) - // extract key so that it can be checked later - let key; - if (isStringLiteral(keyNode)) { - doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider - key = keyNode.value; - } - else if (keyNode.type === experimental_utils_1.AST_NODE_TYPES.ObjectExpression) { - for (let property of keyNode.properties) { - if (property.type === experimental_utils_1.AST_NODE_TYPES.Property && !property.computed) { - if (property.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier && property.key.name === 'key') { - if (isStringLiteral(property.value)) { - doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider - key = property.value.value; - break; - } - } - } - } - } - if (typeof key === 'string') { - let array = externalizedStringLiterals.get(key); - if (!array) { - array = []; - externalizedStringLiterals.set(key, array); - } - array.push({ call: node, message: messageNode }); - } - // (2) - // remove message-argument from doubleQuoted list and make - // sure it is a string-literal - doubleQuotedStringLiterals.delete(messageNode); - if (!isStringLiteral(messageNode)) { - context.report({ - loc: messageNode.loc, - messageId: 'badMessage', - data: { message: context.getSourceCode().getText(node) } - }); - } - } - function reportBadStringsAndBadKeys() { - // (1) - // report all strings that are in double quotes - for (const node of doubleQuotedStringLiterals) { - context.report({ loc: node.loc, messageId: 'doubleQuoted' }); - } - for (const [key, values] of externalizedStringLiterals) { - // (2) - // report all invalid NLS keys - if (!key.match(NoUnexternalizedStrings._rNlsKeys)) { - for (let value of values) { - context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } }); - } - } - // (2) - // report all invalid duplicates (same key, different message) - if (values.length > 1) { - for (let i = 1; i < values.length; i++) { - if (context.getSourceCode().getText(values[i - 1].message) !== context.getSourceCode().getText(values[i].message)) { - context.report({ loc: values[i].call.loc, messageId: 'duplicateKey', data: { key } }); - } - } - } - } - } - return { - ['Literal']: (node) => collectDoubleQuotedStrings(node), - ['ExpressionStatement[directive] Literal:exit']: (node) => doubleQuotedStringLiterals.delete(node), - ['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node) => visitLocalizeCall(node), - ['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node) => visitLocalizeCall(node), - ['Program:exit']: reportBadStringsAndBadKeys, - }; - } - }, - _a._rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/, - _a); diff --git a/build/lib/eslint/code-no-unexternalized-strings.ts b/build/lib/eslint/code-no-unexternalized-strings.ts index 29db884cd9f..8d9e142bb7f 100644 --- a/build/lib/eslint/code-no-unexternalized-strings.ts +++ b/build/lib/eslint/code-no-unexternalized-strings.ts @@ -47,7 +47,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { // extract key so that it can be checked later let key: string | undefined; if (isStringLiteral(keyNode)) { - doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider + doubleQuotedStringLiterals.delete(keyNode); key = keyNode.value; } else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) { @@ -55,7 +55,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { if (property.type === AST_NODE_TYPES.Property && !property.computed) { if (property.key.type === AST_NODE_TYPES.Identifier && property.key.name === 'key') { if (isStringLiteral(property.value)) { - doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider + doubleQuotedStringLiterals.delete(property.value); key = property.value.value; break; } @@ -123,4 +123,3 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { }; } }; - diff --git a/build/lib/eslint/code-no-unused-expressions.js b/build/lib/eslint/code-no-unused-expressions.ts similarity index 66% rename from build/lib/eslint/code-no-unused-expressions.js rename to build/lib/eslint/code-no-unused-expressions.ts index c7b17551311..d130d670da5 100644 --- a/build/lib/eslint/code-no-unused-expressions.js +++ b/build/lib/eslint/code-no-unused-expressions.ts @@ -13,6 +13,10 @@ 'use strict'; +import * as eslint from 'eslint'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; +import * as ESTree from 'estree'; + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -50,29 +54,29 @@ module.exports = { ] }, - create(context) { + create(context: eslint.Rule.RuleContext) { const config = context.options[0] || {}, allowShortCircuit = config.allowShortCircuit || false, allowTernary = config.allowTernary || false, allowTaggedTemplates = config.allowTaggedTemplates || false; // eslint-disable-next-line jsdoc/require-description - /** - * @param {ASTNode} node any node - * @returns {boolean} whether the given node structurally represents a directive - */ - function looksLikeDirective(node) { + /** + * @param node any node + * @returns whether the given node structurally represents a directive + */ + function looksLikeDirective(node: TSESTree.Node): boolean { return node.type === 'ExpressionStatement' && node.expression.type === 'Literal' && typeof node.expression.value === 'string'; } // eslint-disable-next-line jsdoc/require-description - /** - * @param {Function} predicate ([a] -> Boolean) the function used to make the determination - * @param {a[]} list the input list - * @returns {a[]} the leading sequence of members in the given list that pass the given predicate - */ - function takeWhile(predicate, list) { + /** + * @param predicate ([a] -> Boolean) the function used to make the determination + * @param list the input list + * @returns the leading sequence of members in the given list that pass the given predicate + */ + function takeWhile(predicate: (item: T) => boolean, list: T[]): T[] { for (let i = 0; i < list.length; ++i) { if (!predicate(list[i])) { return list.slice(0, i); @@ -82,21 +86,21 @@ module.exports = { } // eslint-disable-next-line jsdoc/require-description - /** - * @param {ASTNode} node a Program or BlockStatement node - * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body - */ - function directives(node) { + /** + * @param node a Program or BlockStatement node + * @returns the leading sequence of directive nodes in the given node's body + */ + function directives(node: TSESTree.Program | TSESTree.BlockStatement): TSESTree.Node[] { return takeWhile(looksLikeDirective, node.body); } // eslint-disable-next-line jsdoc/require-description - /** - * @param {ASTNode} node any node - * @param {ASTNode[]} ancestors the given node's ancestors - * @returns {boolean} whether the given node is considered a directive in its current position - */ - function isDirective(node, ancestors) { + /** + * @param node any node + * @param ancestors the given node's ancestors + * @returns whether the given node is considered a directive in its current position + */ + function isDirective(node: TSESTree.Node, ancestors: TSESTree.Node[]): boolean { const parent = ancestors[ancestors.length - 1], grandparent = ancestors[ancestors.length - 2]; @@ -105,12 +109,12 @@ module.exports = { directives(parent).indexOf(node) >= 0; } - /** - * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags. - * @param {ASTNode} node any node - * @returns {boolean} whether the given node is a valid expression - */ - function isValidExpression(node) { + /** + * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags. + * @param node any node + * @returns whether the given node is a valid expression + */ + function isValidExpression(node: TSESTree.Node): boolean { if (allowTernary) { // Recursive check for ternary and logical expressions @@ -134,9 +138,9 @@ module.exports = { } return { - ExpressionStatement(node) { - if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { - context.report({ node, message: 'Expected an assignment or function call and instead saw an expression.' }); + ExpressionStatement(node: TSESTree.ExpressionStatement) { + if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { + context.report({ node: node, message: 'Expected an assignment or function call and instead saw an expression.' }); } } }; diff --git a/build/lib/eslint/code-translation-remind.js b/build/lib/eslint/code-translation-remind.js deleted file mode 100644 index a276e7c0028..00000000000 --- a/build/lib/eslint/code-translation-remind.js +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -var _a; -const fs_1 = require("fs"); -const utils_1 = require("./utils"); -module.exports = new (_a = class TranslationRemind { - constructor() { - this.meta = { - messages: { - missing: 'Please add \'{{resource}}\' to ./build/lib/i18n.resources.json file to use translations here.' - } - }; - } - create(context) { - return utils_1.createImportRuleListener((node, path) => this._checkImport(context, node, path)); - } - _checkImport(context, node, path) { - if (path !== TranslationRemind.NLS_MODULE) { - return; - } - const currentFile = context.getFilename(); - const matchService = currentFile.match(/vs\/workbench\/services\/\w+/); - const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/); - if (!matchService && !matchPart) { - return; - } - const resource = matchService ? matchService[0] : matchPart[0]; - let resourceDefined = false; - let json; - try { - json = fs_1.readFileSync('./build/lib/i18n.resources.json', 'utf8'); - } - catch (e) { - console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.'); - return; - } - const workbenchResources = JSON.parse(json).workbench; - workbenchResources.forEach((existingResource) => { - if (existingResource.name === resource) { - resourceDefined = true; - return; - } - }); - if (!resourceDefined) { - context.report({ - loc: node.loc, - messageId: 'missing', - data: { resource } - }); - } - } - }, - _a.NLS_MODULE = 'vs/nls', - _a); diff --git a/build/lib/eslint/utils.js b/build/lib/eslint/utils.js deleted file mode 100644 index ec59aef3b7d..00000000000 --- a/build/lib/eslint/utils.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -function createImportRuleListener(validateImport) { - function _checkImport(node) { - if (node && node.type === 'Literal' && typeof node.value === 'string') { - validateImport(node, node.value); - } - } - return { - // import ??? from 'module' - ImportDeclaration: (node) => { - _checkImport(node.source); - }, - // import('module').then(...) OR await import('module') - ['CallExpression[callee.type="Import"][arguments.length=1] > Literal']: (node) => { - _checkImport(node); - }, - // import foo = ... - ['TSImportEqualsDeclaration > TSExternalModuleReference > Literal']: (node) => { - _checkImport(node); - }, - // export ?? from 'module' - ExportAllDeclaration: (node) => { - _checkImport(node.source); - }, - // export {foo} from 'module' - ExportNamedDeclaration: (node) => { - _checkImport(node.source); - }, - }; -} -exports.createImportRuleListener = createImportRuleListener; diff --git a/build/lib/eslint/vscode-dts-create-func.ts b/build/lib/eslint/vscode-dts-create-func.ts new file mode 100644 index 00000000000..295d099da7c --- /dev/null +++ b/build/lib/eslint/vscode-dts-create-func.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; + +export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#creating-objects' }, + messages: { sync: '`createXYZ`-functions are constructor-replacements and therefore must return sync', } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + return { + ['TSDeclareFunction Identifier[name=/create.*/]']: (node: any) => { + + const decl = (node).parent; + + if (decl.returnType?.typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) { + return; + } + if (decl.returnType.typeAnnotation.typeName.type !== AST_NODE_TYPES.Identifier) { + return; + } + + const ident = decl.returnType.typeAnnotation.typeName.name; + if (ident === 'Promise' || ident === 'Thenable') { + context.report({ + node, + messageId: 'sync' + }); + } + } + }; + } +}; diff --git a/build/lib/eslint/vscode-dts-event-naming.ts b/build/lib/eslint/vscode-dts-event-naming.ts new file mode 100644 index 00000000000..5ed8818fe44 --- /dev/null +++ b/build/lib/eslint/vscode-dts-event-naming.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; + +export = new class ApiEventNaming implements eslint.Rule.RuleModule { + + private static _nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/; + + readonly meta: eslint.Rule.RuleMetaData = { + docs: { + url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#event-naming' + }, + messages: { + naming: 'Event names must follow this patten: `on[Did|Will]`', + verb: 'Unknown verb \'{{verb}}\' - is this really a verb? Iff so, then add this verb to the configuration', + subject: 'Unknown subject \'{{subject}}\' - This subject has not been used before but it should refer to something in the API', + unknown: 'UNKNOWN event declaration, lint-rule needs tweaking' + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + const config = <{ allowed: string[], verbs: string[] }>context.options[0]; + const allowed = new Set(config.allowed); + const verbs = new Set(config.verbs); + + return { + ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node: any) => { + + const def = (node).parent?.parent?.parent; + const ident = this.getIdent(def); + + if (!ident) { + // event on unknown structure... + return context.report({ + node, + message: 'unknown' + }); + } + + if (allowed.has(ident.name)) { + // configured exception + return; + } + + const match = ApiEventNaming._nameRegExp.exec(ident.name); + if (!match) { + context.report({ + node: ident, + messageId: 'naming' + }); + return; + } + + // check that is spelled out (configured) as verb + if (!verbs.has(match[2].toLowerCase())) { + context.report({ + node: ident, + messageId: 'verb', + data: { verb: match[2] } + }); + } + + // check that a subject (if present) has occurred + if (match[3]) { + const regex = new RegExp(match[3], 'ig'); + const parts = context.getSourceCode().getText().split(regex); + if (parts.length < 3) { + context.report({ + node: ident, + messageId: 'subject', + data: { subject: match[3] } + }); + } + } + } + }; + } + + private getIdent(def: TSESTree.Node | undefined): TSESTree.Identifier | undefined { + if (!def) { + return; + } + + if (def.type === AST_NODE_TYPES.Identifier) { + return def; + } else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) { + return def.key; + } + + return this.getIdent(def.parent); + } +}; + diff --git a/build/lib/eslint/vscode-dts-interface-naming.ts b/build/lib/eslint/vscode-dts-interface-naming.ts new file mode 100644 index 00000000000..d9ec4e8c34c --- /dev/null +++ b/build/lib/eslint/vscode-dts-interface-naming.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; + +export = new class ApiInterfaceNaming implements eslint.Rule.RuleModule { + + private static _nameRegExp = /I[A-Z]/; + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + naming: 'Interfaces must not be prefixed with uppercase `I`', + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + return { + ['TSInterfaceDeclaration Identifier']: (node: any) => { + + const name = (node).name; + if (ApiInterfaceNaming._nameRegExp.test(name)) { + context.report({ + node, + messageId: 'naming' + }); + } + } + }; + } +}; + diff --git a/build/lib/eslint/vscode-dts-literal-or-types.ts b/build/lib/eslint/vscode-dts-literal-or-types.ts new file mode 100644 index 00000000000..fe4befd84e7 --- /dev/null +++ b/build/lib/eslint/vscode-dts-literal-or-types.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; + +export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#enums' }, + messages: { useEnum: 'Use enums, not literal-or-types', } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + return { + ['TSTypeAnnotation TSUnionType TSLiteralType']: (node: any) => { + if (node.literal?.type === 'TSNullKeyword') { + return; + } + context.report({ + node: node, + messageId: 'useEnum' + }); + } + }; + } +}; diff --git a/build/lib/extensions.js b/build/lib/extensions.js deleted file mode 100644 index c4385655eb9..00000000000 --- a/build/lib/extensions.js +++ /dev/null @@ -1,215 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const fs = require("fs"); -const glob = require("glob"); -const gulp = require("gulp"); -const path = require("path"); -const File = require("vinyl"); -const vsce = require("vsce"); -const stats_1 = require("./stats"); -const util2 = require("./util"); -const remote = require("gulp-remote-retry-src"); -const vzip = require('gulp-vinyl-zip'); -const filter = require("gulp-filter"); -const rename = require("gulp-rename"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const buffer = require('gulp-buffer'); -const json = require("gulp-json-editor"); -const webpack = require('webpack'); -const webpackGulp = require('webpack-stream'); -const util = require('./util'); -const root = path.dirname(path.dirname(__dirname)); -const commit = util.getVersion(root); -const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; -function fromLocal(extensionPath) { - const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); - const input = fs.existsSync(webpackFilename) - ? fromLocalWebpack(extensionPath) - : fromLocalNormal(extensionPath); - const tmLanguageJsonFilter = filter('**/*.tmLanguage.json', { restore: true }); - return input - .pipe(tmLanguageJsonFilter) - .pipe(buffer()) - .pipe(es.mapSync((f) => { - f.contents = Buffer.from(JSON.stringify(JSON.parse(f.contents.toString('utf8')))); - return f; - })) - .pipe(tmLanguageJsonFilter.restore); -} -function fromLocalWebpack(extensionPath) { - const result = es.through(); - const packagedDependencies = []; - const packageJsonConfig = require(path.join(extensionPath, 'package.json')); - if (packageJsonConfig.dependencies) { - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); - for (const key in webpackRootConfig.externals) { - if (key in packageJsonConfig.dependencies) { - packagedDependencies.push(key); - } - } - } - vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => { - const files = fileNames - .map(fileName => path.join(extensionPath, fileName)) - .map(filePath => new File({ - path: filePath, - stat: fs.statSync(filePath), - base: extensionPath, - contents: fs.createReadStream(filePath) - })); - const filesStream = es.readArray(files); - // check for a webpack configuration files, then invoke webpack - // and merge its output with the files stream. also rewrite the package.json - // file to a new entry point - const webpackConfigLocations = glob.sync(path.join(extensionPath, '/**/extension.webpack.config.js'), { ignore: ['**/node_modules'] }); - const packageJsonFilter = filter(f => { - if (path.basename(f.path) === 'package.json') { - // only modify package.json's next to the webpack file. - // to be safe, use existsSync instead of path comparison. - return fs.existsSync(path.join(path.dirname(f.path), 'extension.webpack.config.js')); - } - return false; - }, { restore: true }); - const patchFilesStream = filesStream - .pipe(packageJsonFilter) - .pipe(buffer()) - .pipe(json((data) => { - if (data.main) { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); - } - return data; - })) - .pipe(packageJsonFilter.restore); - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { - const webpackDone = (err, stats) => { - fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); - if (err) { - result.emit('error', err); - } - const { compilation } = stats; - if (compilation.errors.length > 0) { - result.emit('error', compilation.errors.join('\n')); - } - if (compilation.warnings.length > 0) { - result.emit('error', compilation.warnings.join('\n')); - } - }; - const webpackConfig = Object.assign(Object.assign({}, require(webpackConfigPath)), { mode: 'production' }); - const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path); - return webpackGulp(webpackConfig, webpack, webpackDone) - .pipe(es.through(function (data) { - data.stat = data.stat || {}; - data.base = extensionPath; - this.emit('data', data); - })) - .pipe(es.through(function (data) { - // source map handling: - // * rewrite sourceMappingURL - // * save to disk so that upload-task picks this up - const contents = data.contents.toString('utf8'); - data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { - return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`; - }), 'utf8'); - this.emit('data', data); - })); - }); - es.merge(...webpackStreams, patchFilesStream) - // .pipe(es.through(function (data) { - // // debug - // console.log('out', data.path, data.contents.length); - // this.emit('data', data); - // })) - .pipe(result); - }).catch(err => { - console.error(extensionPath); - console.error(packagedDependencies); - result.emit('error', err); - }); - return result.pipe(stats_1.createStatsStream(path.basename(extensionPath))); -} -function fromLocalNormal(extensionPath) { - const result = es.through(); - vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn }) - .then(fileNames => { - const files = fileNames - .map(fileName => path.join(extensionPath, fileName)) - .map(filePath => new File({ - path: filePath, - stat: fs.statSync(filePath), - base: extensionPath, - contents: fs.createReadStream(filePath) - })); - es.readArray(files).pipe(result); - }) - .catch(err => result.emit('error', err)); - return result.pipe(stats_1.createStatsStream(path.basename(extensionPath))); -} -const baseHeaders = { - 'X-Market-Client-Id': 'VSCode Build', - 'User-Agent': 'VSCode Build', - 'X-Market-User-Id': '291C1CD0-051A-4123-9B4B-30D60EF52EE2', -}; -function fromMarketplace(extensionName, version, metadata) { - const [publisher, name] = extensionName.split('.'); - const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`; - fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); - const options = { - base: url, - requestOptions: { - gzip: true, - headers: baseHeaders - } - }; - const packageJsonFilter = filter('package.json', { restore: true }); - return remote('', options) - .pipe(vzip.src()) - .pipe(filter('extension/**')) - .pipe(rename(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) - .pipe(packageJsonFilter) - .pipe(buffer()) - .pipe(json({ __metadata: metadata })) - .pipe(packageJsonFilter.restore); -} -exports.fromMarketplace = fromMarketplace; -const excludedExtensions = [ - 'vscode-api-tests', - 'vscode-colorize-tests', - 'vscode-test-resolver', - 'ms-vscode.node-debug', - 'ms-vscode.node-debug2', -]; -const builtInExtensions = require('../builtInExtensions.json'); -function packageLocalExtensionsStream() { - const localExtensionDescriptions = glob.sync('extensions/*/package.json') - .map(manifestPath => { - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath }; - }) - .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); - const nodeModules = gulp.src('extensions/node_modules/**', { base: '.' }); - const localExtensions = localExtensionDescriptions.map(extension => { - return fromLocal(extension.path) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - }); - return es.merge(nodeModules, ...localExtensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); -} -exports.packageLocalExtensionsStream = packageLocalExtensionsStream; -function packageMarketplaceExtensionsStream() { - const extensions = builtInExtensions.map(extension => { - return fromMarketplace(extension.name, extension.version, extension.metadata) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - }); - return es.merge(extensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); -} -exports.packageMarketplaceExtensionsStream = packageMarketplaceExtensionsStream; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 05bc7094846..dac71c81479 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -21,6 +21,7 @@ import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; const buffer = require('gulp-buffer'); import json = require('gulp-json-editor'); +import * as jsoncParser from 'jsonc-parser'; const webpack = require('webpack'); const webpackGulp = require('webpack-stream'); const util = require('./util'); @@ -28,31 +29,67 @@ const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; -function fromLocal(extensionPath: string): Stream { - const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); - const input = fs.existsSync(webpackFilename) - ? fromLocalWebpack(extensionPath) - : fromLocalNormal(extensionPath); - - const tmLanguageJsonFilter = filter('**/*.tmLanguage.json', { restore: true }); - +function minifyExtensionResources(input: Stream): Stream { + const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); return input - .pipe(tmLanguageJsonFilter) + .pipe(jsonFilter) .pipe(buffer()) .pipe(es.mapSync((f: File) => { - f.contents = Buffer.from(JSON.stringify(JSON.parse(f.contents.toString('utf8')))); + const errors: jsoncParser.ParseError[] = []; + const value = jsoncParser.parse(f.contents.toString('utf8'), errors); + if (errors.length === 0) { + // file parsed OK => just stringify to drop whitespace and comments + f.contents = Buffer.from(JSON.stringify(value)); + } return f; })) - .pipe(tmLanguageJsonFilter.restore); + .pipe(jsonFilter.restore); } -function fromLocalWebpack(extensionPath: string): Stream { +function updateExtensionPackageJSON(input: Stream, update: (data: any) => any): Stream { + const packageJsonFilter = filter('extensions/*/package.json', { restore: true }); + return input + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(es.mapSync((f: File) => { + const data = JSON.parse(f.contents.toString('utf8')); + f.contents = Buffer.from(JSON.stringify(update(data))); + return f; + })) + .pipe(packageJsonFilter.restore); +} + +function fromLocal(extensionPath: string, forWeb: boolean): Stream { + const webpackConfigFileName = forWeb ? 'extension-browser.webpack.config.js' : 'extension.webpack.config.js'; + + const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName)); + let input = isWebPacked + ? fromLocalWebpack(extensionPath, webpackConfigFileName) + : fromLocalNormal(extensionPath); + + if (isWebPacked) { + input = updateExtensionPackageJSON(input, (data: any) => { + delete data.scripts; + delete data.dependencies; + delete data.devDependencies; + if (data.main) { + data.main = data.main.replace('/out/', /dist/); + } + return data; + }); + } + + return input; +} + + +function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string): Stream { const result = es.through(); const packagedDependencies: string[] = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); if (packageJsonConfig.dependencies) { - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); + const webpackRootConfig = require(path.join(extensionPath, webpackConfigFileName)); for (const key in webpackRootConfig.externals) { if (key in packageJsonConfig.dependencies) { packagedDependencies.push(key); @@ -70,38 +107,13 @@ function fromLocalWebpack(extensionPath: string): Stream { contents: fs.createReadStream(filePath) as any })); - const filesStream = es.readArray(files); - // check for a webpack configuration files, then invoke webpack - // and merge its output with the files stream. also rewrite the package.json - // file to a new entry point + // and merge its output with the files stream. const webpackConfigLocations = (glob.sync( - path.join(extensionPath, '/**/extension.webpack.config.js'), + path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] } )); - const packageJsonFilter = filter(f => { - if (path.basename(f.path) === 'package.json') { - // only modify package.json's next to the webpack file. - // to be safe, use existsSync instead of path comparison. - return fs.existsSync(path.join(path.dirname(f.path), 'extension.webpack.config.js')); - } - return false; - }, { restore: true }); - - const patchFilesStream = filesStream - .pipe(packageJsonFilter) - .pipe(buffer()) - .pipe(json((data: any) => { - if (data.main) { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); - } - return data; - })) - .pipe(packageJsonFilter.restore); - - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { const webpackDone = (err: any, stats: any) => { @@ -143,7 +155,7 @@ function fromLocalWebpack(extensionPath: string): Stream { })); }); - es.merge(...webpackStreams, patchFilesStream) + es.merge(...webpackStreams, es.readArray(files)) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -212,13 +224,18 @@ export function fromMarketplace(extensionName: string, version: string, metadata .pipe(json({ __metadata: metadata })) .pipe(packageJsonFilter.restore); } - const excludedExtensions = [ 'vscode-api-tests', 'vscode-colorize-tests', 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', + 'vscode-notebook-tests', + 'vscode-custom-editor-tests', +]; + +const marketplaceWebExtensions = [ + 'ms-vscode.references-view' ]; interface IBuiltInExtension { @@ -228,34 +245,153 @@ interface IBuiltInExtension { metadata: any; } -const builtInExtensions: IBuiltInExtension[] = require('../builtInExtensions.json'); +const productJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const builtInExtensions: IBuiltInExtension[] = productJson.builtInExtensions || []; +const webBuiltInExtensions: IBuiltInExtension[] = productJson.webBuiltInExtensions || []; -export function packageLocalExtensionsStream(): NodeJS.ReadWriteStream { - const localExtensionDescriptions = (glob.sync('extensions/*/package.json')) - .map(manifestPath => { - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath }; - }) - .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); - - const nodeModules = gulp.src('extensions/node_modules/**', { base: '.' }); - const localExtensions = localExtensionDescriptions.map(extension => { - return fromLocal(extension.path) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - }); - - return es.merge(nodeModules, ...localExtensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); +type ExtensionKind = 'ui' | 'workspace' | 'web'; +interface IExtensionManifest { + main: string; + browser: string; + extensionKind?: ExtensionKind | ExtensionKind[]; +} +/** + * Loosely based on `getExtensionKind` from `src/vs/workbench/services/extensions/common/extensionsUtil.ts` + */ +function isWebExtension(manifest: IExtensionManifest): boolean { + if (typeof manifest.extensionKind !== 'undefined') { + const extensionKind = Array.isArray(manifest.extensionKind) ? manifest.extensionKind : [manifest.extensionKind]; + return (extensionKind.indexOf('web') >= 0); + } + return (!Boolean(manifest.main) || Boolean(manifest.browser)); } -export function packageMarketplaceExtensionsStream(): NodeJS.ReadWriteStream { - const extensions = builtInExtensions.map(extension => { - return fromMarketplace(extension.name, extension.version, extension.metadata) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - }); +export function packageLocalExtensionsStream(forWeb: boolean): Stream { + const localExtensionsDescriptions = ( + (glob.sync('extensions/*/package.json')) + .map(manifestPath => { + const absoluteManifestPath = path.join(root, manifestPath); + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; + }) + .filter(({ name }) => excludedExtensions.indexOf(name) === -1) + .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) + .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true)) + ); + const localExtensionsStream = minifyExtensionResources( + es.merge( + ...localExtensionsDescriptions.map(extension => { + return fromLocal(extension.path, forWeb) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + }) + ) + ); - return es.merge(extensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); + let result: Stream; + if (forWeb) { + result = localExtensionsStream; + } else { + // also include shared node modules + result = es.merge(localExtensionsStream, gulp.src('extensions/node_modules/**', { base: '.' })); + } + + return ( + result + .pipe(util2.setExecutableBit(['**/*.sh'])) + ); +} + +export function packageMarketplaceExtensionsStream(forWeb: boolean): Stream { + const marketplaceExtensionsDescriptions = [ + ...builtInExtensions.filter(({ name }) => (forWeb ? marketplaceWebExtensions.indexOf(name) >= 0 : true)), + ...(forWeb ? webBuiltInExtensions : []) + ]; + const marketplaceExtensionsStream = minifyExtensionResources( + es.merge( + ...marketplaceExtensionsDescriptions + .map(extension => { + const input = fromMarketplace(extension.name, extension.version, extension.metadata) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + return updateExtensionPackageJSON(input, (data: any) => { + delete data.scripts; + delete data.dependencies; + delete data.devDependencies; + return data; + }); + }) + ) + ); + + return ( + marketplaceExtensionsStream + .pipe(util2.setExecutableBit(['**/*.sh'])) + ); +} + +export interface IScannedBuiltinExtension { + extensionPath: string; + packageJSON: any; + packageNLS?: any; + readmePath?: string; + changelogPath?: string; +} + +export function scanBuiltinExtensions(extensionsRoot: string, exclude: string[] = []): IScannedBuiltinExtension[] { + const scannedExtensions: IScannedBuiltinExtension[] = []; + + try { + const extensionsFolders = fs.readdirSync(extensionsRoot); + for (const extensionFolder of extensionsFolders) { + if (exclude.indexOf(extensionFolder) >= 0) { + continue; + } + const packageJSONPath = path.join(extensionsRoot, extensionFolder, 'package.json'); + if (!fs.existsSync(packageJSONPath)) { + continue; + } + let packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); + if (!isWebExtension(packageJSON)) { + continue; + } + const children = fs.readdirSync(path.join(extensionsRoot, extensionFolder)); + const packageNLSPath = children.filter(child => child === 'package.nls.json')[0]; + const packageNLS = packageNLSPath ? JSON.parse(fs.readFileSync(path.join(extensionsRoot, extensionFolder, packageNLSPath)).toString()) : undefined; + const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; + const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; + + scannedExtensions.push({ + extensionPath: extensionFolder, + packageJSON, + packageNLS, + readmePath: readme ? path.join(extensionFolder, readme) : undefined, + changelogPath: changelog ? path.join(extensionFolder, changelog) : undefined, + }); + } + return scannedExtensions; + } catch (ex) { + return scannedExtensions; + } +} + +export function translatePackageJSON(packageJSON: string, packageNLSPath: string) { + const CharCode_PC = '%'.charCodeAt(0); + const packageNls = JSON.parse(fs.readFileSync(packageNLSPath).toString()); + const translate = (obj: any) => { + for (let key in obj) { + const val = obj[key]; + if (Array.isArray(val)) { + val.forEach(translate); + } else if (val && typeof val === 'object') { + translate(val); + } else if (typeof val === 'string' && val.charCodeAt(0) === CharCode_PC && val.charCodeAt(val.length - 1) === CharCode_PC) { + const translated = packageNls[val.substr(1, val.length - 2)]; + if (translated) { + obj[key] = translated; + } + } + } + }; + translate(packageJSON); + return packageJSON; } diff --git a/build/lib/git.js b/build/lib/git.js deleted file mode 100644 index da5d66fd8d2..00000000000 --- a/build/lib/git.js +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const fs = require("fs"); -/** - * Returns the sha1 commit version of a repository or undefined in case of failure. - */ -function getVersion(repo) { - const git = path.join(repo, '.git'); - const headPath = path.join(git, 'HEAD'); - let head; - try { - head = fs.readFileSync(headPath, 'utf8').trim(); - } - catch (e) { - return undefined; - } - if (/^[0-9a-f]{40}$/i.test(head)) { - return head; - } - const refMatch = /^ref: (.*)$/.exec(head); - if (!refMatch) { - return undefined; - } - const ref = refMatch[1]; - const refPath = path.join(git, ref); - try { - return fs.readFileSync(refPath, 'utf8').trim(); - } - catch (e) { - // noop - } - const packedRefsPath = path.join(git, 'packed-refs'); - let refsRaw; - try { - refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); - } - catch (e) { - return undefined; - } - const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; - let refsMatch; - let refs = {}; - while (refsMatch = refsRegex.exec(refsRaw)) { - refs[refsMatch[2]] = refsMatch[1]; - } - return refs[ref]; -} -exports.getVersion = getVersion; diff --git a/build/lib/i18n.js b/build/lib/i18n.js deleted file mode 100644 index 27a4054a1e4..00000000000 --- a/build/lib/i18n.js +++ /dev/null @@ -1,1204 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const fs = require("fs"); -const event_stream_1 = require("event-stream"); -const File = require("vinyl"); -const Is = require("is"); -const xml2js = require("xml2js"); -const glob = require("glob"); -const https = require("https"); -const gulp = require("gulp"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const iconv = require("iconv-lite"); -const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; -function log(message, ...rest) { - fancyLog(ansiColors.green('[i18n]'), message, ...rest); -} -exports.defaultLanguages = [ - { id: 'zh-tw', folderName: 'cht', translationId: 'zh-hant' }, - { id: 'zh-cn', folderName: 'chs', translationId: 'zh-hans' }, - { id: 'ja', folderName: 'jpn' }, - { id: 'ko', folderName: 'kor' }, - { id: 'de', folderName: 'deu' }, - { id: 'fr', folderName: 'fra' }, - { id: 'es', folderName: 'esn' }, - { id: 'ru', folderName: 'rus' }, - { id: 'it', folderName: 'ita' } -]; -// languages requested by the community to non-stable builds -exports.extraLanguages = [ - { id: 'pt-br', folderName: 'ptb' }, - { id: 'hu', folderName: 'hun' }, - { id: 'tr', folderName: 'trk' } -]; -// non built-in extensions also that are transifex and need to be part of the language packs -exports.externalExtensionsWithTranslations = { - 'vscode-chrome-debug': 'msjsdiag.debugger-for-chrome', - 'vscode-node-debug': 'ms-vscode.node-debug', - 'vscode-node-debug2': 'ms-vscode.node-debug2' -}; -var LocalizeInfo; -(function (LocalizeInfo) { - function is(value) { - let candidate = value; - return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(element => Is.string(element)))); - } - LocalizeInfo.is = is; -})(LocalizeInfo || (LocalizeInfo = {})); -var BundledFormat; -(function (BundledFormat) { - function is(value) { - if (Is.undef(value)) { - return false; - } - let candidate = value; - let length = Object.keys(value).length; - return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); - } - BundledFormat.is = is; -})(BundledFormat || (BundledFormat = {})); -var PackageJsonFormat; -(function (PackageJsonFormat) { - function is(value) { - if (Is.undef(value) || !Is.object(value)) { - return false; - } - return Object.keys(value).every(key => { - let element = value[key]; - return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); - }); - } - PackageJsonFormat.is = is; -})(PackageJsonFormat || (PackageJsonFormat = {})); -class Line { - constructor(indent = 0) { - this.buffer = []; - if (indent > 0) { - this.buffer.push(new Array(indent + 1).join(' ')); - } - } - append(value) { - this.buffer.push(value); - return this; - } - toString() { - return this.buffer.join(''); - } -} -exports.Line = Line; -class TextModel { - constructor(contents) { - this._lines = contents.split(/\r\n|\r|\n/); - } - get lines() { - return this._lines; - } -} -class XLF { - constructor(project) { - this.project = project; - this.buffer = []; - this.files = Object.create(null); - this.numberOfMessages = 0; - } - toString() { - this.appendHeader(); - for (let file in this.files) { - this.appendNewLine(``, 2); - for (let item of this.files[file]) { - this.addStringItem(item); - } - this.appendNewLine('', 2); - } - this.appendFooter(); - return this.buffer.join('\r\n'); - } - addFile(original, keys, messages) { - if (keys.length === 0) { - console.log('No keys in ' + original); - return; - } - if (keys.length !== messages.length) { - throw new Error(`Unmatching keys(${keys.length}) and messages(${messages.length}).`); - } - this.numberOfMessages += keys.length; - this.files[original] = []; - let existingKeys = new Set(); - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let realKey; - let comment; - if (Is.string(key)) { - realKey = key; - comment = undefined; - } - else if (LocalizeInfo.is(key)) { - realKey = key.key; - if (key.comment && key.comment.length > 0) { - comment = key.comment.map(comment => encodeEntities(comment)).join('\r\n'); - } - } - if (!realKey || existingKeys.has(realKey)) { - continue; - } - existingKeys.add(realKey); - let message = encodeEntities(messages[i]); - this.files[original].push({ id: realKey, message: message, comment: comment }); - } - } - addStringItem(item) { - if (!item.id || !item.message) { - throw new Error(`No item ID or value specified: ${JSON.stringify(item)}`); - } - this.appendNewLine(``, 4); - this.appendNewLine(`${item.message}`, 6); - if (item.comment) { - this.appendNewLine(`${item.comment}`, 6); - } - this.appendNewLine('', 4); - } - appendHeader() { - this.appendNewLine('', 0); - this.appendNewLine('', 0); - } - appendFooter() { - this.appendNewLine('', 0); - } - appendNewLine(content, indent) { - let line = new Line(indent); - line.append(content); - this.buffer.push(line.toString()); - } -} -exports.XLF = XLF; -XLF.parsePseudo = function (xlfString) { - return new Promise((resolve) => { - let parser = new xml2js.Parser(); - let files = []; - parser.parseString(xlfString, function (_err, result) { - const fileNodes = result['xliff']['file']; - fileNodes.forEach(file => { - const originalFilePath = file.$.original; - const messages = {}; - const transUnits = file.body[0]['trans-unit']; - if (transUnits) { - transUnits.forEach((unit) => { - const key = unit.$.id; - const val = pseudify(unit.source[0]['_'].toString()); - if (key && val) { - messages[key] = decodeEntities(val); - } - }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: 'ps' }); - } - }); - resolve(files); - }); - }); -}; -XLF.parse = function (xlfString) { - return new Promise((resolve, reject) => { - let parser = new xml2js.Parser(); - let files = []; - parser.parseString(xlfString, function (err, result) { - if (err) { - reject(new Error(`XLF parsing error: Failed to parse XLIFF string. ${err}`)); - } - const fileNodes = result['xliff']['file']; - if (!fileNodes) { - reject(new Error(`XLF parsing error: XLIFF file does not contain "xliff" or "file" node(s) required for parsing.`)); - } - fileNodes.forEach((file) => { - const originalFilePath = file.$.original; - if (!originalFilePath) { - reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); - } - let language = file.$['target-language']; - if (!language) { - reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`)); - } - const messages = {}; - const transUnits = file.body[0]['trans-unit']; - if (transUnits) { - transUnits.forEach((unit) => { - const key = unit.$.id; - if (!unit.target) { - return; // No translation available - } - let val = unit.target[0]; - if (typeof val !== 'string') { - val = val._; - } - if (key && val) { - messages[key] = decodeEntities(val); - } - else { - reject(new Error(`XLF parsing error: XLIFF file ${originalFilePath} does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); - } - }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); - } - }); - resolve(files); - }); - }); -}; -class Limiter { - constructor(maxDegreeOfParalellism) { - this.maxDegreeOfParalellism = maxDegreeOfParalellism; - this.outstandingPromises = []; - this.runningPromises = 0; - } - queue(factory) { - return new Promise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); - this.consume(); - }); - } - consume() { - while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { - const iLimitedTask = this.outstandingPromises.shift(); - this.runningPromises++; - const promise = iLimitedTask.factory(); - promise.then(iLimitedTask.c).catch(iLimitedTask.e); - promise.then(() => this.consumed()).catch(() => this.consumed()); - } - } - consumed() { - this.runningPromises--; - this.consume(); - } -} -exports.Limiter = Limiter; -function sortLanguages(languages) { - return languages.sort((a, b) => { - return a.id < b.id ? -1 : (a.id > b.id ? 1 : 0); - }); -} -function stripComments(content) { - /** - * First capturing group matches double quoted string - * Second matches single quotes string - * Third matches block comments - * Fourth matches line comments - */ - const regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; - let result = content.replace(regexp, (match, _m1, _m2, m3, m4) => { - // Only one of m1, m2, m3, m4 matches - if (m3) { - // A block comment. Replace with nothing - return ''; - } - else if (m4) { - // A line comment. If it ends in \r?\n then keep it. - let length = m4.length; - if (length > 2 && m4[length - 1] === '\n') { - return m4[length - 2] === '\r' ? '\r\n' : '\n'; - } - else { - return ''; - } - } - else { - // We match a string - return match; - } - }); - return result; -} -function escapeCharacters(value) { - const result = []; - for (let i = 0; i < value.length; i++) { - const ch = value.charAt(i); - switch (ch) { - case '\'': - result.push('\\\''); - break; - case '"': - result.push('\\"'); - break; - case '\\': - result.push('\\\\'); - break; - case '\n': - result.push('\\n'); - break; - case '\r': - result.push('\\r'); - break; - case '\t': - result.push('\\t'); - break; - case '\b': - result.push('\\b'); - break; - case '\f': - result.push('\\f'); - break; - default: - result.push(ch); - } - } - return result.join(''); -} -function processCoreBundleFormat(fileHeader, languages, json, emitter) { - let keysSection = json.keys; - let messageSection = json.messages; - let bundleSection = json.bundles; - let statistics = Object.create(null); - let defaultMessages = Object.create(null); - let modules = Object.keys(keysSection); - modules.forEach((module) => { - let keys = keysSection[module]; - let messages = messageSection[module]; - if (!messages || keys.length !== messages.length) { - emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); - return; - } - let messageMap = Object.create(null); - defaultMessages[module] = messageMap; - keys.map((key, i) => { - if (typeof key === 'string') { - messageMap[key] = messages[i]; - } - else { - messageMap[key.key] = messages[i]; - } - }); - }); - let languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); - if (!fs.existsSync(languageDirectory)) { - log(`No VS Code localization repository found. Looking at ${languageDirectory}`); - log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); - } - let sortedLanguages = sortLanguages(languages); - sortedLanguages.forEach((language) => { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`Generating nls bundles for: ${language.id}`); - } - statistics[language.id] = 0; - let localizedModules = Object.create(null); - let languageFolderName = language.translationId || language.id; - let i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); - let allMessages; - if (fs.existsSync(i18nFile)) { - let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); - allMessages = JSON.parse(content); - } - modules.forEach((module) => { - let order = keysSection[module]; - let moduleMessage; - if (allMessages) { - moduleMessage = allMessages.contents[module]; - } - if (!moduleMessage) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`No localized messages found for module ${module}. Using default messages.`); - } - moduleMessage = defaultMessages[module]; - statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; - } - let localizedMessages = []; - order.forEach((keyInfo) => { - let key = null; - if (typeof keyInfo === 'string') { - key = keyInfo; - } - else { - key = keyInfo.key; - } - let message = moduleMessage[key]; - if (!message) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`No localized message found for key ${key} in module ${module}. Using default message.`); - } - message = defaultMessages[module][key]; - statistics[language.id] = statistics[language.id] + 1; - } - localizedMessages.push(message); - }); - localizedModules[module] = localizedMessages; - }); - Object.keys(bundleSection).forEach((bundle) => { - let modules = bundleSection[bundle]; - let contents = [ - fileHeader, - `define("${bundle}.nls.${language.id}", {` - ]; - modules.forEach((module, index) => { - contents.push(`\t"${module}": [`); - let messages = localizedModules[module]; - if (!messages) { - emitter.emit('error', `Didn't find messages for module ${module}.`); - return; - } - messages.forEach((message, index) => { - contents.push(`\t\t"${escapeCharacters(message)}${index < messages.length ? '",' : '"'}`); - }); - contents.push(index < modules.length - 1 ? '\t],' : '\t]'); - }); - contents.push('});'); - emitter.queue(new File({ path: bundle + '.nls.' + language.id + '.js', contents: Buffer.from(contents.join('\n'), 'utf-8') })); - }); - }); - Object.keys(statistics).forEach(key => { - let value = statistics[key]; - log(`${key} has ${value} untranslated strings.`); - }); - sortedLanguages.forEach(language => { - let stats = statistics[language.id]; - if (Is.undef(stats)) { - log(`\tNo translations found for language ${language.id}. Using default language instead.`); - } - }); -} -function processNlsFiles(opts) { - return event_stream_1.through(function (file) { - let fileName = path.basename(file.path); - if (fileName === 'nls.metadata.json') { - let json = null; - if (file.isBuffer()) { - json = JSON.parse(file.contents.toString('utf8')); - } - else { - this.emit('error', `Failed to read component file: ${file.relative}`); - return; - } - if (BundledFormat.is(json)) { - processCoreBundleFormat(opts.fileHeader, opts.languages, json, this); - } - } - this.queue(file); - }); -} -exports.processNlsFiles = processNlsFiles; -const editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench', extensionsProject = 'vscode-extensions', setupProject = 'vscode-setup'; -function getResource(sourceFile) { - let resource; - if (/^vs\/platform/.test(sourceFile)) { - return { name: 'vs/platform', project: editorProject }; - } - else if (/^vs\/editor\/contrib/.test(sourceFile)) { - return { name: 'vs/editor/contrib', project: editorProject }; - } - else if (/^vs\/editor/.test(sourceFile)) { - return { name: 'vs/editor', project: editorProject }; - } - else if (/^vs\/base/.test(sourceFile)) { - return { name: 'vs/base', project: editorProject }; - } - else if (/^vs\/code/.test(sourceFile)) { - return { name: 'vs/code', project: workbenchProject }; - } - else if (/^vs\/workbench\/contrib/.test(sourceFile)) { - resource = sourceFile.split('/', 4).join('/'); - return { name: resource, project: workbenchProject }; - } - else if (/^vs\/workbench\/services/.test(sourceFile)) { - resource = sourceFile.split('/', 4).join('/'); - return { name: resource, project: workbenchProject }; - } - else if (/^vs\/workbench/.test(sourceFile)) { - return { name: 'vs/workbench', project: workbenchProject }; - } - throw new Error(`Could not identify the XLF bundle for ${sourceFile}`); -} -exports.getResource = getResource; -function createXlfFilesForCoreBundle() { - return event_stream_1.through(function (file) { - const basename = path.basename(file.path); - if (basename === 'nls.metadata.json') { - if (file.isBuffer()) { - const xlfs = Object.create(null); - const json = JSON.parse(file.contents.toString('utf8')); - for (let coreModule in json.keys) { - const projectResource = getResource(coreModule); - const resource = projectResource.name; - const project = projectResource.project; - const keys = json.keys[coreModule]; - const messages = json.messages[coreModule]; - if (keys.length !== messages.length) { - this.emit('error', `There is a mismatch between keys and messages in ${file.relative} for module ${coreModule}`); - return; - } - else { - let xlf = xlfs[resource]; - if (!xlf) { - xlf = new XLF(project); - xlfs[resource] = xlf; - } - xlf.addFile(`src/${coreModule}`, keys, messages); - } - } - for (let resource in xlfs) { - const xlf = xlfs[resource]; - const filePath = `${xlf.project}/${resource.replace(/\//g, '_')}.xlf`; - const xlfFile = new File({ - path: filePath, - contents: Buffer.from(xlf.toString(), 'utf8') - }); - this.queue(xlfFile); - } - } - else { - this.emit('error', new Error(`File ${file.relative} is not using a buffer content`)); - return; - } - } - else { - this.emit('error', new Error(`File ${file.relative} is not a core meta data file.`)); - return; - } - }); -} -exports.createXlfFilesForCoreBundle = createXlfFilesForCoreBundle; -function createXlfFilesForExtensions() { - let counter = 0; - let folderStreamEnded = false; - let folderStreamEndEmitted = false; - return event_stream_1.through(function (extensionFolder) { - const folderStream = this; - const stat = fs.statSync(extensionFolder.path); - if (!stat.isDirectory()) { - return; - } - let extensionName = path.basename(extensionFolder.path); - if (extensionName === 'node_modules') { - return; - } - counter++; - let _xlf; - function getXlf() { - if (!_xlf) { - _xlf = new XLF(extensionsProject); - } - return _xlf; - } - gulp.src([`.build/extensions/${extensionName}/package.nls.json`, `.build/extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }).pipe(event_stream_1.through(function (file) { - if (file.isBuffer()) { - const buffer = file.contents; - const basename = path.basename(file.path); - if (basename === 'package.nls.json') { - const json = JSON.parse(buffer.toString('utf8')); - const keys = Object.keys(json); - const messages = keys.map((key) => { - const value = json[key]; - if (Is.string(value)) { - return value; - } - else if (value) { - return value.message; - } - else { - return `Unknown message for key: ${key}`; - } - }); - getXlf().addFile(`extensions/${extensionName}/package`, keys, messages); - } - else if (basename === 'nls.metadata.json') { - const json = JSON.parse(buffer.toString('utf8')); - const relPath = path.relative(`.build/extensions/${extensionName}`, path.dirname(file.path)); - for (let file in json) { - const fileContent = json[file]; - getXlf().addFile(`extensions/${extensionName}/${relPath}/${file}`, fileContent.keys, fileContent.messages); - } - } - else { - this.emit('error', new Error(`${file.path} is not a valid extension nls file`)); - return; - } - } - }, function () { - if (_xlf) { - let xlfFile = new File({ - path: path.join(extensionsProject, extensionName + '.xlf'), - contents: Buffer.from(_xlf.toString(), 'utf8') - }); - folderStream.queue(xlfFile); - } - this.queue(null); - counter--; - if (counter === 0 && folderStreamEnded && !folderStreamEndEmitted) { - folderStreamEndEmitted = true; - folderStream.queue(null); - } - })); - }, function () { - folderStreamEnded = true; - if (counter === 0) { - folderStreamEndEmitted = true; - this.queue(null); - } - }); -} -exports.createXlfFilesForExtensions = createXlfFilesForExtensions; -function createXlfFilesForIsl() { - return event_stream_1.through(function (file) { - let projectName, resourceFile; - if (path.basename(file.path) === 'Default.isl') { - projectName = setupProject; - resourceFile = 'setup_default.xlf'; - } - else { - projectName = workbenchProject; - resourceFile = 'setup_messages.xlf'; - } - let xlf = new XLF(projectName), keys = [], messages = []; - let model = new TextModel(file.contents.toString()); - let inMessageSection = false; - model.lines.forEach(line => { - if (line.length === 0) { - return; - } - let firstChar = line.charAt(0); - switch (firstChar) { - case ';': - // Comment line; - return; - case '[': - inMessageSection = '[Messages]' === line || '[CustomMessages]' === line; - return; - } - if (!inMessageSection) { - return; - } - let sections = line.split('='); - if (sections.length !== 2) { - throw new Error(`Badly formatted message found: ${line}`); - } - else { - let key = sections[0]; - let value = sections[1]; - if (key.length > 0 && value.length > 0) { - keys.push(key); - messages.push(value); - } - } - }); - const originalPath = file.path.substring(file.cwd.length + 1, file.path.split('.')[0].length).replace(/\\/g, '/'); - xlf.addFile(originalPath, keys, messages); - // Emit only upon all ISL files combined into single XLF instance - const newFilePath = path.join(projectName, resourceFile); - const xlfFile = new File({ path: newFilePath, contents: Buffer.from(xlf.toString(), 'utf-8') }); - this.queue(xlfFile); - }); -} -exports.createXlfFilesForIsl = createXlfFilesForIsl; -function pushXlfFiles(apiHostname, username, password) { - let tryGetPromises = []; - let updateCreatePromises = []; - return event_stream_1.through(function (file) { - const project = path.dirname(file.relative); - const fileName = path.basename(file.path); - const slug = fileName.substr(0, fileName.length - '.xlf'.length); - const credentials = `${username}:${password}`; - // Check if resource already exists, if not, then create it. - let promise = tryGetResource(project, slug, apiHostname, credentials); - tryGetPromises.push(promise); - promise.then(exists => { - if (exists) { - promise = updateResource(project, slug, file, apiHostname, credentials); - } - else { - promise = createResource(project, slug, file, apiHostname, credentials); - } - updateCreatePromises.push(promise); - }); - }, function () { - // End the pipe only after all the communication with Transifex API happened - Promise.all(tryGetPromises).then(() => { - Promise.all(updateCreatePromises).then(() => { - this.queue(null); - }).catch((reason) => { throw new Error(reason); }); - }).catch((reason) => { throw new Error(reason); }); - }); -} -exports.pushXlfFiles = pushXlfFiles; -function getAllResources(project, apiHostname, username, password) { - return new Promise((resolve, reject) => { - const credentials = `${username}:${password}`; - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resources`, - auth: credentials, - method: 'GET' - }; - const request = https.request(options, (res) => { - let buffer = []; - res.on('data', (chunk) => buffer.push(chunk)); - res.on('end', () => { - if (res.statusCode === 200) { - let json = JSON.parse(Buffer.concat(buffer).toString()); - if (Array.isArray(json)) { - resolve(json.map(o => o.slug)); - return; - } - reject(`Unexpected data format. Response code: ${res.statusCode}.`); - } - else { - reject(`No resources in ${project} returned no data. Response code: ${res.statusCode}.`); - } - }); - }); - request.on('error', (err) => { - reject(`Failed to query resources in ${project} with the following error: ${err}. ${options.path}`); - }); - request.end(); - }); -} -function findObsoleteResources(apiHostname, username, password) { - let resourcesByProject = Object.create(null); - resourcesByProject[extensionsProject] = [].concat(exports.externalExtensionsWithTranslations); // clone - return event_stream_1.through(function (file) { - const project = path.dirname(file.relative); - const fileName = path.basename(file.path); - const slug = fileName.substr(0, fileName.length - '.xlf'.length); - let slugs = resourcesByProject[project]; - if (!slugs) { - resourcesByProject[project] = slugs = []; - } - slugs.push(slug); - this.push(file); - }, function () { - const json = JSON.parse(fs.readFileSync('./build/lib/i18n.resources.json', 'utf8')); - let i18Resources = [...json.editor, ...json.workbench].map((r) => r.project + '/' + r.name.replace(/\//g, '_')); - let extractedResources = []; - for (let project of [workbenchProject, editorProject]) { - for (let resource of resourcesByProject[project]) { - if (resource !== 'setup_messages') { - extractedResources.push(project + '/' + resource); - } - } - } - if (i18Resources.length !== extractedResources.length) { - console.log(`[i18n] Obsolete resources in file 'build/lib/i18n.resources.json': JSON.stringify(${i18Resources.filter(p => extractedResources.indexOf(p) === -1)})`); - console.log(`[i18n] Missing resources in file 'build/lib/i18n.resources.json': JSON.stringify(${extractedResources.filter(p => i18Resources.indexOf(p) === -1)})`); - } - let promises = []; - for (let project in resourcesByProject) { - promises.push(getAllResources(project, apiHostname, username, password).then(resources => { - let expectedResources = resourcesByProject[project]; - let unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); - if (unusedResources.length) { - console.log(`[transifex] Obsolete resources in project '${project}': ${unusedResources.join(', ')}`); - } - })); - } - return Promise.all(promises).then(_ => { - this.push(null); - }).catch((reason) => { throw new Error(reason); }); - }); -} -exports.findObsoleteResources = findObsoleteResources; -function tryGetResource(project, slug, apiHostname, credentials) { - return new Promise((resolve, reject) => { - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/?details`, - auth: credentials, - method: 'GET' - }; - const request = https.request(options, (response) => { - if (response.statusCode === 404) { - resolve(false); - } - else if (response.statusCode === 200) { - resolve(true); - } - else { - reject(`Failed to query resource ${project}/${slug}. Response: ${response.statusCode} ${response.statusMessage}`); - } - }); - request.on('error', (err) => { - reject(`Failed to get ${project}/${slug} on Transifex: ${err}`); - }); - request.end(); - }); -} -function createResource(project, slug, xlfFile, apiHostname, credentials) { - return new Promise((_resolve, reject) => { - const data = JSON.stringify({ - 'content': xlfFile.contents.toString(), - 'name': slug, - 'slug': slug, - 'i18n_type': 'XLIFF' - }); - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resources`, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data) - }, - auth: credentials, - method: 'POST' - }; - let request = https.request(options, (res) => { - if (res.statusCode === 201) { - log(`Resource ${project}/${slug} successfully created on Transifex.`); - } - else { - reject(`Something went wrong in the request creating ${slug} in ${project}. ${res.statusCode}`); - } - }); - request.on('error', (err) => { - reject(`Failed to create ${project}/${slug} on Transifex: ${err}`); - }); - request.write(data); - request.end(); - }); -} -/** - * The following link provides information about how Transifex handles updates of a resource file: - * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files - */ -function updateResource(project, slug, xlfFile, apiHostname, credentials) { - return new Promise((resolve, reject) => { - const data = JSON.stringify({ content: xlfFile.contents.toString() }); - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/content`, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(data) - }, - auth: credentials, - method: 'PUT' - }; - let request = https.request(options, (res) => { - if (res.statusCode === 200) { - res.setEncoding('utf8'); - let responseBuffer = ''; - res.on('data', function (chunk) { - responseBuffer += chunk; - }); - res.on('end', () => { - const response = JSON.parse(responseBuffer); - log(`Resource ${project}/${slug} successfully updated on Transifex. Strings added: ${response.strings_added}, updated: ${response.strings_added}, deleted: ${response.strings_added}`); - resolve(); - }); - } - else { - reject(`Something went wrong in the request updating ${slug} in ${project}. ${res.statusCode}`); - } - }); - request.on('error', (err) => { - reject(`Failed to update ${project}/${slug} on Transifex: ${err}`); - }); - request.write(data); - request.end(); - }); -} -// cache resources -let _coreAndExtensionResources; -function pullCoreAndExtensionsXlfFiles(apiHostname, username, password, language, externalExtensions) { - if (!_coreAndExtensionResources) { - _coreAndExtensionResources = []; - // editor and workbench - const json = JSON.parse(fs.readFileSync('./build/lib/i18n.resources.json', 'utf8')); - _coreAndExtensionResources.push(...json.editor); - _coreAndExtensionResources.push(...json.workbench); - // extensions - let extensionsToLocalize = Object.create(null); - glob.sync('.build/extensions/**/*.nls.json').forEach(extension => extensionsToLocalize[extension.split('/')[2]] = true); - glob.sync('.build/extensions/*/node_modules/vscode-nls').forEach(extension => extensionsToLocalize[extension.split('/')[2]] = true); - Object.keys(extensionsToLocalize).forEach(extension => { - _coreAndExtensionResources.push({ name: extension, project: extensionsProject }); - }); - if (externalExtensions) { - for (let resourceName in externalExtensions) { - _coreAndExtensionResources.push({ name: resourceName, project: extensionsProject }); - } - } - } - return pullXlfFiles(apiHostname, username, password, language, _coreAndExtensionResources); -} -exports.pullCoreAndExtensionsXlfFiles = pullCoreAndExtensionsXlfFiles; -function pullSetupXlfFiles(apiHostname, username, password, language, includeDefault) { - let setupResources = [{ name: 'setup_messages', project: workbenchProject }]; - if (includeDefault) { - setupResources.push({ name: 'setup_default', project: setupProject }); - } - return pullXlfFiles(apiHostname, username, password, language, setupResources); -} -exports.pullSetupXlfFiles = pullSetupXlfFiles; -function pullXlfFiles(apiHostname, username, password, language, resources) { - const credentials = `${username}:${password}`; - let expectedTranslationsCount = resources.length; - let translationsRetrieved = 0, called = false; - return event_stream_1.readable(function (_count, callback) { - // Mark end of stream when all resources were retrieved - if (translationsRetrieved === expectedTranslationsCount) { - return this.emit('end'); - } - if (!called) { - called = true; - const stream = this; - resources.map(function (resource) { - retrieveResource(language, resource, apiHostname, credentials).then((file) => { - if (file) { - stream.emit('data', file); - } - translationsRetrieved++; - }).catch(error => { throw new Error(error); }); - }); - } - callback(); - }); -} -const limiter = new Limiter(NUMBER_OF_CONCURRENT_DOWNLOADS); -function retrieveResource(language, resource, apiHostname, credentials) { - return limiter.queue(() => new Promise((resolve, reject) => { - const slug = resource.name.replace(/\//g, '_'); - const project = resource.project; - let transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; - const options = { - hostname: apiHostname, - path: `/api/2/project/${project}/resource/${slug}/translation/${transifexLanguageId}?file&mode=onlyreviewed`, - auth: credentials, - port: 443, - method: 'GET' - }; - console.log('[transifex] Fetching ' + options.path); - let request = https.request(options, (res) => { - let xlfBuffer = []; - res.on('data', (chunk) => xlfBuffer.push(chunk)); - res.on('end', () => { - if (res.statusCode === 200) { - resolve(new File({ contents: Buffer.concat(xlfBuffer), path: `${project}/${slug}.xlf` })); - } - else if (res.statusCode === 404) { - console.log(`[transifex] ${slug} in ${project} returned no data.`); - resolve(null); - } - else { - reject(`${slug} in ${project} returned no data. Response code: ${res.statusCode}.`); - } - }); - }); - request.on('error', (err) => { - reject(`Failed to query resource ${slug} with the following error: ${err}. ${options.path}`); - }); - request.end(); - })); -} -function prepareI18nFiles() { - let parsePromises = []; - return event_stream_1.through(function (xlf) { - let stream = this; - let parsePromise = XLF.parse(xlf.contents.toString()); - parsePromises.push(parsePromise); - parsePromise.then(resolvedFiles => { - resolvedFiles.forEach(file => { - let translatedFile = createI18nFile(file.originalFilePath, file.messages); - stream.queue(translatedFile); - }); - }); - }, function () { - Promise.all(parsePromises) - .then(() => { this.queue(null); }) - .catch(reason => { throw new Error(reason); }); - }); -} -exports.prepareI18nFiles = prepareI18nFiles; -function createI18nFile(originalFilePath, messages) { - let result = Object.create(null); - result[''] = [ - '--------------------------------------------------------------------------------------------', - 'Copyright (c) Microsoft Corporation. All rights reserved.', - 'Licensed under the MIT License. See License.txt in the project root for license information.', - '--------------------------------------------------------------------------------------------', - 'Do not edit this file. It is machine generated.' - ]; - for (let key of Object.keys(messages)) { - result[key] = messages[key]; - } - let content = JSON.stringify(result, null, '\t'); - if (process.platform === 'win32') { - content = content.replace(/\n/g, '\r\n'); - } - return new File({ - path: path.join(originalFilePath + '.i18n.json'), - contents: Buffer.from(content, 'utf8') - }); -} -const i18nPackVersion = "1.0.0"; -function pullI18nPackFiles(apiHostname, username, password, language, resultingTranslationPaths) { - return pullCoreAndExtensionsXlfFiles(apiHostname, username, password, language, exports.externalExtensionsWithTranslations) - .pipe(prepareI18nPackFiles(exports.externalExtensionsWithTranslations, resultingTranslationPaths, language.id === 'ps')); -} -exports.pullI18nPackFiles = pullI18nPackFiles; -function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pseudo = false) { - let parsePromises = []; - let mainPack = { version: i18nPackVersion, contents: {} }; - let extensionsPacks = {}; - let errors = []; - return event_stream_1.through(function (xlf) { - let project = path.basename(path.dirname(xlf.relative)); - let resource = path.basename(xlf.relative, '.xlf'); - let contents = xlf.contents.toString(); - let parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); - parsePromises.push(parsePromise); - parsePromise.then(resolvedFiles => { - resolvedFiles.forEach(file => { - const path = file.originalFilePath; - const firstSlash = path.indexOf('/'); - if (project === extensionsProject) { - let extPack = extensionsPacks[resource]; - if (!extPack) { - extPack = extensionsPacks[resource] = { version: i18nPackVersion, contents: {} }; - } - const externalId = externalExtensions[resource]; - if (!externalId) { // internal extension: remove 'extensions/extensionId/' segnent - const secondSlash = path.indexOf('/', firstSlash + 1); - extPack.contents[path.substr(secondSlash + 1)] = file.messages; - } - else { - extPack.contents[path] = file.messages; - } - } - else { - mainPack.contents[path.substr(firstSlash + 1)] = file.messages; - } - }); - }).catch(reason => { - errors.push(reason); - }); - }, function () { - Promise.all(parsePromises) - .then(() => { - if (errors.length > 0) { - throw errors; - } - const translatedMainFile = createI18nFile('./main', mainPack); - resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' }); - this.queue(translatedMainFile); - for (let extension in extensionsPacks) { - const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); - this.queue(translatedExtFile); - const externalExtensionId = externalExtensions[extension]; - if (externalExtensionId) { - resultingTranslationPaths.push({ id: externalExtensionId, resourceName: `extensions/${extension}.i18n.json` }); - } - else { - resultingTranslationPaths.push({ id: `vscode.${extension}`, resourceName: `extensions/${extension}.i18n.json` }); - } - } - this.queue(null); - }) - .catch((reason) => { - this.emit('error', reason); - }); - }); -} -exports.prepareI18nPackFiles = prepareI18nPackFiles; -function prepareIslFiles(language, innoSetupConfig) { - let parsePromises = []; - return event_stream_1.through(function (xlf) { - let stream = this; - let parsePromise = XLF.parse(xlf.contents.toString()); - parsePromises.push(parsePromise); - parsePromise.then(resolvedFiles => { - resolvedFiles.forEach(file => { - if (path.basename(file.originalFilePath) === 'Default' && !innoSetupConfig.defaultInfo) { - return; - } - let translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); - stream.queue(translatedFile); - }); - }).catch(reason => { - this.emit('error', reason); - }); - }, function () { - Promise.all(parsePromises) - .then(() => { this.queue(null); }) - .catch(reason => { - this.emit('error', reason); - }); - }); -} -exports.prepareIslFiles = prepareIslFiles; -function createIslFile(originalFilePath, messages, language, innoSetup) { - let content = []; - let originalContent; - if (path.basename(originalFilePath) === 'Default') { - originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); - } - else { - originalContent = new TextModel(fs.readFileSync(originalFilePath + '.en.isl', 'utf8')); - } - originalContent.lines.forEach(line => { - if (line.length > 0) { - let firstChar = line.charAt(0); - if (firstChar === '[' || firstChar === ';') { - if (line === '; *** Inno Setup version 5.5.3+ English messages ***') { - content.push(`; *** Inno Setup version 5.5.3+ ${innoSetup.defaultInfo.name} messages ***`); - } - else { - content.push(line); - } - } - else { - let sections = line.split('='); - let key = sections[0]; - let translated = line; - if (key) { - if (key === 'LanguageName') { - translated = `${key}=${innoSetup.defaultInfo.name}`; - } - else if (key === 'LanguageID') { - translated = `${key}=${innoSetup.defaultInfo.id}`; - } - else if (key === 'LanguageCodePage') { - translated = `${key}=${innoSetup.codePage.substr(2)}`; - } - else { - let translatedMessage = messages[key]; - if (translatedMessage) { - translated = `${key}=${translatedMessage}`; - } - } - } - content.push(translated); - } - } - }); - const basename = path.basename(originalFilePath); - const filePath = `${basename}.${language.id}.isl`; - return new File({ - path: filePath, - contents: iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage) - }); -} -function encodeEntities(value) { - let result = []; - for (let i = 0; i < value.length; i++) { - let ch = value[i]; - switch (ch) { - case '<': - result.push('<'); - break; - case '>': - result.push('>'); - break; - case '&': - result.push('&'); - break; - default: - result.push(ch); - } - } - return result.join(''); -} -function decodeEntities(value) { - return value.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); -} -function pseudify(message) { - return '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; -} diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 174083dac88..47f0155d8a5 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -94,6 +94,10 @@ "name": "vs/workbench/contrib/issue", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/keybindings", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/markers", "project": "vscode-workbench" @@ -119,7 +123,11 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/quickopen", + "name": "vs/workbench/contrib/notebook", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/quickaccess", "project": "vscode-workbench" }, { @@ -134,6 +142,10 @@ "name": "vs/workbench/contrib/relauncher", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/sash", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/scm", "project": "vscode-workbench" @@ -142,6 +154,10 @@ "name": "vs/workbench/contrib/search", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/searchEditor", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/snippets", "project": "vscode-workbench" @@ -190,6 +206,14 @@ "name": "vs/workbench/contrib/webview", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/webviewPanel", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/workspaces", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/customEditor", "project": "vscode-workbench" @@ -206,6 +230,10 @@ "name": "vs/workbench/contrib/userDataSync", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/views", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/actions", "project": "vscode-workbench" @@ -230,10 +258,6 @@ "name": "vs/workbench/services/configurationResolver", "project": "vscode-workbench" }, - { - "name": "vs/workbench/services/crashReporter", - "project": "vscode-workbench" - }, { "name": "vs/workbench/services/dialogs", "project": "vscode-workbench" @@ -254,6 +278,10 @@ "name": "vs/workbench/services/files", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/log", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/integrity", "project": "vscode-workbench" @@ -294,6 +322,10 @@ "name": "vs/workbench/services/textMate", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/workingCopy", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/workspaces", "project": "vscode-workbench" @@ -321,6 +353,18 @@ { "name": "vs/workbench/services/userDataSync", "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/views", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/timeline", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/authentication", + "project": "vscode-workbench" } ] } diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index d69109a9d05..a23b58ccd3d 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -15,7 +15,7 @@ import * as https from 'https'; import * as gulp from 'gulp'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; -import * as iconv from 'iconv-lite'; +import * as iconv from 'iconv-lite-umd'; const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; @@ -201,7 +201,7 @@ export class XLF { for (let file in this.files) { this.appendNewLine(``, 2); for (let item of this.files[file]) { - this.addStringItem(item); + this.addStringItem(file, item); } this.appendNewLine('', 2); } @@ -243,9 +243,12 @@ export class XLF { } } - private addStringItem(item: Item): void { - if (!item.id || !item.message) { - throw new Error(`No item ID or value specified: ${JSON.stringify(item)}`); + private addStringItem(file: string, item: Item): void { + if (!item.id || item.message === undefined || item.message === null) { + throw new Error(`No item ID or value specified: ${JSON.stringify(item)}. File: ${file}`); + } + if (item.message.length === 0) { + log(`Item with id ${item.id} in file ${file} has an empty message.`); } this.appendNewLine(``, 4); @@ -993,7 +996,7 @@ function createResource(project: string, slug: string, xlfFile: File, apiHostnam * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files */ function updateResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: string): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const data = JSON.stringify({ content: xlfFile.contents.toString() }); const options = { hostname: apiHostname, @@ -1185,7 +1188,7 @@ interface I18nPack { }; } -const i18nPackVersion = "1.0.0"; +const i18nPackVersion = '1.0.0'; export interface TranslationPath { id: string; @@ -1305,11 +1308,7 @@ function createIslFile(originalFilePath: string, messages: Map, language if (line.length > 0) { let firstChar = line.charAt(0); if (firstChar === '[' || firstChar === ';') { - if (line === '; *** Inno Setup version 5.5.3+ English messages ***') { - content.push(`; *** Inno Setup version 5.5.3+ ${innoSetup.defaultInfo!.name} messages ***`); - } else { - content.push(line); - } + content.push(line); } else { let sections: string[] = line.split('='); let key = sections[0]; @@ -1336,10 +1335,11 @@ function createIslFile(originalFilePath: string, messages: Map, language const basename = path.basename(originalFilePath); const filePath = `${basename}.${language.id}.isl`; + const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage); return new File({ path: filePath, - contents: iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage) + contents: Buffer.from(encoded), }); } diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js deleted file mode 100644 index 8f77ed81458..00000000000 --- a/build/lib/layersChecker.js +++ /dev/null @@ -1,209 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const ts = require("typescript"); -const fs_1 = require("fs"); -const path_1 = require("path"); -const minimatch_1 = require("minimatch"); -// -// ############################################################################################# -// -// A custom typescript checker for the specific task of detecting the use of certain types in a -// layer that does not allow such use. For example: -// - using DOM globals in common/node/electron-main layer (e.g. HTMLElement) -// - using node.js globals in common/browser layer (e.g. process) -// -// Make changes to below RULES to lift certain files from these checks only if absolutely needed -// -// ############################################################################################# -// -// Types we assume are present in all implementations of JS VMs (node.js, browsers) -// Feel free to add more core types as you see needed if present in node.js and browsers -const CORE_TYPES = [ - 'require', - 'atob', - 'btoa', - 'setTimeout', - 'clearTimeout', - 'setInterval', - 'clearInterval', - 'console', - 'log', - 'info', - 'warn', - 'error', - 'group', - 'groupEnd', - 'table', - 'Error', - 'String', - 'throws', - 'stack', - 'captureStackTrace', - 'stackTraceLimit', - 'TextDecoder', - 'TextEncoder', - 'encode', - 'decode', - 'self', - 'trimLeft', - 'trimRight' -]; -const RULES = [ - // Tests: skip - { - target: '**/vs/**/test/**', - skip: true // -> skip all test files - }, - // Common: vs/base/common/platform.ts - { - target: '**/vs/base/common/platform.ts', - allowedTypes: [ - ...CORE_TYPES, - // Safe access to postMessage() and friends - 'MessageEvent', - 'data' - ], - disallowedDefinitions: [ - 'lib.dom.d.ts', - '@types/node' // no node.js - ] - }, - // Common: vs/workbench/api/common/extHostExtensionService.ts - { - target: '**/vs/workbench/api/common/extHostExtensionService.ts', - allowedTypes: [ - ...CORE_TYPES, - // Safe access to global - 'global' - ], - disallowedDefinitions: [ - 'lib.dom.d.ts', - '@types/node' // no node.js - ] - }, - // Common - { - target: '**/vs/**/common/**', - allowedTypes: CORE_TYPES, - disallowedDefinitions: [ - 'lib.dom.d.ts', - '@types/node' // no node.js - ] - }, - // Browser - { - target: '**/vs/**/browser/**', - allowedTypes: CORE_TYPES, - disallowedDefinitions: [ - '@types/node' // no node.js - ] - }, - // node.js - { - target: '**/vs/**/node/**', - allowedTypes: [ - ...CORE_TYPES, - // --> types from node.d.ts that duplicate from lib.dom.d.ts - 'URL', - 'protocol', - 'hostname', - 'port', - 'pathname', - 'search', - 'username', - 'password' - ], - disallowedDefinitions: [ - 'lib.dom.d.ts' // no DOM - ] - }, - // Electron (renderer): skip - { - target: '**/vs/**/electron-browser/**', - skip: true // -> supports all types - }, - // Electron (main) - { - target: '**/vs/**/electron-main/**', - allowedTypes: [ - ...CORE_TYPES, - // --> types from electron.d.ts that duplicate from lib.dom.d.ts - 'Event', - 'Request' - ], - disallowedDefinitions: [ - 'lib.dom.d.ts' // no DOM - ] - } -]; -const TS_CONFIG_PATH = path_1.join(__dirname, '../../', 'src', 'tsconfig.json'); -let hasErrors = false; -function checkFile(program, sourceFile, rule) { - checkNode(sourceFile); - function checkNode(node) { - var _a; - if (node.kind !== ts.SyntaxKind.Identifier) { - return ts.forEachChild(node, checkNode); // recurse down - } - const text = node.getText(sourceFile); - if ((_a = rule.allowedTypes) === null || _a === void 0 ? void 0 : _a.some(allowed => allowed === text)) { - return; // override - } - const checker = program.getTypeChecker(); - const symbol = checker.getSymbolAtLocation(node); - if (symbol) { - const declarations = symbol.declarations; - if (Array.isArray(declarations)) { - for (const declaration of declarations) { - if (declaration) { - const parent = declaration.parent; - if (parent) { - const parentSourceFile = parent.getSourceFile(); - if (parentSourceFile) { - const definitionFileName = parentSourceFile.fileName; - if (rule.disallowedDefinitions) { - for (const disallowedDefinition of rule.disallowedDefinitions) { - if (definitionFileName.indexOf(disallowedDefinition) >= 0) { - const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart()); - console.log(`[build/lib/layersChecker.ts]: Reference to '${text}' from '${disallowedDefinition}' violates layer '${rule.target}' (${sourceFile.fileName} (${line + 1},${character + 1})`); - hasErrors = true; - return; - } - } - } - } - } - } - } - } - } - } -} -function createProgram(tsconfigPath) { - const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); - const configHostParser = { fileExists: fs_1.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs_1.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; - const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path_1.resolve(path_1.dirname(tsconfigPath)), { noEmit: true }); - const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true); - return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); -} -// -// Create program and start checking -// -const program = createProgram(TS_CONFIG_PATH); -for (const sourceFile of program.getSourceFiles()) { - for (const rule of RULES) { - if (minimatch_1.match([sourceFile.fileName], rule.target).length > 0) { - if (!rule.skip) { - checkFile(program, sourceFile, rule); - } - break; - } - } -} -if (hasErrors) { - process.exit(1); -} diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 787ddf967ce..1c7579206af 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -25,8 +25,8 @@ import { match } from 'minimatch'; // Feel free to add more core types as you see needed if present in node.js and browsers const CORE_TYPES = [ 'require', // from our AMD loader - 'atob', - 'btoa', + // 'atob', + // 'btoa', 'setTimeout', 'clearTimeout', 'setInterval', @@ -39,6 +39,7 @@ const CORE_TYPES = [ 'group', 'groupEnd', 'table', + 'assert', 'Error', 'String', 'throws', @@ -54,6 +55,15 @@ const CORE_TYPES = [ 'trimRight' ]; +// Types that are defined in a common layer but are known to be only +// available in native environments should not be allowed in browser +const NATIVE_TYPES = [ + 'NativeParsedArgs', + 'INativeEnvironmentService', + 'INativeWindowConfiguration', + 'ICommonNativeHostService' +]; + const RULES = [ // Tests: skip @@ -72,6 +82,51 @@ const RULES = [ 'MessageEvent', 'data' ], + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Common: vs/platform/environment/common/argv.ts + { + target: '**/vs/platform/environment/common/argv.ts', + disallowedTypes: [/* Ignore native types that are defined from here */], + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Common: vs/platform/environment/common/environment.ts + { + target: '**/vs/platform/environment/common/environment.ts', + disallowedTypes: [/* Ignore native types that are defined from here */], + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Common: vs/platform/windows/common/windows.ts + { + target: '**/vs/platform/windows/common/windows.ts', + disallowedTypes: [/* Ignore native types that are defined from here */], + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Common: vs/platform/native/common/native.ts + { + target: '**/vs/platform/native/common/native.ts', + disallowedTypes: [/* Ignore native types that are defined from here */], + allowedTypes: CORE_TYPES, disallowedDefinitions: [ 'lib.dom.d.ts', // no DOM '@types/node' // no node.js @@ -87,6 +142,7 @@ const RULES = [ // Safe access to global 'global' ], + disallowedTypes: NATIVE_TYPES, disallowedDefinitions: [ 'lib.dom.d.ts', // no DOM '@types/node' // no node.js @@ -97,6 +153,7 @@ const RULES = [ { target: '**/vs/**/common/**', allowedTypes: CORE_TYPES, + disallowedTypes: NATIVE_TYPES, disallowedDefinitions: [ 'lib.dom.d.ts', // no DOM '@types/node' // no node.js @@ -107,6 +164,17 @@ const RULES = [ { target: '**/vs/**/browser/**', allowedTypes: CORE_TYPES, + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + '@types/node' // no node.js + ] + }, + + // Browser (editor contrib) + { + target: '**/src/vs/editor/contrib/**', + allowedTypes: CORE_TYPES, + disallowedTypes: NATIVE_TYPES, disallowedDefinitions: [ '@types/node' // no node.js ] @@ -133,6 +201,15 @@ const RULES = [ ] }, + // Electron (sandbox) + { + target: '**/vs/**/electron-sandbox/**', + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + '@types/node' // no node.js + ] + }, + // Electron (renderer): skip { target: '**/vs/**/electron-browser/**', @@ -162,6 +239,7 @@ interface IRule { skip?: boolean; allowedTypes?: string[]; disallowedDefinitions?: string[]; + disallowedTypes?: string[]; } let hasErrors = false; @@ -180,6 +258,14 @@ function checkFile(program: ts.Program, sourceFile: ts.SourceFile, rule: IRule) return; // override } + if (rule.disallowedTypes?.some(disallowed => disallowed === text)) { + const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart()); + console.log(`[build/lib/layersChecker.ts]: Reference to '${text}' violates layer '${rule.target}' (${sourceFile.fileName} (${line + 1},${character + 1})`); + + hasErrors = true; + return; + } + const checker = program.getTypeChecker(); const symbol = checker.getSymbolAtLocation(node); if (symbol) { diff --git a/build/monaco/api.ts b/build/lib/monaco-api.ts similarity index 98% rename from build/monaco/api.ts rename to build/lib/monaco-api.ts index 511768ee64b..6604b14d91e 100644 --- a/build/monaco/api.ts +++ b/build/lib/monaco-api.ts @@ -14,7 +14,7 @@ const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); -export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); +export const RECIPE_PATH = path.join(__dirname, '../monaco/monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message: any, ...rest: any[]): void { @@ -167,10 +167,11 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati result = result.replace(memberText, ''); } else { const memberName = (member.name).text; + const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`); if (isStatic(member)) { - usage.push(`a = ${staticTypeName}.${memberName};`); + usage.push(`a = ${staticTypeName}${memberAccess};`); } else { - usage.push(`a = (<${instanceTypeName}>b).${memberName};`); + usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`); } } } catch (err) { diff --git a/build/lib/nls.js b/build/lib/nls.js deleted file mode 100644 index 3807e90a612..00000000000 --- a/build/lib/nls.js +++ /dev/null @@ -1,354 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -const ts = require("typescript"); -const lazy = require("lazy.js"); -const event_stream_1 = require("event-stream"); -const File = require("vinyl"); -const sm = require("source-map"); -const path = require("path"); -var CollectStepResult; -(function (CollectStepResult) { - CollectStepResult[CollectStepResult["Yes"] = 0] = "Yes"; - CollectStepResult[CollectStepResult["YesAndRecurse"] = 1] = "YesAndRecurse"; - CollectStepResult[CollectStepResult["No"] = 2] = "No"; - CollectStepResult[CollectStepResult["NoAndRecurse"] = 3] = "NoAndRecurse"; -})(CollectStepResult || (CollectStepResult = {})); -function collect(node, fn) { - const result = []; - function loop(node) { - const stepResult = fn(node); - if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) { - result.push(node); - } - if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) { - ts.forEachChild(node, loop); - } - } - loop(node); - return result; -} -function clone(object) { - const result = {}; - for (const id in object) { - result[id] = object[id]; - } - return result; -} -function template(lines) { - let indent = '', wrap = ''; - if (lines.length > 1) { - indent = '\t'; - wrap = '\n'; - } - return `/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ -define([], [${wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`; -} -/** - * Returns a stream containing the patched JavaScript and source maps. - */ -function nls() { - const input = event_stream_1.through(); - const output = input.pipe(event_stream_1.through(function (f) { - if (!f.sourceMap) { - return this.emit('error', new Error(`File ${f.relative} does not have sourcemaps.`)); - } - let source = f.sourceMap.sources[0]; - if (!source) { - return this.emit('error', new Error(`File ${f.relative} does not have a source in the source map.`)); - } - const root = f.sourceMap.sourceRoot; - if (root) { - source = path.join(root, source); - } - const typescript = f.sourceMap.sourcesContent[0]; - if (!typescript) { - return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`)); - } - nls.patchFiles(f, typescript).forEach(f => this.emit('data', f)); - })); - return event_stream_1.duplex(input, output); -} -function isImportNode(node) { - return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration; -} -(function (nls_1) { - function fileFrom(file, contents, path = file.path) { - return new File({ - contents: Buffer.from(contents), - base: file.base, - cwd: file.cwd, - path: path - }); - } - nls_1.fileFrom = fileFrom; - function mappedPositionFrom(source, lc) { - return { source, line: lc.line + 1, column: lc.character }; - } - nls_1.mappedPositionFrom = mappedPositionFrom; - function lcFrom(position) { - return { line: position.line - 1, character: position.column }; - } - nls_1.lcFrom = lcFrom; - class SingleFileServiceHost { - constructor(options, filename, contents) { - this.options = options; - this.filename = filename; - this.getCompilationSettings = () => this.options; - this.getScriptFileNames = () => [this.filename]; - this.getScriptVersion = () => '1'; - this.getScriptSnapshot = (name) => name === this.filename ? this.file : this.lib; - this.getCurrentDirectory = () => ''; - this.getDefaultLibFileName = () => 'lib.d.ts'; - this.file = ts.ScriptSnapshot.fromString(contents); - this.lib = ts.ScriptSnapshot.fromString(''); - } - } - nls_1.SingleFileServiceHost = SingleFileServiceHost; - function isCallExpressionWithinTextSpanCollectStep(textSpan, node) { - if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) { - return CollectStepResult.No; - } - return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; - } - function analyze(contents, options = {}) { - const filename = 'file.ts'; - const serviceHost = new SingleFileServiceHost(Object.assign(clone(options), { noResolve: true }), filename, contents); - const service = ts.createLanguageService(serviceHost); - const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true); - // all imports - const imports = lazy(collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse)); - // import nls = require('vs/nls'); - const importEqualsDeclarations = imports - .filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration) - .map(n => n) - .filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) - .filter(d => d.moduleReference.expression.getText() === '\'vs/nls\''); - // import ... from 'vs/nls'; - const importDeclarations = imports - .filter(n => n.kind === ts.SyntaxKind.ImportDeclaration) - .map(n => n) - .filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) - .filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'') - .filter(d => !!d.importClause && !!d.importClause.namedBindings); - const nlsExpressions = importEqualsDeclarations - .map(d => d.moduleReference.expression) - .concat(importDeclarations.map(d => d.moduleSpecifier)) - .map(d => ({ - start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), - end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) - })); - // `nls.localize(...)` calls - const nlsLocalizeCallExpressions = importDeclarations - .filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport)) - .map(d => d.importClause.namedBindings.name) - .concat(importEqualsDeclarations.map(d => d.name)) - // find read-only references to `nls` - .map(n => service.getReferencesAtPosition(filename, n.pos + 1)) - .flatten() - .filter(r => !r.isWriteAccess) - // find the deepest call expressions AST nodes that contain those references - .map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n))) - .map(a => lazy(a).last()) - .filter(n => !!n) - .map(n => n) - // only `localize` calls - .filter(n => n.expression.kind === ts.SyntaxKind.PropertyAccessExpression && n.expression.name.getText() === 'localize'); - // `localize` named imports - const allLocalizeImportDeclarations = importDeclarations - .filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamedImports)) - .map(d => [].concat(d.importClause.namedBindings.elements)) - .flatten(); - // `localize` read-only references - const localizeReferences = allLocalizeImportDeclarations - .filter(d => d.name.getText() === 'localize') - .map(n => service.getReferencesAtPosition(filename, n.pos + 1)) - .flatten() - .filter(r => !r.isWriteAccess); - // custom named `localize` read-only references - const namedLocalizeReferences = allLocalizeImportDeclarations - .filter(d => d.propertyName && d.propertyName.getText() === 'localize') - .map(n => service.getReferencesAtPosition(filename, n.name.pos + 1)) - .flatten() - .filter(r => !r.isWriteAccess); - // find the deepest call expressions AST nodes that contain those references - const localizeCallExpressions = localizeReferences - .concat(namedLocalizeReferences) - .map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n))) - .map(a => lazy(a).last()) - .filter(n => !!n) - .map(n => n); - // collect everything - const localizeCalls = nlsLocalizeCallExpressions - .concat(localizeCallExpressions) - .map(e => e.arguments) - .filter(a => a.length > 1) - .sort((a, b) => a[0].getStart() - b[0].getStart()) - .map(a => ({ - keySpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getEnd()) }, - key: a[0].getText(), - valueSpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getEnd()) }, - value: a[1].getText() - })); - return { - localizeCalls: localizeCalls.toArray(), - nlsExpressions: nlsExpressions.toArray() - }; - } - nls_1.analyze = analyze; - class TextModel { - constructor(contents) { - const regex = /\r\n|\r|\n/g; - let index = 0; - let match; - this.lines = []; - this.lineEndings = []; - while (match = regex.exec(contents)) { - this.lines.push(contents.substring(index, match.index)); - this.lineEndings.push(match[0]); - index = regex.lastIndex; - } - if (contents.length > 0) { - this.lines.push(contents.substring(index, contents.length)); - this.lineEndings.push(''); - } - } - get(index) { - return this.lines[index]; - } - set(index, line) { - this.lines[index] = line; - } - get lineCount() { - return this.lines.length; - } - /** - * Applies patch(es) to the model. - * Multiple patches must be ordered. - * Does not support patches spanning multiple lines. - */ - apply(patch) { - const startLineNumber = patch.span.start.line; - const endLineNumber = patch.span.end.line; - const startLine = this.lines[startLineNumber] || ''; - const endLine = this.lines[endLineNumber] || ''; - this.lines[startLineNumber] = [ - startLine.substring(0, patch.span.start.character), - patch.content, - endLine.substring(patch.span.end.character) - ].join(''); - for (let i = startLineNumber + 1; i <= endLineNumber; i++) { - this.lines[i] = ''; - } - } - toString() { - return lazy(this.lines).zip(this.lineEndings) - .flatten().toArray().join(''); - } - } - nls_1.TextModel = TextModel; - function patchJavascript(patches, contents, moduleId) { - const model = new nls.TextModel(contents); - // patch the localize calls - lazy(patches).reverse().each(p => model.apply(p)); - // patch the 'vs/nls' imports - const firstLine = model.get(0); - const patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); - model.set(0, patchedFirstLine); - return model.toString(); - } - nls_1.patchJavascript = patchJavascript; - function patchSourcemap(patches, rsm, smc) { - const smg = new sm.SourceMapGenerator({ - file: rsm.file, - sourceRoot: rsm.sourceRoot - }); - patches = patches.reverse(); - let currentLine = -1; - let currentLineDiff = 0; - let source = null; - smc.eachMapping(m => { - const patch = patches[patches.length - 1]; - const original = { line: m.originalLine, column: m.originalColumn }; - const generated = { line: m.generatedLine, column: m.generatedColumn }; - if (currentLine !== generated.line) { - currentLineDiff = 0; - } - currentLine = generated.line; - generated.column += currentLineDiff; - if (patch && m.generatedLine - 1 === patch.span.end.line && m.generatedColumn === patch.span.end.character) { - const originalLength = patch.span.end.character - patch.span.start.character; - const modifiedLength = patch.content.length; - const lengthDiff = modifiedLength - originalLength; - currentLineDiff += lengthDiff; - generated.column += lengthDiff; - patches.pop(); - } - source = rsm.sourceRoot ? path.relative(rsm.sourceRoot, m.source) : m.source; - source = source.replace(/\\/g, '/'); - smg.addMapping({ source, name: m.name, original, generated }); - }, null, sm.SourceMapConsumer.GENERATED_ORDER); - if (source) { - smg.setSourceContent(source, smc.sourceContentFor(source)); - } - return JSON.parse(smg.toString()); - } - nls_1.patchSourcemap = patchSourcemap; - function patch(moduleId, typescript, javascript, sourcemap) { - const { localizeCalls, nlsExpressions } = analyze(typescript); - if (localizeCalls.length === 0) { - return { javascript, sourcemap }; - } - const nlsKeys = template(localizeCalls.map(lc => lc.key)); - const nls = template(localizeCalls.map(lc => lc.value)); - const smc = new sm.SourceMapConsumer(sourcemap); - const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); - let i = 0; - // build patches - const patches = lazy(localizeCalls) - .map(lc => ([ - { range: lc.keySpan, content: '' + (i++) }, - { range: lc.valueSpan, content: 'null' } - ])) - .flatten() - .map(c => { - const start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start))); - const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); - return { span: { start, end }, content: c.content }; - }) - .toArray(); - javascript = patchJavascript(patches, javascript, moduleId); - // since imports are not within the sourcemap information, - // we must do this MacGyver style - if (nlsExpressions.length) { - javascript = javascript.replace(/^define\(.*$/m, line => { - return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); - }); - } - sourcemap = patchSourcemap(patches, sourcemap, smc); - return { javascript, sourcemap, nlsKeys, nls }; - } - nls_1.patch = patch; - function patchFiles(javascriptFile, typescript) { - // hack? - const moduleId = javascriptFile.relative - .replace(/\.js$/, '') - .replace(/\\/g, '/'); - const { javascript, sourcemap, nlsKeys, nls } = patch(moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap); - const result = [fileFrom(javascriptFile, javascript)]; - result[0].sourceMap = sourcemap; - if (nlsKeys) { - result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); - } - if (nls) { - result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); - } - return result; - } - nls_1.patchFiles = patchFiles; -})(nls || (nls = {})); -module.exports = nls; diff --git a/build/lib/node.js b/build/lib/node.js deleted file mode 100644 index 403ae3d9657..00000000000 --- a/build/lib/node.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const fs = require("fs"); -const root = path.dirname(path.dirname(__dirname)); -const yarnrcPath = path.join(root, 'remote', '.yarnrc'); -const yarnrc = fs.readFileSync(yarnrcPath, 'utf8'); -const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)[1]; -const node = process.platform === 'win32' ? 'node.exe' : 'node'; -const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node); -console.log(nodePath); diff --git a/build/lib/optimize.js b/build/lib/optimize.js deleted file mode 100644 index 45f11698463..00000000000 --- a/build/lib/optimize.js +++ /dev/null @@ -1,278 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const fs = require("fs"); -const gulp = require("gulp"); -const concat = require("gulp-concat"); -const minifyCSS = require("gulp-cssnano"); -const filter = require("gulp-filter"); -const flatmap = require("gulp-flatmap"); -const sourcemaps = require("gulp-sourcemaps"); -const uglify = require("gulp-uglify"); -const composer = require("gulp-uglify/composer"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const path = require("path"); -const pump = require("pump"); -const terser = require("terser"); -const VinylFile = require("vinyl"); -const bundle = require("./bundle"); -const i18n_1 = require("./i18n"); -const stats_1 = require("./stats"); -const util = require("./util"); -const REPO_ROOT_PATH = path.join(__dirname, '../..'); -function log(prefix, message) { - fancyLog(ansiColors.cyan('[' + prefix + ']'), message); -} -function loaderConfig(emptyPaths) { - const result = { - paths: { - 'vs': 'out-build/vs', - 'vscode': 'empty:' - }, - nodeModules: emptyPaths || [] - }; - result['vs/css'] = { inlineResources: true }; - return result; -} -exports.loaderConfig = loaderConfig; -const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(src, bundledFileHeader, bundleLoader) { - let sources = [ - `${src}/vs/loader.js` - ]; - if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); - } - let isFirst = true; - return (gulp - .src(sources, { base: `${src}` }) - .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); - } - else { - this.emit('data', data); - } - })) - .pipe(util.loadSourcemaps()) - .pipe(concat('vs/loader.js')) - .pipe(es.mapSync(function (f) { - f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src')); - return f; - }))); -} -function toConcatStream(src, bundledFileHeader, sources, dest) { - const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); - // If a bundle ends up including in any of the sources our copyright, then - // insert a fake source at the beginning of each bundle with our copyright - let containsOurCopyright = false; - for (let i = 0, len = sources.length; i < len; i++) { - const fileContents = sources[i].contents; - if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { - containsOurCopyright = true; - break; - } - } - if (containsOurCopyright) { - sources.unshift({ - path: null, - contents: bundledFileHeader - }); - } - const treatedSources = sources.map(function (source) { - const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - const base = source.path ? root + `/${src}` : ''; - return new VinylFile({ - path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', - base: base, - contents: Buffer.from(source.contents) - }); - }); - return es.readArray(treatedSources) - .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) - .pipe(concat(dest)) - .pipe(stats_1.createStatsStream(dest)); -} -function toBundleStream(src, bundledFileHeader, bundles) { - return es.merge(bundles.map(function (bundle) { - return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); - })); -} -const DEFAULT_FILE_HEADER = [ - '/*!--------------------------------------------------------', - ' * Copyright (C) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' -].join('\n'); -function optimizeTask(opts) { - const src = opts.src; - const entryPoints = opts.entryPoints; - const resources = opts.resources; - const loaderConfig = opts.loaderConfig; - const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; - const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); - const out = opts.out; - return function () { - const bundlesStream = es.through(); // this stream will contain the bundled files - const resourcesStream = es.through(); // this stream will contain the resources - const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json - bundle.bundle(entryPoints, loaderConfig, function (err, result) { - if (err || !result) { - return bundlesStream.emit('error', JSON.stringify(err)); - } - if (opts.inlineAmdImages) { - try { - result = inlineAmdImages(src, result); - } - catch (err) { - return bundlesStream.emit('error', JSON.stringify(err)); - } - } - toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); - // Remove css inlined resources - const filteredResources = resources.slice(); - result.cssInlinedResources.forEach(function (resource) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log('optimizer', 'excluding inlined: ' + resource); - } - filteredResources.push('!' + resource); - }); - gulp.src(filteredResources, { base: `${src}`, allowEmpty: true }).pipe(resourcesStream); - const bundleInfoArray = []; - if (opts.bundleInfo) { - bundleInfoArray.push(new VinylFile({ - path: 'bundleInfo.json', - base: '.', - contents: Buffer.from(JSON.stringify(result.bundleData, null, '\t')) - })); - } - es.readArray(bundleInfoArray).pipe(bundleInfoStream); - }); - const result = es.merge(loader(src, bundledFileHeader, bundleLoader), bundlesStream, resourcesStream, bundleInfoStream); - return result - .pipe(sourcemaps.write('./', { - sourceRoot: undefined, - addComment: true, - includeContent: true - })) - .pipe(opts.languages && opts.languages.length ? i18n_1.processNlsFiles({ - fileHeader: bundledFileHeader, - languages: opts.languages - }) : es.through()) - .pipe(gulp.dest(out)); - }; -} -exports.optimizeTask = optimizeTask; -function inlineAmdImages(src, result) { - for (const outputFile of result.files) { - for (const sourceFile of outputFile.sources) { - if (sourceFile.path && /\.js$/.test(sourceFile.path)) { - sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => { - let imagePath = m0; - // remove `` or '' - if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`') - || (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) { - imagePath = imagePath.substr(1, imagePath.length - 2); - } - if (!/\.(png|svg)$/.test(imagePath)) { - console.log(`original: ${_}`); - return _; - } - const repoLocation = path.join(src, imagePath); - const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation); - if (!fs.existsSync(absoluteLocation)) { - const message = `Invalid amd image url in file ${sourceFile.path}: ${imagePath}`; - console.log(message); - throw new Error(message); - } - const fileContents = fs.readFileSync(absoluteLocation); - const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png'; - // Mark the file as inlined so we don't ship it by itself - result.cssInlinedResources.push(repoLocation); - return `("data:${mime};base64,${fileContents.toString('base64')}")`; - }); - } - } - } - return result; -} -/** - * Wrap around uglify and allow the preserveComments function - * to have a file "context" to include our copyright only once per file. - */ -function uglifyWithCopyrights() { - const preserveComments = (f) => { - return (_node, comment) => { - const text = comment.value; - const type = comment.type; - if (/@minifier_do_not_preserve/.test(text)) { - return false; - } - const isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text); - if (isOurCopyright) { - if (f.__hasOurCopyright) { - return false; - } - f.__hasOurCopyright = true; - return true; - } - if ('comment2' === type) { - // check for /*!. Note that text doesn't contain leading /* - return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text); - } - else if ('comment1' === type) { - return /license|copyright/i.test(text); - } - return false; - }; - }; - const minify = composer(terser); - const input = es.through(); - const output = input - .pipe(flatmap((stream, f) => { - return stream.pipe(minify({ - output: { - comments: preserveComments(f), - max_line_len: 1024 - } - })); - })); - return es.duplex(input, output); -} -function minifyTask(src, sourceMapBaseUrl) { - const sourceMappingURL = sourceMapBaseUrl ? ((f) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined; - return cb => { - const jsFilter = filter('**/*.js', { restore: true }); - const cssFilter = filter('**/*.css', { restore: true }); - pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.mapSources((sourcePath) => { - if (sourcePath === 'bootstrap-fork.js') { - return 'bootstrap-fork.orig.js'; - } - return sourcePath; - }), sourcemaps.write('./', { - sourceMappingURL, - sourceRoot: undefined, - includeContent: true, - addComment: true - }), gulp.dest(src + '-min'), (err) => { - if (err instanceof uglify.GulpUglifyError) { - console.error(`Uglify error in '${err.cause && err.cause.filename}'`); - } - cb(err); - }); - }; -} -exports.minifyTask = minifyTask; diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 46189514c30..86400889de8 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -6,7 +6,6 @@ 'use strict'; import * as es from 'event-stream'; -import * as fs from 'fs'; import * as gulp from 'gulp'; import * as concat from 'gulp-concat'; import * as minifyCSS from 'gulp-cssnano'; @@ -19,7 +18,6 @@ import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; import * as path from 'path'; import * as pump from 'pump'; -import * as sm from 'source-map'; import * as terser from 'terser'; import * as VinylFile from 'vinyl'; import * as bundle from './bundle'; @@ -33,13 +31,13 @@ function log(prefix: string, message: string): void { fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } -export function loaderConfig(emptyPaths?: string[]) { +export function loaderConfig() { const result: any = { paths: { 'vs': 'out-build/vs', 'vscode': 'empty:' }, - nodeModules: emptyPaths || [] + amdModulesPattern: /^vs\// }; result['vs/css'] = { inlineResources: true }; @@ -49,10 +47,6 @@ export function loaderConfig(emptyPaths?: string[]) { const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -declare class FileSourceMap extends VinylFile { - public sourceMap: sm.RawSourceMap; -} - function loader(src: string, bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWriteStream { let sources = [ `${src}/vs/loader.js` @@ -73,7 +67,7 @@ function loader(src: string, bundledFileHeader: string, bundleLoader: boolean): isFirst = false; this.emit('data', new VinylFile({ path: 'fake', - base: '', + base: '.', contents: Buffer.from(bundledFileHeader) })); this.emit('data', data); @@ -81,16 +75,11 @@ function loader(src: string, bundledFileHeader: string, bundleLoader: boolean): this.emit('data', data); } })) - .pipe(util.loadSourcemaps()) .pipe(concat('vs/loader.js')) - .pipe(es.mapSync(function (f) { - f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src')); - return f; - })) ); } -function toConcatStream(src: string, bundledFileHeader: string, sources: bundle.IFile[], dest: string): NodeJS.ReadWriteStream { +function toConcatStream(src: string, bundledFileHeader: string, sources: bundle.IFile[], dest: string, fileContentMapper: (contents: string, path: string) => string): NodeJS.ReadWriteStream { const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); // If a bundle ends up including in any of the sources our copyright, then @@ -113,12 +102,14 @@ function toConcatStream(src: string, bundledFileHeader: string, sources: bundle. const treatedSources = sources.map(function (source) { const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - const base = source.path ? root + `/${src}` : ''; + const base = source.path ? root + `/${src}` : '.'; + const path = source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake'; + const contents = source.path ? fileContentMapper(source.contents, path) : source.contents; return new VinylFile({ - path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', + path: path, base: base, - contents: Buffer.from(source.contents) + contents: Buffer.from(contents) }); }); @@ -128,9 +119,9 @@ function toConcatStream(src: string, bundledFileHeader: string, sources: bundle. .pipe(createStatsStream(dest)); } -function toBundleStream(src: string, bundledFileHeader: string, bundles: bundle.IConcatFile[]): NodeJS.ReadWriteStream { +function toBundleStream(src: string, bundledFileHeader: string, bundles: bundle.IConcatFile[], fileContentMapper: (contents: string, path: string) => string): NodeJS.ReadWriteStream { return es.merge(bundles.map(function (bundle) { - return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); + return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest, fileContentMapper); })); } @@ -160,10 +151,6 @@ export interface IOptimizeTaskOpts { * (emit bundleInfo.json file) */ bundleInfo: boolean; - /** - * replace calls to `registerAndGetAmdImageURL` with data uris - */ - inlineAmdImages: boolean; /** * (out folder name) */ @@ -172,6 +159,12 @@ export interface IOptimizeTaskOpts { * (out folder name) */ languages?: Language[]; + /** + * File contents interceptor + * @param contents The contens of the file + * @param path The absolute file path, always using `/`, even on Windows + */ + fileContentMapper?: (contents: string, path: string) => string; } const DEFAULT_FILE_HEADER = [ @@ -188,6 +181,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); const out = opts.out; + const fileContentMapper = opts.fileContentMapper || ((contents: string, _path: string) => contents); return function () { const bundlesStream = es.through(); // this stream will contain the bundled files @@ -197,15 +191,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr bundle.bundle(entryPoints, loaderConfig, function (err, result) { if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); } - if (opts.inlineAmdImages) { - try { - result = inlineAmdImages(src, result); - } catch (err) { - return bundlesStream.emit('error', JSON.stringify(err)); - } - } - - toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); + toBundleStream(src, bundledFileHeader, result.files, fileContentMapper).pipe(bundlesStream); // Remove css inlined resources const filteredResources = resources.slice(); @@ -249,42 +235,6 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr }; } -function inlineAmdImages(src: string, result: bundle.IBundleResult): bundle.IBundleResult { - for (const outputFile of result.files) { - for (const sourceFile of outputFile.sources) { - if (sourceFile.path && /\.js$/.test(sourceFile.path)) { - sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => { - let imagePath = m0; - // remove `` or '' - if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`') - || (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) { - imagePath = imagePath.substr(1, imagePath.length - 2); - } - if (!/\.(png|svg)$/.test(imagePath)) { - console.log(`original: ${_}`); - return _; - } - const repoLocation = path.join(src, imagePath); - const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation); - if (!fs.existsSync(absoluteLocation)) { - const message = `Invalid amd image url in file ${sourceFile.path}: ${imagePath}`; - console.log(message); - throw new Error(message); - } - const fileContents = fs.readFileSync(absoluteLocation); - const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png'; - - // Mark the file as inlined so we don't ship it by itself - result.cssInlinedResources.push(repoLocation); - - return `("data:${mime};base64,${fileContents.toString('base64')}")`; - }); - } - } - } - return result; -} - declare class FileWithCopyright extends VinylFile { public __hasOurCopyright: boolean; } diff --git a/build/lib/preLaunch.ts b/build/lib/preLaunch.ts new file mode 100644 index 00000000000..57441870cc4 --- /dev/null +++ b/build/lib/preLaunch.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +// @ts-check + +import * as path from 'path'; +import { spawn } from 'child_process'; +import { promises as fs } from 'fs'; + +const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; +const rootDir = path.resolve(__dirname, '..', '..'); + +function runProcess(command: string, args: ReadonlyArray = []) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { cwd: rootDir, stdio: 'inherit', env: process.env }); + child.on('exit', err => !err ? resolve() : process.exit(err ?? 1)); + child.on('error', reject); + }); +} + +async function exists(subdir: string) { + try { + await fs.stat(path.join(rootDir, subdir)); + return true; + } catch { + return false; + } +} + +async function ensureNodeModules() { + if (!(await exists('node_modules'))) { + await runProcess(yarn); + } +} + +async function getElectron() { + await runProcess(yarn, ['electron']); +} + +async function ensureCompiled() { + if (!(await exists('out'))) { + await runProcess(yarn, ['compile']); + } +} + +async function main() { + await ensureNodeModules(); + await getElectron(); + await ensureCompiled(); + + // Can't require this until after dependencies are installed + const { getBuiltInExtensions } = require('./builtInExtensions'); + await getBuiltInExtensions(); +} + +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/lib/reporter.js b/build/lib/reporter.js deleted file mode 100644 index e0461dc6d9d..00000000000 --- a/build/lib/reporter.js +++ /dev/null @@ -1,85 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const _ = require("underscore"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const fs = require("fs"); -const path = require("path"); -const allErrors = []; -let startTime = null; -let count = 0; -function onStart() { - if (count++ > 0) { - return; - } - startTime = new Date().getTime(); - fancyLog(`Starting ${ansiColors.green('compilation')}...`); -} -function onEnd() { - if (--count > 0) { - return; - } - log(); -} -const buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); -try { - fs.mkdirSync(path.dirname(buildLogPath)); -} -catch (err) { - // ignore -} -function log() { - const errors = _.flatten(allErrors); - const seen = new Set(); - errors.map(err => { - if (!seen.has(err)) { - seen.add(err); - fancyLog(`${ansiColors.red('Error')}: ${err}`); - } - }); - const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; - const messages = errors - .map(err => regex.exec(err)) - .filter(match => !!match) - .map(x => x) - .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); - try { - fs.writeFileSync(buildLogPath, JSON.stringify(messages)); - } - catch (err) { - //noop - } - fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime) + ' ms')}`); -} -function createReporter() { - const errors = []; - allErrors.push(errors); - const result = (err) => errors.push(err); - result.hasErrors = () => errors.length > 0; - result.end = (emitError) => { - errors.length = 0; - onStart(); - return es.through(undefined, function () { - onEnd(); - if (emitError && errors.length > 0) { - if (!errors.__logged__) { - log(); - } - errors.__logged__ = true; - const err = new Error(`Found ${errors.length} errors`); - err.__reporter__ = true; - this.emit('error', err); - } - else { - this.emit('end'); - } - }); - }; - return result; -} -exports.createReporter = createReporter; diff --git a/build/lib/snapshotLoader.js b/build/lib/snapshotLoader.js deleted file mode 100644 index ee626a0f7f1..00000000000 --- a/build/lib/snapshotLoader.js +++ /dev/null @@ -1,55 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var snaps; -(function (snaps) { - const fs = require('fs'); - const path = require('path'); - const os = require('os'); - const cp = require('child_process'); - const mksnapshot = path.join(__dirname, `../../node_modules/.bin/${process.platform === 'win32' ? 'mksnapshot.cmd' : 'mksnapshot'}`); - const product = require('../../product.json'); - const arch = (process.argv.join('').match(/--arch=(.*)/) || [])[1]; - // - let loaderFilepath; - let startupBlobFilepath; - switch (process.platform) { - case 'darwin': - loaderFilepath = `VSCode-darwin/${product.nameLong}.app/Contents/Resources/app/out/vs/loader.js`; - startupBlobFilepath = `VSCode-darwin/${product.nameLong}.app/Contents/Frameworks/Electron Framework.framework/Resources/snapshot_blob.bin`; - break; - case 'win32': - case 'linux': - loaderFilepath = `VSCode-${process.platform}-${arch}/resources/app/out/vs/loader.js`; - startupBlobFilepath = `VSCode-${process.platform}-${arch}/snapshot_blob.bin`; - break; - default: - throw new Error('Unknown platform'); - } - loaderFilepath = path.join(__dirname, '../../../', loaderFilepath); - startupBlobFilepath = path.join(__dirname, '../../../', startupBlobFilepath); - snapshotLoader(loaderFilepath, startupBlobFilepath); - function snapshotLoader(loaderFilepath, startupBlobFilepath) { - const inputFile = fs.readFileSync(loaderFilepath); - const wrappedInputFile = ` - var Monaco_Loader_Init; - (function() { - var doNotInitLoader = true; - ${inputFile.toString()}; - Monaco_Loader_Init = function() { - AMDLoader.init(); - CSSLoaderPlugin.init(); - NLSLoaderPlugin.init(); - - return { define, require }; - } - })(); - `; - const wrappedInputFilepath = path.join(os.tmpdir(), 'wrapped-loader.js'); - console.log(wrappedInputFilepath); - fs.writeFileSync(wrappedInputFilepath, wrappedInputFile); - cp.execFileSync(mksnapshot, [wrappedInputFilepath, `--startup_blob`, startupBlobFilepath]); - } -})(snaps || (snaps = {})); diff --git a/build/lib/standalone.js b/build/lib/standalone.js deleted file mode 100644 index ccfd7b45d23..00000000000 --- a/build/lib/standalone.js +++ /dev/null @@ -1,313 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const ts = require("typescript"); -const fs = require("fs"); -const path = require("path"); -const tss = require("./treeshaking"); -const REPO_ROOT = path.join(__dirname, '../../'); -const SRC_DIR = path.join(REPO_ROOT, 'src'); -let dirCache = {}; -function writeFile(filePath, contents) { - function ensureDirs(dirPath) { - if (dirCache[dirPath]) { - return; - } - dirCache[dirPath] = true; - ensureDirs(path.dirname(dirPath)); - if (fs.existsSync(dirPath)) { - return; - } - fs.mkdirSync(dirPath); - } - ensureDirs(path.dirname(filePath)); - fs.writeFileSync(filePath, contents); -} -function extractEditor(options) { - const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); - let compilerOptions; - if (tsConfig.extends) { - compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); - delete tsConfig.extends; - } - else { - compilerOptions = tsConfig.compilerOptions; - } - tsConfig.compilerOptions = compilerOptions; - compilerOptions.noEmit = false; - compilerOptions.noUnusedLocals = false; - compilerOptions.preserveConstEnums = false; - compilerOptions.declaration = false; - compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; - options.compilerOptions = compilerOptions; - console.log(`Running tree shaker with shakeLevel ${tss.toStringShakeLevel(options.shakeLevel)}`); - // Take the extra included .d.ts files from `tsconfig.monaco.json` - options.typings = tsConfig.include.filter(includedFile => /\.d\.ts$/.test(includedFile)); - let result = tss.shake(options); - for (let fileName in result) { - if (result.hasOwnProperty(fileName)) { - writeFile(path.join(options.destRoot, fileName), result[fileName]); - } - } - let copied = {}; - const copyFile = (fileName) => { - if (copied[fileName]) { - return; - } - copied[fileName] = true; - const srcPath = path.join(options.sourcesRoot, fileName); - const dstPath = path.join(options.destRoot, fileName); - writeFile(dstPath, fs.readFileSync(srcPath)); - }; - const writeOutputFile = (fileName, contents) => { - writeFile(path.join(options.destRoot, fileName), contents); - }; - for (let fileName in result) { - if (result.hasOwnProperty(fileName)) { - const fileContents = result[fileName]; - const info = ts.preProcessFile(fileContents); - for (let i = info.importedFiles.length - 1; i >= 0; i--) { - const importedFileName = info.importedFiles[i].fileName; - let importedFilePath; - if (/^vs\/css!/.test(importedFileName)) { - importedFilePath = importedFileName.substr('vs/css!'.length) + '.css'; - } - else { - importedFilePath = importedFileName; - } - if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) { - importedFilePath = path.join(path.dirname(fileName), importedFilePath); - } - if (/\.css$/.test(importedFilePath)) { - transportCSS(importedFilePath, copyFile, writeOutputFile); - } - else { - if (fs.existsSync(path.join(options.sourcesRoot, importedFilePath + '.js'))) { - copyFile(importedFilePath + '.js'); - } - } - } - } - } - delete tsConfig.compilerOptions.moduleResolution; - writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); - [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', - 'vs/loader.js', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', - 'vs/nls.mock.ts', - ].forEach(copyFile); -} -exports.extractEditor = extractEditor; -function createESMSourcesAndResources2(options) { - const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder); - const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); - const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); - const getDestAbsoluteFilePath = (file) => { - let dest = options.renames[file.replace(/\\/g, '/')] || file; - if (dest === 'tsconfig.json') { - return path.join(OUT_FOLDER, `tsconfig.json`); - } - if (/\.ts$/.test(dest)) { - return path.join(OUT_FOLDER, dest); - } - return path.join(OUT_RESOURCES_FOLDER, dest); - }; - const allFiles = walkDirRecursive(SRC_FOLDER); - for (const file of allFiles) { - if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { - continue; - } - if (file === 'tsconfig.json') { - const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); - tsConfig.compilerOptions.module = 'es6'; - tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); - write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); - continue; - } - if (/\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file) || /\.ttf$/.test(file)) { - // Transport the files directly - write(getDestAbsoluteFilePath(file), fs.readFileSync(path.join(SRC_FOLDER, file))); - continue; - } - if (/\.ts$/.test(file)) { - // Transform the .ts file - let fileContents = fs.readFileSync(path.join(SRC_FOLDER, file)).toString(); - const info = ts.preProcessFile(fileContents); - for (let i = info.importedFiles.length - 1; i >= 0; i--) { - const importedFilename = info.importedFiles[i].fileName; - const pos = info.importedFiles[i].pos; - const end = info.importedFiles[i].end; - let importedFilepath; - if (/^vs\/css!/.test(importedFilename)) { - importedFilepath = importedFilename.substr('vs/css!'.length) + '.css'; - } - else { - importedFilepath = importedFilename; - } - if (/(^\.\/)|(^\.\.\/)/.test(importedFilepath)) { - importedFilepath = path.join(path.dirname(file), importedFilepath); - } - let relativePath; - if (importedFilepath === path.dirname(file).replace(/\\/g, '/')) { - relativePath = '../' + path.basename(path.dirname(file)); - } - else if (importedFilepath === path.dirname(path.dirname(file)).replace(/\\/g, '/')) { - relativePath = '../../' + path.basename(path.dirname(path.dirname(file))); - } - else { - relativePath = path.relative(path.dirname(file), importedFilepath); - } - relativePath = relativePath.replace(/\\/g, '/'); - if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { - relativePath = './' + relativePath; - } - fileContents = (fileContents.substring(0, pos + 1) - + relativePath - + fileContents.substring(end + 1)); - } - fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { - return `import * as ${m1} from ${m2};`; - }); - write(getDestAbsoluteFilePath(file), fileContents); - continue; - } - console.log(`UNKNOWN FILE: ${file}`); - } - function walkDirRecursive(dir) { - if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { - dir += '/'; - } - let result = []; - _walkDirRecursive(dir, result, dir.length); - return result; - } - function _walkDirRecursive(dir, result, trimPos) { - const files = fs.readdirSync(dir); - for (let i = 0; i < files.length; i++) { - const file = path.join(dir, files[i]); - if (fs.statSync(file).isDirectory()) { - _walkDirRecursive(file, result, trimPos); - } - else { - result.push(file.substr(trimPos)); - } - } - } - function write(absoluteFilePath, contents) { - if (/(\.ts$)|(\.js$)/.test(absoluteFilePath)) { - contents = toggleComments(contents.toString()); - } - writeFile(absoluteFilePath, contents); - function toggleComments(fileContents) { - let lines = fileContents.split(/\r\n|\r|\n/); - let mode = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (mode === 0) { - if (/\/\/ ESM-comment-begin/.test(line)) { - mode = 1; - continue; - } - if (/\/\/ ESM-uncomment-begin/.test(line)) { - mode = 2; - continue; - } - continue; - } - if (mode === 1) { - if (/\/\/ ESM-comment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = '// ' + line; - continue; - } - if (mode === 2) { - if (/\/\/ ESM-uncomment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { - return indent; - }); - } - } - return lines.join('\n'); - } - } -} -exports.createESMSourcesAndResources2 = createESMSourcesAndResources2; -function transportCSS(module, enqueue, write) { - if (!/\.css/.test(module)) { - return false; - } - const filename = path.join(SRC_DIR, module); - const fileContents = fs.readFileSync(filename).toString(); - const inlineResources = 'base64'; // see https://github.com/Microsoft/monaco-editor/issues/148 - const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64'); - write(module, newContents); - return true; - function _rewriteOrInlineUrls(contents, forceBase64) { - return _replaceURL(contents, (url) => { - const fontMatch = url.match(/^(.*).ttf\?(.*)$/); - if (fontMatch) { - const relativeFontPath = `${fontMatch[1]}.ttf`; // trim the query parameter - const fontPath = path.join(path.dirname(module), relativeFontPath); - enqueue(fontPath); - return relativeFontPath; - } - const imagePath = path.join(path.dirname(module), url); - const fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); - const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; - let DATA = ';base64,' + fileContents.toString('base64'); - if (!forceBase64 && /\.svg$/.test(url)) { - // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - let newText = fileContents.toString() - .replace(/"/g, '\'') - .replace(//g, '%3E') - .replace(/&/g, '%26') - .replace(/#/g, '%23') - .replace(/\s+/g, ' '); - let encodedData = ',' + newText; - if (encodedData.length < DATA.length) { - DATA = encodedData; - } - } - return '"data:' + MIME + DATA + '"'; - }); - } - function _replaceURL(contents, replacer) { - // Use ")" as the terminator as quotes are oftentimes not used at all - return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_, ...matches) => { - let url = matches[0]; - // Eliminate starting quotes (the initial whitespace is not captured) - if (url.charAt(0) === '"' || url.charAt(0) === '\'') { - url = url.substring(1); - } - // The ending whitespace is captured - while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { - url = url.substring(0, url.length - 1); - } - // Eliminate ending quotes - if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { - url = url.substring(0, url.length - 1); - } - if (!_startsWith(url, 'data:') && !_startsWith(url, 'http://') && !_startsWith(url, 'https://')) { - url = replacer(url); - } - return 'url(' + url + ')'; - }); - } - function _startsWith(haystack, needle) { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; - } -} diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index f80c611e6fc..51868c22fc5 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -55,6 +55,13 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str // Take the extra included .d.ts files from `tsconfig.monaco.json` options.typings = (tsConfig.include).filter(includedFile => /\.d\.ts$/.test(includedFile)); + // Add extra .d.ts files from `node_modules/@types/` + if (Array.isArray(options.compilerOptions?.types)) { + options.compilerOptions.types.forEach((type: string) => { + options.typings.push(`../node_modules/@types/${type}/index.d.ts`); + }); + } + let result = tss.shake(options); for (let fileName in result) { if (result.hasOwnProperty(fileName)) { @@ -292,7 +299,7 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: const filename = path.join(SRC_DIR, module); const fileContents = fs.readFileSync(filename).toString(); - const inlineResources = 'base64'; // see https://github.com/Microsoft/monaco-editor/issues/148 + const inlineResources = 'base64'; // see https://github.com/microsoft/monaco-editor/issues/148 const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64'); write(module, newContents); diff --git a/build/lib/stats.js b/build/lib/stats.js deleted file mode 100644 index 99ad665f223..00000000000 --- a/build/lib/stats.js +++ /dev/null @@ -1,136 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const appInsights = require("applicationinsights"); -class Entry { - constructor(name, totalCount, totalSize) { - this.name = name; - this.totalCount = totalCount; - this.totalSize = totalSize; - } - toString(pretty) { - if (!pretty) { - if (this.totalCount === 1) { - return `${this.name}: ${this.totalSize} bytes`; - } - else { - return `${this.name}: ${this.totalCount} files with ${this.totalSize} bytes`; - } - } - else { - if (this.totalCount === 1) { - return `Stats for '${ansiColors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; - } - else { - const count = this.totalCount < 100 - ? ansiColors.green(this.totalCount.toString()) - : ansiColors.red(this.totalCount.toString()); - return `Stats for '${ansiColors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; - } - } - } -} -const _entries = new Map(); -function createStatsStream(group, log) { - const entry = new Entry(group, 0, 0); - _entries.set(entry.name, entry); - return es.through(function (data) { - const file = data; - if (typeof file.path === 'string') { - entry.totalCount += 1; - if (Buffer.isBuffer(file.contents)) { - entry.totalSize += file.contents.length; - } - else if (file.stat && typeof file.stat.size === 'number') { - entry.totalSize += file.stat.size; - } - else { - // funky file... - } - } - this.emit('data', data); - }, function () { - if (log) { - if (entry.totalCount === 1) { - fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); - } - else { - const count = entry.totalCount < 100 - ? ansiColors.green(entry.totalCount.toString()) - : ansiColors.red(entry.totalCount.toString()); - fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); - } - } - this.emit('end'); - }); -} -exports.createStatsStream = createStatsStream; -function submitAllStats(productJson, commit) { - const sorted = []; - // move entries for single files to the front - _entries.forEach(value => { - if (value.totalCount === 1) { - sorted.unshift(value); - } - else { - sorted.push(value); - } - }); - // print to console - for (const entry of sorted) { - console.log(entry.toString(true)); - } - // send data as telementry event when the - // product is configured to send telemetry - if (!productJson || !productJson.aiConfig || typeof productJson.aiConfig.asimovKey !== 'string') { - return Promise.resolve(false); - } - return new Promise(resolve => { - try { - const sizes = {}; - const counts = {}; - for (const entry of sorted) { - sizes[entry.name] = entry.totalSize; - counts[entry.name] = entry.totalCount; - } - appInsights.setup(productJson.aiConfig.asimovKey) - .setAutoCollectConsole(false) - .setAutoCollectExceptions(false) - .setAutoCollectPerformance(false) - .setAutoCollectRequests(false) - .setAutoCollectDependencies(false) - .setAutoDependencyCorrelation(false) - .start(); - appInsights.defaultClient.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1'; - /* __GDPR__ - "monacoworkbench/packagemetrics" : { - "commit" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "size" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "count" : {"classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - appInsights.defaultClient.trackEvent({ - name: 'monacoworkbench/packagemetrics', - properties: { commit, size: JSON.stringify(sizes), count: JSON.stringify(counts) } - }); - appInsights.defaultClient.flush({ - callback: () => { - appInsights.dispose(); - resolve(true); - } - }); - } - catch (err) { - console.error('ERROR sending build stats as telemetry event!'); - console.error(err); - resolve(false); - } - }); -} -exports.submitAllStats = submitAllStats; diff --git a/build/lib/task.js b/build/lib/task.js deleted file mode 100644 index f1e6e3f6245..00000000000 --- a/build/lib/task.js +++ /dev/null @@ -1,96 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -function _isPromise(p) { - if (typeof p.then === 'function') { - return true; - } - return false; -} -function _renderTime(time) { - return `${Math.round(time)} ms`; -} -async function _execute(task) { - const name = task.taskName || task.displayName || ``; - if (!task._tasks) { - fancyLog('Starting', ansiColors.cyan(name), '...'); - } - const startTime = process.hrtime(); - await _doExecute(task); - const elapsedArr = process.hrtime(startTime); - const elapsedNanoseconds = (elapsedArr[0] * 1e9 + elapsedArr[1]); - if (!task._tasks) { - fancyLog(`Finished`, ansiColors.cyan(name), 'after', ansiColors.magenta(_renderTime(elapsedNanoseconds / 1e6))); - } -} -async function _doExecute(task) { - // Always invoke as if it were a callback task - return new Promise((resolve, reject) => { - if (task.length === 1) { - // this is a callback task - task((err) => { - if (err) { - return reject(err); - } - resolve(); - }); - return; - } - const taskResult = task(); - if (typeof taskResult === 'undefined') { - // this is a sync task - resolve(); - return; - } - if (_isPromise(taskResult)) { - // this is a promise returning task - taskResult.then(resolve, reject); - return; - } - // this is a stream returning task - taskResult.on('end', _ => resolve()); - taskResult.on('error', err => reject(err)); - }); -} -function series(...tasks) { - const result = async () => { - for (let i = 0; i < tasks.length; i++) { - await _execute(tasks[i]); - } - }; - result._tasks = tasks; - return result; -} -exports.series = series; -function parallel(...tasks) { - const result = async () => { - await Promise.all(tasks.map(t => _execute(t))); - }; - result._tasks = tasks; - return result; -} -exports.parallel = parallel; -function define(name, task) { - if (task._tasks) { - // This is a composite task - const lastTask = task._tasks[task._tasks.length - 1]; - if (lastTask._tasks || lastTask.taskName) { - // This is a composite task without a real task function - // => generate a fake task function - return define(name, series(task, () => Promise.resolve())); - } - lastTask.taskName = name; - task.displayName = name; - return task; - } - // This is a simple task - task.taskName = name; - task.displayName = name; - return task; -} -exports.define = define; diff --git a/build/lib/test/i18n.test.js b/build/lib/test/i18n.test.js deleted file mode 100644 index 3dd104259fa..00000000000 --- a/build/lib/test/i18n.test.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const assert = require("assert"); -const i18n = require("../i18n"); -suite('XLF Parser Tests', () => { - const sampleXlf = 'Key #1Key #2 &'; - const sampleTranslatedXlf = 'Key #1Кнопка #1Key #2 &Кнопка #2 &'; - const originalFilePath = 'vs/base/common/keybinding'; - const keys = ['key1', 'key2']; - const messages = ['Key #1', 'Key #2 &']; - const translatedMessages = { key1: 'Кнопка #1', key2: 'Кнопка #2 &' }; - test('Keys & messages to XLF conversion', () => { - const xlf = new i18n.XLF('vscode-workbench'); - xlf.addFile(originalFilePath, keys, messages); - const xlfString = xlf.toString(); - assert.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); - }); - test('XLF to keys & messages conversion', () => { - i18n.XLF.parse(sampleTranslatedXlf).then(function (resolvedFiles) { - assert.deepEqual(resolvedFiles[0].messages, translatedMessages); - assert.strictEqual(resolvedFiles[0].originalFilePath, originalFilePath); - }); - }); - test('JSON file source path to Transifex resource match', () => { - const editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench'; - const platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/textfile', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; - assert.deepEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); - assert.deepEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); - assert.deepEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); - assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); - assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); - assert.deepEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); - assert.deepEqual(i18n.getResource('vs/workbench/services/textfile/node/testFileService'), workbenchServices); - assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); - }); -}); diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js deleted file mode 100644 index 65dd88f585c..00000000000 --- a/build/lib/treeshaking.js +++ /dev/null @@ -1,689 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const ts = require("typescript"); -const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); -var ShakeLevel; -(function (ShakeLevel) { - ShakeLevel[ShakeLevel["Files"] = 0] = "Files"; - ShakeLevel[ShakeLevel["InnerFile"] = 1] = "InnerFile"; - ShakeLevel[ShakeLevel["ClassMembers"] = 2] = "ClassMembers"; -})(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {})); -function toStringShakeLevel(shakeLevel) { - switch (shakeLevel) { - case 0 /* Files */: - return 'Files (0)'; - case 1 /* InnerFile */: - return 'InnerFile (1)'; - case 2 /* ClassMembers */: - return 'ClassMembers (2)'; - } -} -exports.toStringShakeLevel = toStringShakeLevel; -function printDiagnostics(options, diagnostics) { - for (const diag of diagnostics) { - let result = ''; - if (diag.file) { - result += `${path.join(options.sourcesRoot, diag.file.fileName)}`; - } - if (diag.file && diag.start) { - let location = diag.file.getLineAndCharacterOfPosition(diag.start); - result += `:${location.line + 1}:${location.character}`; - } - result += ` - ` + JSON.stringify(diag.messageText); - console.log(result); - } -} -function shake(options) { - const languageService = createTypeScriptLanguageService(options); - const program = languageService.getProgram(); - const globalDiagnostics = program.getGlobalDiagnostics(); - if (globalDiagnostics.length > 0) { - printDiagnostics(options, globalDiagnostics); - throw new Error(`Compilation Errors encountered.`); - } - const syntacticDiagnostics = program.getSyntacticDiagnostics(); - if (syntacticDiagnostics.length > 0) { - printDiagnostics(options, syntacticDiagnostics); - throw new Error(`Compilation Errors encountered.`); - } - const semanticDiagnostics = program.getSemanticDiagnostics(); - if (semanticDiagnostics.length > 0) { - printDiagnostics(options, semanticDiagnostics); - throw new Error(`Compilation Errors encountered.`); - } - markNodes(languageService, options); - return generateResult(languageService, options.shakeLevel); -} -exports.shake = shake; -//#region Discovery, LanguageService & Setup -function createTypeScriptLanguageService(options) { - // Discover referenced files - const FILES = discoverAndReadFiles(options); - // Add fake usage files - options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { - FILES[`inlineEntryPoint.${index}.ts`] = inlineEntryPoint; - }); - // Add additional typings - options.typings.forEach((typing) => { - const filePath = path.join(options.sourcesRoot, typing); - FILES[typing] = fs.readFileSync(filePath).toString(); - }); - // Resolve libs - const RESOLVED_LIBS = {}; - options.libs.forEach((filename) => { - const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); - RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); - }); - const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; - const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions); - return ts.createLanguageService(host); -} -/** - * Read imports and follow them until all files have been handled - */ -function discoverAndReadFiles(options) { - const FILES = {}; - const in_queue = Object.create(null); - const queue = []; - const enqueue = (moduleId) => { - if (in_queue[moduleId]) { - return; - } - in_queue[moduleId] = true; - queue.push(moduleId); - }; - options.entryPoints.forEach((entryPoint) => enqueue(entryPoint)); - while (queue.length > 0) { - const moduleId = queue.shift(); - const dts_filename = path.join(options.sourcesRoot, moduleId + '.d.ts'); - if (fs.existsSync(dts_filename)) { - const dts_filecontents = fs.readFileSync(dts_filename).toString(); - FILES[`${moduleId}.d.ts`] = dts_filecontents; - continue; - } - const js_filename = path.join(options.sourcesRoot, moduleId + '.js'); - if (fs.existsSync(js_filename)) { - // This is an import for a .js file, so ignore it... - continue; - } - let ts_filename; - if (options.redirects[moduleId]) { - ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); - } - else { - ts_filename = path.join(options.sourcesRoot, moduleId + '.ts'); - } - const ts_filecontents = fs.readFileSync(ts_filename).toString(); - const info = ts.preProcessFile(ts_filecontents); - for (let i = info.importedFiles.length - 1; i >= 0; i--) { - const importedFileName = info.importedFiles[i].fileName; - if (options.importIgnorePattern.test(importedFileName)) { - // Ignore vs/css! imports - continue; - } - let importedModuleId = importedFileName; - if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { - importedModuleId = path.join(path.dirname(moduleId), importedModuleId); - } - enqueue(importedModuleId); - } - FILES[`${moduleId}.ts`] = ts_filecontents; - } - return FILES; -} -/** - * A TypeScript language service host - */ -class TypeScriptLanguageServiceHost { - constructor(libs, files, compilerOptions) { - this._libs = libs; - this._files = files; - this._compilerOptions = compilerOptions; - } - // --- language service host --------------- - getCompilationSettings() { - return this._compilerOptions; - } - getScriptFileNames() { - return ([] - .concat(Object.keys(this._libs)) - .concat(Object.keys(this._files))); - } - getScriptVersion(_fileName) { - return '1'; - } - getProjectVersion() { - return '1'; - } - getScriptSnapshot(fileName) { - if (this._files.hasOwnProperty(fileName)) { - return ts.ScriptSnapshot.fromString(this._files[fileName]); - } - else if (this._libs.hasOwnProperty(fileName)) { - return ts.ScriptSnapshot.fromString(this._libs[fileName]); - } - else { - return ts.ScriptSnapshot.fromString(''); - } - } - getScriptKind(_fileName) { - return ts.ScriptKind.TS; - } - getCurrentDirectory() { - return ''; - } - getDefaultLibFileName(_options) { - return 'defaultLib:lib.d.ts'; - } - isDefaultLibFileName(fileName) { - return fileName === this.getDefaultLibFileName(this._compilerOptions); - } -} -//#endregion -//#region Tree Shaking -var NodeColor; -(function (NodeColor) { - NodeColor[NodeColor["White"] = 0] = "White"; - NodeColor[NodeColor["Gray"] = 1] = "Gray"; - NodeColor[NodeColor["Black"] = 2] = "Black"; -})(NodeColor || (NodeColor = {})); -function getColor(node) { - return node.$$$color || 0 /* White */; -} -function setColor(node, color) { - node.$$$color = color; -} -function nodeOrParentIsBlack(node) { - while (node) { - const color = getColor(node); - if (color === 2 /* Black */) { - return true; - } - node = node.parent; - } - return false; -} -function nodeOrChildIsBlack(node) { - if (getColor(node) === 2 /* Black */) { - return true; - } - for (const child of node.getChildren()) { - if (nodeOrChildIsBlack(child)) { - return true; - } - } - return false; -} -function markNodes(languageService, options) { - const program = languageService.getProgram(); - if (!program) { - throw new Error('Could not get program from language service'); - } - if (options.shakeLevel === 0 /* Files */) { - // Mark all source files Black - program.getSourceFiles().forEach((sourceFile) => { - setColor(sourceFile, 2 /* Black */); - }); - return; - } - const black_queue = []; - const gray_queue = []; - const sourceFilesLoaded = {}; - function enqueueTopLevelModuleStatements(sourceFile) { - sourceFile.forEachChild((node) => { - if (ts.isImportDeclaration(node)) { - if (!node.importClause && ts.isStringLiteral(node.moduleSpecifier)) { - setColor(node, 2 /* Black */); - enqueueImport(node, node.moduleSpecifier.text); - } - return; - } - if (ts.isExportDeclaration(node)) { - if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { - setColor(node, 2 /* Black */); - enqueueImport(node, node.moduleSpecifier.text); - } - return; - } - if (ts.isExpressionStatement(node) - || ts.isIfStatement(node) - || ts.isIterationStatement(node, true) - || ts.isExportAssignment(node)) { - enqueue_black(node); - } - if (ts.isImportEqualsDeclaration(node)) { - if (/export/.test(node.getFullText(sourceFile))) { - // e.g. "export import Severity = BaseSeverity;" - enqueue_black(node); - } - } - }); - } - function enqueue_gray(node) { - if (nodeOrParentIsBlack(node) || getColor(node) === 1 /* Gray */) { - return; - } - setColor(node, 1 /* Gray */); - gray_queue.push(node); - } - function enqueue_black(node) { - const previousColor = getColor(node); - if (previousColor === 2 /* Black */) { - return; - } - if (previousColor === 1 /* Gray */) { - // remove from gray queue - gray_queue.splice(gray_queue.indexOf(node), 1); - setColor(node, 0 /* White */); - // add to black queue - enqueue_black(node); - // // move from one queue to the other - // black_queue.push(node); - // setColor(node, NodeColor.Black); - return; - } - if (nodeOrParentIsBlack(node)) { - return; - } - const fileName = node.getSourceFile().fileName; - if (/^defaultLib:/.test(fileName) || /\.d\.ts$/.test(fileName)) { - setColor(node, 2 /* Black */); - return; - } - const sourceFile = node.getSourceFile(); - if (!sourceFilesLoaded[sourceFile.fileName]) { - sourceFilesLoaded[sourceFile.fileName] = true; - enqueueTopLevelModuleStatements(sourceFile); - } - if (ts.isSourceFile(node)) { - return; - } - setColor(node, 2 /* Black */); - black_queue.push(node); - if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { - const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); - if (references) { - for (let i = 0, len = references.length; i < len; i++) { - const reference = references[i]; - const referenceSourceFile = program.getSourceFile(reference.fileName); - if (!referenceSourceFile) { - continue; - } - const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false); - if (ts.isMethodDeclaration(referenceNode.parent) - || ts.isPropertyDeclaration(referenceNode.parent) - || ts.isGetAccessor(referenceNode.parent) - || ts.isSetAccessor(referenceNode.parent)) { - enqueue_gray(referenceNode.parent); - } - } - } - } - } - function enqueueFile(filename) { - const sourceFile = program.getSourceFile(filename); - if (!sourceFile) { - console.warn(`Cannot find source file ${filename}`); - return; - } - enqueue_black(sourceFile); - } - function enqueueImport(node, importText) { - if (options.importIgnorePattern.test(importText)) { - // this import should be ignored - return; - } - const nodeSourceFile = node.getSourceFile(); - let fullPath; - if (/(^\.\/)|(^\.\.\/)/.test(importText)) { - fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; - } - else { - fullPath = importText + '.ts'; - } - enqueueFile(fullPath); - } - options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); - // Add fake usage files - options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint.${index}.ts`)); - let step = 0; - const checker = program.getTypeChecker(); - while (black_queue.length > 0 || gray_queue.length > 0) { - ++step; - let node; - if (step % 100 === 0) { - console.log(`Treeshaking - ${Math.floor(100 * step / (step + black_queue.length + gray_queue.length))}% - ${step}/${step + black_queue.length + gray_queue.length} (${black_queue.length}, ${gray_queue.length})`); - } - if (black_queue.length === 0) { - for (let i = 0; i < gray_queue.length; i++) { - const node = gray_queue[i]; - const nodeParent = node.parent; - if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { - gray_queue.splice(i, 1); - black_queue.push(node); - setColor(node, 2 /* Black */); - i--; - } - } - } - if (black_queue.length > 0) { - node = black_queue.shift(); - } - else { - // only gray nodes remaining... - break; - } - const nodeSourceFile = node.getSourceFile(); - const loop = (node) => { - const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node); - if (symbolImportNode) { - setColor(symbolImportNode, 2 /* Black */); - } - if (symbol && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { - for (let i = 0, len = symbol.declarations.length; i < len; i++) { - const declaration = symbol.declarations[i]; - if (ts.isSourceFile(declaration)) { - // Do not enqueue full source files - // (they can be the declaration of a module import) - continue; - } - if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { - enqueue_black(declaration.name); - for (let j = 0; j < declaration.members.length; j++) { - const member = declaration.members[j]; - const memberName = member.name ? member.name.getText() : null; - if (ts.isConstructorDeclaration(member) - || ts.isConstructSignatureDeclaration(member) - || ts.isIndexSignatureDeclaration(member) - || ts.isCallSignatureDeclaration(member) - || memberName === 'toJSON' - || memberName === 'toString' - || memberName === 'dispose' // TODO: keeping all `dispose` methods - || /^_(.*)Brand$/.test(memberName || '') // TODO: keeping all members ending with `Brand`... - ) { - enqueue_black(member); - } - } - // queue the heritage clauses - if (declaration.heritageClauses) { - for (let heritageClause of declaration.heritageClauses) { - enqueue_black(heritageClause); - } - } - } - else { - enqueue_black(declaration); - } - } - } - node.forEachChild(loop); - }; - node.forEachChild(loop); - } -} -function nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol) { - for (let i = 0, len = symbol.declarations.length; i < len; i++) { - const declaration = symbol.declarations[i]; - const declarationSourceFile = declaration.getSourceFile(); - if (nodeSourceFile === declarationSourceFile) { - if (declaration.pos <= node.pos && node.end <= declaration.end) { - return true; - } - } - } - return false; -} -function generateResult(languageService, shakeLevel) { - const program = languageService.getProgram(); - if (!program) { - throw new Error('Could not get program from language service'); - } - let result = {}; - const writeFile = (filePath, contents) => { - result[filePath] = contents; - }; - program.getSourceFiles().forEach((sourceFile) => { - const fileName = sourceFile.fileName; - if (/^defaultLib:/.test(fileName)) { - return; - } - const destination = fileName; - if (/\.d\.ts$/.test(fileName)) { - if (nodeOrChildIsBlack(sourceFile)) { - writeFile(destination, sourceFile.text); - } - return; - } - let text = sourceFile.text; - let result = ''; - function keep(node) { - result += text.substring(node.pos, node.end); - } - function write(data) { - result += data; - } - function writeMarkedNodes(node) { - if (getColor(node) === 2 /* Black */) { - return keep(node); - } - // Always keep certain top-level statements - if (ts.isSourceFile(node.parent)) { - if (ts.isExpressionStatement(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'use strict') { - return keep(node); - } - if (ts.isVariableStatement(node) && nodeOrChildIsBlack(node)) { - return keep(node); - } - } - // Keep the entire import in import * as X cases - if (ts.isImportDeclaration(node)) { - if (node.importClause && node.importClause.namedBindings) { - if (ts.isNamespaceImport(node.importClause.namedBindings)) { - if (getColor(node.importClause.namedBindings) === 2 /* Black */) { - return keep(node); - } - } - else { - let survivingImports = []; - for (const importNode of node.importClause.namedBindings.elements) { - if (getColor(importNode) === 2 /* Black */) { - survivingImports.push(importNode.getFullText(sourceFile)); - } - } - const leadingTriviaWidth = node.getLeadingTriviaWidth(); - const leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth); - if (survivingImports.length > 0) { - if (node.importClause && node.importClause.name && getColor(node.importClause) === 2 /* Black */) { - return write(`${leadingTrivia}import ${node.importClause.name.text}, {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); - } - return write(`${leadingTrivia}import {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); - } - else { - if (node.importClause && node.importClause.name && getColor(node.importClause) === 2 /* Black */) { - return write(`${leadingTrivia}import ${node.importClause.name.text} from${node.moduleSpecifier.getFullText(sourceFile)};`); - } - } - } - } - else { - if (node.importClause && getColor(node.importClause) === 2 /* Black */) { - return keep(node); - } - } - } - if (shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { - let toWrite = node.getFullText(); - for (let i = node.members.length - 1; i >= 0; i--) { - const member = node.members[i]; - if (getColor(member) === 2 /* Black */ || !member.name) { - // keep method - continue; - } - let pos = member.pos - node.pos; - let end = member.end - node.pos; - toWrite = toWrite.substring(0, pos) + toWrite.substring(end); - } - return write(toWrite); - } - if (ts.isFunctionDeclaration(node)) { - // Do not go inside functions if they haven't been marked - return; - } - node.forEachChild(writeMarkedNodes); - } - if (getColor(sourceFile) !== 2 /* Black */) { - if (!nodeOrChildIsBlack(sourceFile)) { - // none of the elements are reachable => don't write this file at all! - return; - } - sourceFile.forEachChild(writeMarkedNodes); - result += sourceFile.endOfFileToken.getFullText(sourceFile); - } - else { - result = text; - } - writeFile(destination, result); - }); - return result; -} -//#endregion -//#region Utils -/** - * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) - */ -function getRealNodeSymbol(checker, node) { - const getPropertySymbolsFromContextualType = ts.getPropertySymbolsFromContextualType; - const getContainingObjectLiteralElement = ts.getContainingObjectLiteralElement; - const getNameFromPropertyName = ts.getNameFromPropertyName; - // Go to the original declaration for cases: - // - // (1) when the aliased symbol was declared in the location(parent). - // (2) when the aliased symbol is originating from an import. - // - function shouldSkipAlias(node, declaration) { - if (node.kind !== ts.SyntaxKind.Identifier) { - return false; - } - if (node.parent === declaration) { - return true; - } - switch (declaration.kind) { - case ts.SyntaxKind.ImportClause: - case ts.SyntaxKind.ImportEqualsDeclaration: - return true; - case ts.SyntaxKind.ImportSpecifier: - return declaration.parent.kind === ts.SyntaxKind.NamedImports; - default: - return false; - } - } - if (!ts.isShorthandPropertyAssignment(node)) { - if (node.getChildCount() !== 0) { - return [null, null]; - } - } - const { parent } = node; - let symbol = checker.getSymbolAtLocation(node); - let importNode = null; - // If this is an alias, and the request came at the declaration location - // get the aliased symbol instead. This allows for goto def on an import e.g. - // import {A, B} from "mod"; - // to jump to the implementation directly. - if (symbol && symbol.flags & ts.SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { - const aliased = checker.getAliasedSymbol(symbol); - if (aliased.declarations) { - // We should mark the import as visited - importNode = symbol.declarations[0]; - symbol = aliased; - } - } - if (symbol) { - // Because name in short-hand property assignment has two different meanings: property name and property value, - // using go-to-definition at such position should go to the variable declaration of the property value rather than - // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition - // is performed at the location of property access, we would like to go to definition of the property in the short-hand - // assignment. This case and others are handled by the following code. - if (node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { - symbol = checker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - } - // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the - // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern - // and return the property declaration for the referenced property. - // For example: - // import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo" - // - // function bar(onfulfilled: (value: T) => void) { //....} - // interface Test { - // pr/*destination*/op1: number - // } - // bar(({pr/*goto*/op1})=>{}); - if (ts.isPropertyName(node) && ts.isBindingElement(parent) && ts.isObjectBindingPattern(parent.parent) && - (node === (parent.propertyName || parent.name))) { - const name = getNameFromPropertyName(node); - const type = checker.getTypeAtLocation(parent.parent); - if (name && type) { - if (type.isUnion()) { - const prop = type.types[0].getProperty(name); - if (prop) { - symbol = prop; - } - } - else { - const prop = type.getProperty(name); - if (prop) { - symbol = prop; - } - } - } - } - // If the current location we want to find its definition is in an object literal, try to get the contextual type for the - // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. - // For example - // interface Props{ - // /*first*/prop1: number - // prop2: boolean - // } - // function Foo(arg: Props) {} - // Foo( { pr/*1*/op1: 10, prop2: false }) - const element = getContainingObjectLiteralElement(node); - if (element) { - const contextualType = element && checker.getContextualType(element.parent); - if (contextualType) { - const propertySymbols = getPropertySymbolsFromContextualType(element, checker, contextualType, /*unionSymbolOk*/ false); - if (propertySymbols) { - symbol = propertySymbols[0]; - } - } - } - } - if (symbol && symbol.declarations) { - return [symbol, importNode]; - } - return [null, null]; -} -/** Get the token whose text contains the position */ -function getTokenAtPosition(sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) { - let current = sourceFile; - outer: while (true) { - // find the child that contains 'position' - for (const child of current.getChildren()) { - const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true); - if (start > position) { - // If this child begins after position, then all subsequent children will as well. - break; - } - const end = child.getEnd(); - if (position < end || (position === end && (child.kind === ts.SyntaxKind.EndOfFileToken || includeEndPosition))) { - current = child; - continue outer; - } - } - return current; - } -} diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index 89f562ad1b8..405336bfcbb 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -18,7 +18,7 @@ export const enum ShakeLevel { } export function toStringShakeLevel(shakeLevel: ShakeLevel): string { - switch(shakeLevel) { + switch (shakeLevel) { case ShakeLevel.Files: return 'Files (0)'; case ShakeLevel.InnerFile: @@ -42,11 +42,6 @@ export interface ITreeShakingOptions { * Inline usages. */ inlineEntryPoints: string[]; - /** - * TypeScript libs. - * e.g. `lib.d.ts`, `lib.es2015.collection.d.ts` - */ - libs: string[]; /** * Other .d.ts files */ @@ -130,11 +125,7 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu }); // Resolve libs - const RESOLVED_LIBS: ILibMap = {}; - options.libs.forEach((filename) => { - const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); - RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); - }); + const RESOLVED_LIBS = processLibFiles(options); const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; @@ -205,6 +196,34 @@ function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap { return FILES; } +/** + * Read lib files and follow lib references + */ +function processLibFiles(options: ITreeShakingOptions): ILibMap { + + const stack: string[] = [...options.compilerOptions.lib]; + const result: ILibMap = {}; + + while (stack.length > 0) { + const filename = `lib.${stack.shift()!.toLowerCase()}.d.ts`; + const key = `defaultLib:${filename}`; + if (!result[key]) { + // add this file + const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); + const sourceText = fs.readFileSync(filepath).toString(); + result[key] = sourceText; + + // precess dependencies and "recurse" + const info = ts.preProcessFile(sourceText); + for (let ref of info.libReferenceDirectives) { + stack.push(ref.fileName); + } + } + } + + return result; +} + interface ILibMap { [libName: string]: string; } interface IFileMap { [fileName: string]: string; } @@ -317,6 +336,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt const black_queue: ts.Node[] = []; const gray_queue: ts.Node[] = []; + const export_import_queue: ts.Node[] = []; const sourceFilesLoaded: { [fileName: string]: boolean } = {}; function enqueueTopLevelModuleStatements(sourceFile: ts.SourceFile): void { @@ -332,10 +352,16 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt } if (ts.isExportDeclaration(node)) { - if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { + if (!node.exportClause && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { + // export * from "foo"; setColor(node, NodeColor.Black); enqueueImport(node, node.moduleSpecifier.text); } + if (node.exportClause && ts.isNamedExports(node.exportClause)) { + for (const exportSpecifier of node.exportClause.elements) { + export_import_queue.push(exportSpecifier); + } + } return; } @@ -410,7 +436,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt setColor(node, NodeColor.Black); black_queue.push(node); - if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); if (references) { for (let i = 0, len = references.length; i < len; i++) { @@ -475,7 +501,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt } if (black_queue.length === 0) { - for (let i = 0; i< gray_queue.length; i++) { + for (let i = 0; i < gray_queue.length; i++) { const node = gray_queue[i]; const nodeParent = node.parent; if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { @@ -510,7 +536,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt continue; } - if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) { enqueue_black(declaration.name!); for (let j = 0; j < declaration.members.length; j++) { @@ -521,6 +547,8 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt || ts.isConstructSignatureDeclaration(member) || ts.isIndexSignatureDeclaration(member) || ts.isCallSignatureDeclaration(member) + || memberName === '[Symbol.iterator]' + || memberName === '[Symbol.toStringTag]' || memberName === 'toJSON' || memberName === 'toString' || memberName === 'dispose'// TODO: keeping all `dispose` methods @@ -545,6 +573,23 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt }; node.forEachChild(loop); } + + while (export_import_queue.length > 0) { + const node = export_import_queue.shift()!; + if (nodeOrParentIsBlack(node)) { + continue; + } + const symbol: ts.Symbol | undefined = (node).symbol; + if (!symbol) { + continue; + } + const aliased = checker.getAliasedSymbol(symbol); + if (aliased.declarations && aliased.declarations.length > 0) { + if (nodeOrParentIsBlack(aliased.declarations[0]) || nodeOrChildIsBlack(aliased.declarations[0])) { + setColor(node, NodeColor.Black); + } + } + } } function nodeIsInItsOwnDeclaration(nodeSourceFile: ts.SourceFile, node: ts.Node, symbol: ts.Symbol): boolean { @@ -646,6 +691,22 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe } } + if (ts.isExportDeclaration(node)) { + if (node.exportClause && node.moduleSpecifier && ts.isNamedExports(node.exportClause)) { + let survivingExports: string[] = []; + for (const exportSpecifier of node.exportClause.elements) { + if (getColor(exportSpecifier) === NodeColor.Black) { + survivingExports.push(exportSpecifier.getFullText(sourceFile)); + } + } + const leadingTriviaWidth = node.getLeadingTriviaWidth(); + const leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth); + if (survivingExports.length > 0) { + return write(`${leadingTrivia}export {${survivingExports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); + } + } + } + if (shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { let toWrite = node.getFullText(); for (let i = node.members.length - 1; i >= 0; i--) { @@ -691,6 +752,36 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe //#region Utils +function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program: ts.Program, checker: ts.TypeChecker, declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean { + if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) { + for (const heritageClause of declaration.heritageClauses) { + for (const type of heritageClause.types) { + const symbol = findSymbolFromHeritageType(checker, type); + if (symbol) { + const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]); + if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) { + return true; + } + } + } + } + } + return false; +} + +function findSymbolFromHeritageType(checker: ts.TypeChecker, type: ts.ExpressionWithTypeArguments | ts.Expression | ts.PrivateIdentifier): ts.Symbol | null { + if (ts.isExpressionWithTypeArguments(type)) { + return findSymbolFromHeritageType(checker, type.expression); + } + if (ts.isIdentifier(type)) { + return getRealNodeSymbol(checker, type)[0]; + } + if (ts.isPropertyAccessExpression(type)) { + return findSymbolFromHeritageType(checker, type.name); + } + return null; +} + /** * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) */ @@ -708,7 +799,7 @@ function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | // (2) when the aliased symbol is originating from an import. // function shouldSkipAlias(node: ts.Node, declaration: ts.Node): boolean { - if (node.kind !== ts.SyntaxKind.Identifier) { + if (!ts.isShorthandPropertyAssignment(node) && node.kind !== ts.SyntaxKind.Identifier) { return false; } if (node.parent === declaration) { @@ -733,7 +824,12 @@ function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | const { parent } = node; - let symbol = checker.getSymbolAtLocation(node); + let symbol = ( + ts.isShorthandPropertyAssignment(node) + ? checker.getShorthandAssignmentValueSymbol(node) + : checker.getSymbolAtLocation(node) + ); + let importNode: ts.Declaration | null = null; // If this is an alias, and the request came at the declaration location // get the aliased symbol instead. This allows for goto def on an import e.g. diff --git a/build/lib/typings/gulp-bom.d.ts b/build/lib/typings/gulp-bom.d.ts index 94dc5fd6d21..88525a7e9db 100644 --- a/build/lib/typings/gulp-bom.d.ts +++ b/build/lib/typings/gulp-bom.d.ts @@ -4,7 +4,7 @@ declare module "gulp-bom" { /** * This is required as per: - * https://github.com/Microsoft/TypeScript/issues/5073 + * https://github.com/microsoft/TypeScript/issues/5073 */ namespace f {} diff --git a/build/lib/typings/gulp-cssnano.d.ts b/build/lib/typings/gulp-cssnano.d.ts index 48f8cbf7165..97d3827641a 100644 --- a/build/lib/typings/gulp-cssnano.d.ts +++ b/build/lib/typings/gulp-cssnano.d.ts @@ -4,9 +4,9 @@ declare module "gulp-cssnano" { /** * This is required as per: - * https://github.com/Microsoft/TypeScript/issues/5073 + * https://github.com/microsoft/TypeScript/issues/5073 */ namespace f {} export = f; -} \ No newline at end of file +} diff --git a/build/lib/typings/gulp-flatmap.d.ts b/build/lib/typings/gulp-flatmap.d.ts index 82dd84e15b0..c99232c61cc 100644 --- a/build/lib/typings/gulp-flatmap.d.ts +++ b/build/lib/typings/gulp-flatmap.d.ts @@ -4,9 +4,9 @@ declare module 'gulp-flatmap' { /** * This is required as per: - * https://github.com/Microsoft/TypeScript/issues/5073 + * https://github.com/microsoft/TypeScript/issues/5073 */ namespace f {} export = f; -} \ No newline at end of file +} diff --git a/build/lib/typings/vinyl.d.ts b/build/lib/typings/vinyl.d.ts index a85632e172b..6be30a1eebf 100644 --- a/build/lib/typings/vinyl.d.ts +++ b/build/lib/typings/vinyl.d.ts @@ -103,10 +103,10 @@ declare module "vinyl" { /** * This is required as per: - * https://github.com/Microsoft/TypeScript/issues/5073 + * https://github.com/microsoft/TypeScript/issues/5073 */ namespace File {} export = File; -} \ No newline at end of file +} diff --git a/build/lib/util.js b/build/lib/util.js deleted file mode 100644 index 752d9fb63f0..00000000000 --- a/build/lib/util.js +++ /dev/null @@ -1,256 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const debounce = require("debounce"); -const _filter = require("gulp-filter"); -const rename = require("gulp-rename"); -const path = require("path"); -const fs = require("fs"); -const _rimraf = require("rimraf"); -const git = require("./git"); -const VinylFile = require("vinyl"); -const NoCancellationToken = { isCancellationRequested: () => false }; -function incremental(streamProvider, initial, supportsCancellation) { - const input = es.through(); - const output = es.through(); - let state = 'idle'; - let buffer = Object.create(null); - const token = !supportsCancellation ? undefined : { isCancellationRequested: () => Object.keys(buffer).length > 0 }; - const run = (input, isCancellable) => { - state = 'running'; - const stream = !supportsCancellation ? streamProvider() : streamProvider(isCancellable ? token : NoCancellationToken); - input - .pipe(stream) - .pipe(es.through(undefined, () => { - state = 'idle'; - eventuallyRun(); - })) - .pipe(output); - }; - if (initial) { - run(initial, false); - } - const eventuallyRun = debounce(() => { - const paths = Object.keys(buffer); - if (paths.length === 0) { - return; - } - const data = paths.map(path => buffer[path]); - buffer = Object.create(null); - run(es.readArray(data), true); - }, 500); - input.on('data', (f) => { - buffer[f.path] = f; - if (state === 'idle') { - eventuallyRun(); - } - }); - return es.duplex(input, output); -} -exports.incremental = incremental; -function fixWin32DirectoryPermissions() { - if (!/win32/.test(process.platform)) { - return es.through(); - } - return es.mapSync(f => { - if (f.stat && f.stat.isDirectory && f.stat.isDirectory()) { - f.stat.mode = 16877; - } - return f; - }); -} -exports.fixWin32DirectoryPermissions = fixWin32DirectoryPermissions; -function setExecutableBit(pattern) { - const setBit = es.mapSync(f => { - if (!f.stat) { - f.stat = { isFile() { return true; } }; - } - f.stat.mode = /* 100755 */ 33261; - return f; - }); - if (!pattern) { - return setBit; - } - const input = es.through(); - const filter = _filter(pattern, { restore: true }); - const output = input - .pipe(filter) - .pipe(setBit) - .pipe(filter.restore); - return es.duplex(input, output); -} -exports.setExecutableBit = setExecutableBit; -function toFileUri(filePath) { - const match = filePath.match(/^([a-z])\:(.*)$/i); - if (match) { - filePath = '/' + match[1].toUpperCase() + ':' + match[2]; - } - return 'file://' + filePath.replace(/\\/g, '/'); -} -exports.toFileUri = toFileUri; -function skipDirectories() { - return es.mapSync(f => { - if (!f.isDirectory()) { - return f; - } - }); -} -exports.skipDirectories = skipDirectories; -function cleanNodeModules(rulePath) { - const rules = fs.readFileSync(rulePath, 'utf8') - .split(/\r?\n/g) - .map(line => line.trim()) - .filter(line => line && !/^#/.test(line)); - const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); - const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); - const input = es.through(); - const output = es.merge(input.pipe(_filter(['**', ...excludes])), input.pipe(_filter(includes))); - return es.duplex(input, output); -} -exports.cleanNodeModules = cleanNodeModules; -function loadSourcemaps() { - const input = es.through(); - const output = input - .pipe(es.map((f, cb) => { - if (f.sourceMap) { - cb(undefined, f); - return; - } - if (!f.contents) { - cb(undefined, f); - return; - } - const contents = f.contents.toString('utf8'); - const reg = /\/\/# sourceMappingURL=(.*)$/g; - let lastMatch = null; - let match = null; - while (match = reg.exec(contents)) { - lastMatch = match; - } - if (!lastMatch) { - f.sourceMap = { - version: '3', - names: [], - mappings: '', - sources: [f.relative.replace(/\//g, '/')], - sourcesContent: [contents] - }; - cb(undefined, f); - return; - } - f.contents = Buffer.from(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8'); - fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', (err, contents) => { - if (err) { - return cb(err); - } - f.sourceMap = JSON.parse(contents); - cb(undefined, f); - }); - })); - return es.duplex(input, output); -} -exports.loadSourcemaps = loadSourcemaps; -function stripSourceMappingURL() { - const input = es.through(); - const output = input - .pipe(es.mapSync(f => { - const contents = f.contents.toString('utf8'); - f.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8'); - return f; - })); - return es.duplex(input, output); -} -exports.stripSourceMappingURL = stripSourceMappingURL; -function rimraf(dir) { - const result = () => new Promise((c, e) => { - let retries = 0; - const retry = () => { - _rimraf(dir, { maxBusyTries: 1 }, (err) => { - if (!err) { - return c(); - } - if (err.code === 'ENOTEMPTY' && ++retries < 5) { - return setTimeout(() => retry(), 10); - } - return e(err); - }); - }; - retry(); - }); - result.taskName = `clean-${path.basename(dir).toLowerCase()}`; - return result; -} -exports.rimraf = rimraf; -function _rreaddir(dirPath, prepend, result) { - const entries = fs.readdirSync(dirPath, { withFileTypes: true }); - for (const entry of entries) { - if (entry.isDirectory()) { - _rreaddir(path.join(dirPath, entry.name), `${prepend}/${entry.name}`, result); - } - else { - result.push(`${prepend}/${entry.name}`); - } - } -} -function rreddir(dirPath) { - let result = []; - _rreaddir(dirPath, '', result); - return result; -} -exports.rreddir = rreddir; -function ensureDir(dirPath) { - if (fs.existsSync(dirPath)) { - return; - } - ensureDir(path.dirname(dirPath)); - fs.mkdirSync(dirPath); -} -exports.ensureDir = ensureDir; -function getVersion(root) { - let version = process.env['BUILD_SOURCEVERSION']; - if (!version || !/^[0-9a-f]{40}$/i.test(version)) { - version = git.getVersion(root); - } - return version; -} -exports.getVersion = getVersion; -function rebase(count) { - return rename(f => { - const parts = f.dirname ? f.dirname.split(/[\/\\]/) : []; - f.dirname = parts.slice(count).join(path.sep); - }); -} -exports.rebase = rebase; -function filter(fn) { - const result = es.through(function (data) { - if (fn(data)) { - this.emit('data', data); - } - else { - result.restore.push(data); - } - }); - result.restore = es.through(); - return result; -} -exports.filter = filter; -function versionStringToNumber(versionStr) { - const semverRegex = /(\d+)\.(\d+)\.(\d+)/; - const match = versionStr.match(semverRegex); - if (!match) { - throw new Error('Version string is not properly formatted: ' + versionStr); - } - return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); -} -exports.versionStringToNumber = versionStringToNumber; -function streamToPromise(stream) { - return new Promise((c, e) => { - stream.on('error', err => e(err)); - stream.on('end', () => c()); - }); -} -exports.streamToPromise = streamToPromise; diff --git a/build/lib/util.ts b/build/lib/util.ts index 45b6c9e1b82..c0a0d9619d7 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -18,6 +18,8 @@ import * as VinylFile from 'vinyl'; import { ThroughStream } from 'through'; import * as sm from 'source-map'; +const root = path.dirname(path.dirname(__dirname)); + export interface ICancellationToken { isCancellationRequested(): boolean; } @@ -184,7 +186,7 @@ export function loadSourcemaps(): NodeJS.ReadWriteStream { version: '3', names: [], mappings: '', - sources: [f.relative.replace(/\//g, '/')], + sources: [f.relative], sourcesContent: [contents] }; @@ -218,6 +220,20 @@ export function stripSourceMappingURL(): NodeJS.ReadWriteStream { return es.duplex(input, output); } +export function rewriteSourceMappingURL(sourceMappingURLBase: string): NodeJS.ReadWriteStream { + const input = es.through(); + + const output = input + .pipe(es.mapSync(f => { + const contents = (f.contents).toString('utf8'); + const str = `//# sourceMappingURL=${sourceMappingURLBase}/${path.dirname(f.relative).replace(/\\/g, '/')}/$1`; + f.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, str)); + return f; + })); + + return es.duplex(input, output); +} + export function rimraf(dir: string): () => Promise { const result = () => new Promise((c, e) => { let retries = 0; @@ -318,3 +334,9 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise { stream.on('end', () => c()); }); } + +export function getElectronVersion(): string { + const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8'); + const target = /^target "(.*)"$/m.exec(yarnrc)![1]; + return target; +} diff --git a/build/lib/watch/index.js b/build/lib/watch/index.ts similarity index 100% rename from build/lib/watch/index.js rename to build/lib/watch/index.ts diff --git a/build/lib/watch/watch-win32.js b/build/lib/watch/watch-win32.ts similarity index 51% rename from build/lib/watch/watch-win32.js rename to build/lib/watch/watch-win32.ts index d0cd307ba16..4ed37277123 100644 --- a/build/lib/watch/watch-win32.js +++ b/build/lib/watch/watch-win32.ts @@ -3,16 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -var path = require('path'); -var cp = require('child_process'); -var fs = require('fs'); -var File = require('vinyl'); -var es = require('event-stream'); -var filter = require('gulp-filter'); +import * as path from 'path'; +import * as cp from 'child_process'; +import * as fs from 'fs'; +import * as File from 'vinyl'; +import * as es from 'event-stream'; +import * as filter from 'gulp-filter'; +import { Stream } from 'stream'; -var watcherPath = path.join(__dirname, 'watcher.exe'); +const watcherPath = path.join(__dirname, 'watcher.exe'); -function toChangeType(type) { +function toChangeType(type: '0' | '1' | '2'): 'change' | 'add' | 'unlink' { switch (type) { case '0': return 'change'; case '1': return 'add'; @@ -20,35 +21,33 @@ function toChangeType(type) { } } -function watch(root) { - var result = es.through(); - var child = cp.spawn(watcherPath, [root]); +function watch(root: string): Stream { + const result = es.through(); + let child: cp.ChildProcess | null = cp.spawn(watcherPath, [root]); child.stdout.on('data', function (data) { - // @ts-ignore - var lines = data.toString('utf8').split('\n'); - for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim(); + const lines: string[] = data.toString('utf8').split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); if (line.length === 0) { continue; } - var changeType = line[0]; - var changePath = line.substr(2); + const changeType = <'0' | '1' | '2'>line[0]; + const changePath = line.substr(2); // filter as early as possible if (/^\.git/.test(changePath) || /(^|\\)out($|\\)/.test(changePath)) { continue; } - var changePathFull = path.join(root, changePath); + const changePathFull = path.join(root, changePath); - var file = new File({ + const file = new File({ path: changePathFull, base: root }); - //@ts-ignore - file.event = toChangeType(changeType); + (file).event = toChangeType(changeType); result.emit('data', file); } }); @@ -64,46 +63,46 @@ function watch(root) { process.once('SIGTERM', function () { process.exit(0); }); process.once('SIGTERM', function () { process.exit(0); }); - process.once('exit', function () { child && child.kill(); }); + process.once('exit', function () { if (child) { child.kill(); } }); return result; } -var cache = Object.create(null); +const cache: { [cwd: string]: Stream; } = Object.create(null); -module.exports = function (pattern, options) { +module.exports = function (pattern: string | string[] | filter.FileFunction, options?: { cwd?: string; base?: string; }) { options = options || {}; - var cwd = path.normalize(options.cwd || process.cwd()); - var watcher = cache[cwd]; + const cwd = path.normalize(options.cwd || process.cwd()); + let watcher = cache[cwd]; if (!watcher) { watcher = cache[cwd] = watch(cwd); } - var rebase = !options.base ? es.through() : es.mapSync(function (f) { - f.base = options.base; + const rebase = !options.base ? es.through() : es.mapSync(function (f: File) { + f.base = options!.base!; return f; }); return watcher .pipe(filter(['**', '!.git{,/**}'])) // ignore all things git .pipe(filter(pattern)) - .pipe(es.map(function (file, cb) { + .pipe(es.map(function (file: File, cb) { fs.stat(file.path, function (err, stat) { - if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err && err.code === 'ENOENT') { return cb(undefined, file); } if (err) { return cb(); } if (!stat.isFile()) { return cb(); } fs.readFile(file.path, function (err, contents) { - if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err && err.code === 'ENOENT') { return cb(undefined, file); } if (err) { return cb(); } file.contents = contents; file.stat = stat; - cb(null, file); + cb(undefined, file); }); }); })) .pipe(rebase); -}; \ No newline at end of file +}; diff --git a/build/lib/watch/yarn.lock b/build/lib/watch/yarn.lock index 3f330da1764..edbfe1f3121 100644 --- a/build/lib/watch/yarn.lock +++ b/build/lib/watch/yarn.lock @@ -230,9 +230,9 @@ for-own@^0.1.4: for-in "^1.0.1" fsevents@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.1.tgz#74c64e21df71721845d0c44fe54b7f56b82995a9" - integrity sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw== + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== glob-base@^0.3.0: version "0.3.0" @@ -258,9 +258,9 @@ glob-parent@^3.0.1: path-dirname "^1.0.0" glob-parent@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" - integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" @@ -405,9 +405,9 @@ kind-of@^3.0.2: is-buffer "^1.1.5" kind-of@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== math-random@^1.0.1: version "1.0.4" @@ -479,9 +479,9 @@ path-is-absolute@^1.0.1: integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= picomatch@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.0.tgz#0fd042f568d08b1ad9ff2d3ec0f0bfb3cb80e177" - integrity sha512-uhnEDzAbrcJ8R3g2fANnSuXZMBtkpSjxTTgn2LeSiQlfmq72enQJWdQllXW24MBLYnA1SBD2vfvx2o0Zw3Ielw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pify@^2.3.0: version "2.3.0" @@ -530,9 +530,9 @@ randomatic@^3.0.0: math-random "^1.0.1" readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" diff --git a/build/monaco/README-npm.md b/build/monaco/README-npm.md index 3174903eb53..ee0ffc6e95c 100644 --- a/build/monaco/README-npm.md +++ b/build/monaco/README-npm.md @@ -5,10 +5,10 @@ npm module and unless you are doing something special (e.g. authoring a monaco e and consumed independently), it is best to consume the [monaco-editor](https://www.npmjs.com/package/monaco-editor) module that contains this module and adds languages supports. -The Monaco Editor is the code editor that powers [VS Code](https://github.com/Microsoft/vscode), +The Monaco Editor is the code editor that powers [VS Code](https://github.com/microsoft/vscode), a good page describing the code editor's features is [here](https://code.visualstudio.com/docs/editor/editingevolved). -This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/Microsoft/vscode). +This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/microsoft/vscode). ## License -[MIT](https://github.com/Microsoft/vscode/blob/master/LICENSE.txt) +[MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt) diff --git a/build/monaco/ThirdPartyNotices.txt b/build/monaco/ThirdPartyNotices.txt index 1de70ddaab6..8b488daf191 100644 --- a/build/monaco/ThirdPartyNotices.txt +++ b/build/monaco/ThirdPartyNotices.txt @@ -33,32 +33,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. END OF nodejs path library NOTICES AND INFORMATION -%% promise-polyfill version 8.1.0 (https://github.com/taylorhakes/promise-polyfill) -========================================= -Copyright (c) 2014 Taylor Hakes -Copyright (c) 2014 Forbes Lindesay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -========================================= -END OF winjs NOTICES AND INFORMATION - - %% string_scorer version 0.1.20 (https://github.com/joshaven/string_score) diff --git a/build/monaco/api.js b/build/monaco/api.js deleted file mode 100644 index 0fbaf7335b7..00000000000 --- a/build/monaco/api.js +++ /dev/null @@ -1,625 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const ts = require("typescript"); -const path = require("path"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const dtsv = '3'; -const tsfmt = require('../../tsfmt.json'); -const SRC = path.join(__dirname, '../../src'); -exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); -const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); -function logErr(message, ...rest) { - fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest); -} -function isDeclaration(a) { - return (a.kind === ts.SyntaxKind.InterfaceDeclaration - || a.kind === ts.SyntaxKind.EnumDeclaration - || a.kind === ts.SyntaxKind.ClassDeclaration - || a.kind === ts.SyntaxKind.TypeAliasDeclaration - || a.kind === ts.SyntaxKind.FunctionDeclaration - || a.kind === ts.SyntaxKind.ModuleDeclaration); -} -function visitTopLevelDeclarations(sourceFile, visitor) { - let stop = false; - let visit = (node) => { - if (stop) { - return; - } - switch (node.kind) { - case ts.SyntaxKind.InterfaceDeclaration: - case ts.SyntaxKind.EnumDeclaration: - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.VariableStatement: - case ts.SyntaxKind.TypeAliasDeclaration: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.ModuleDeclaration: - stop = visitor(node); - } - if (stop) { - return; - } - ts.forEachChild(node, visit); - }; - visit(sourceFile); -} -function getAllTopLevelDeclarations(sourceFile) { - let all = []; - visitTopLevelDeclarations(sourceFile, (node) => { - if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { - let interfaceDeclaration = node; - let triviaStart = interfaceDeclaration.pos; - let triviaEnd = interfaceDeclaration.name.pos; - let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); - if (triviaText.indexOf('@internal') === -1) { - all.push(node); - } - } - else { - let nodeText = getNodeText(sourceFile, node); - if (nodeText.indexOf('@internal') === -1) { - all.push(node); - } - } - return false /*continue*/; - }); - return all; -} -function getTopLevelDeclaration(sourceFile, typeName) { - let result = null; - visitTopLevelDeclarations(sourceFile, (node) => { - if (isDeclaration(node) && node.name) { - if (node.name.text === typeName) { - result = node; - return true /*stop*/; - } - return false /*continue*/; - } - // node is ts.VariableStatement - if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) { - result = node; - return true /*stop*/; - } - return false /*continue*/; - }); - return result; -} -function getNodeText(sourceFile, node) { - return sourceFile.getFullText().substring(node.pos, node.end); -} -function hasModifier(modifiers, kind) { - if (modifiers) { - for (let i = 0; i < modifiers.length; i++) { - let mod = modifiers[i]; - if (mod.kind === kind) { - return true; - } - } - } - return false; -} -function isStatic(member) { - return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword); -} -function isDefaultExport(declaration) { - return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword) - && hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)); -} -function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums) { - let result = getNodeText(sourceFile, declaration); - if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { - let interfaceDeclaration = declaration; - const staticTypeName = (isDefaultExport(interfaceDeclaration) - ? `${importName}.default` - : `${importName}.${declaration.name.text}`); - let instanceTypeName = staticTypeName; - const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); - if (typeParametersCnt > 0) { - let arr = []; - for (let i = 0; i < typeParametersCnt; i++) { - arr.push('any'); - } - instanceTypeName = `${instanceTypeName}<${arr.join(',')}>`; - } - const members = interfaceDeclaration.members; - members.forEach((member) => { - try { - let memberText = getNodeText(sourceFile, member); - if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { - result = result.replace(memberText, ''); - } - else { - const memberName = member.name.text; - if (isStatic(member)) { - usage.push(`a = ${staticTypeName}.${memberName};`); - } - else { - usage.push(`a = (<${instanceTypeName}>b).${memberName};`); - } - } - } - catch (err) { - // life.. - } - }); - } - else if (declaration.kind === ts.SyntaxKind.VariableStatement) { - const jsDoc = result.substr(0, declaration.getLeadingTriviaWidth(sourceFile)); - if (jsDoc.indexOf('@monacodtsreplace') >= 0) { - const jsDocLines = jsDoc.split(/\r\n|\r|\n/); - let directives = []; - for (const jsDocLine of jsDocLines) { - const m = jsDocLine.match(/^\s*\* \/([^/]+)\/([^/]+)\/$/); - if (m) { - directives.push([new RegExp(m[1], 'g'), m[2]]); - } - } - // remove the jsdoc - result = result.substr(jsDoc.length); - if (directives.length > 0) { - // apply replace directives - const replacer = createReplacerFromDirectives(directives); - result = replacer(result); - } - } - } - result = result.replace(/export default /g, 'export '); - result = result.replace(/export declare /g, 'export '); - result = result.replace(/declare /g, ''); - let lines = result.split(/\r\n|\r|\n/); - for (let i = 0; i < lines.length; i++) { - if (/\s*\*/.test(lines[i])) { - // very likely a comment - continue; - } - lines[i] = lines[i].replace(/"/g, '\''); - } - result = lines.join('\n'); - if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { - result = result.replace(/const enum/, 'enum'); - enums.push({ - enumName: declaration.name.getText(sourceFile), - text: result - }); - } - return result; -} -function format(text, endl) { - const REALLY_FORMAT = false; - text = preformat(text, endl); - if (!REALLY_FORMAT) { - return text; - } - // Parse the source text - let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); - // Get the formatting edits on the input sources - let edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); - // Apply the edits on the input code - return applyEdits(text, edits); - function countParensCurly(text) { - let cnt = 0; - for (let i = 0; i < text.length; i++) { - if (text.charAt(i) === '(' || text.charAt(i) === '{') { - cnt++; - } - if (text.charAt(i) === ')' || text.charAt(i) === '}') { - cnt--; - } - } - return cnt; - } - function repeatStr(s, cnt) { - let r = ''; - for (let i = 0; i < cnt; i++) { - r += s; - } - return r; - } - function preformat(text, endl) { - let lines = text.split(endl); - let inComment = false; - let inCommentDeltaIndent = 0; - let indent = 0; - for (let i = 0; i < lines.length; i++) { - let line = lines[i].replace(/\s$/, ''); - let repeat = false; - let lineIndent = 0; - do { - repeat = false; - if (line.substring(0, 4) === ' ') { - line = line.substring(4); - lineIndent++; - repeat = true; - } - if (line.charAt(0) === '\t') { - line = line.substring(1); - lineIndent++; - repeat = true; - } - } while (repeat); - if (line.length === 0) { - continue; - } - if (inComment) { - if (/\*\//.test(line)) { - inComment = false; - } - lines[i] = repeatStr('\t', lineIndent + inCommentDeltaIndent) + line; - continue; - } - if (/\/\*/.test(line)) { - inComment = true; - inCommentDeltaIndent = indent - lineIndent; - lines[i] = repeatStr('\t', indent) + line; - continue; - } - const cnt = countParensCurly(line); - let shouldUnindentAfter = false; - let shouldUnindentBefore = false; - if (cnt < 0) { - if (/[({]/.test(line)) { - shouldUnindentAfter = true; - } - else { - shouldUnindentBefore = true; - } - } - else if (cnt === 0) { - shouldUnindentBefore = /^\}/.test(line); - } - let shouldIndentAfter = false; - if (cnt > 0) { - shouldIndentAfter = true; - } - else if (cnt === 0) { - shouldIndentAfter = /{$/.test(line); - } - if (shouldUnindentBefore) { - indent--; - } - lines[i] = repeatStr('\t', indent) + line; - if (shouldUnindentAfter) { - indent--; - } - if (shouldIndentAfter) { - indent++; - } - } - return lines.join(endl); - } - function getRuleProvider(options) { - // Share this between multiple formatters using the same options. - // This represents the bulk of the space the formatter uses. - return ts.formatting.getFormatContext(options); - } - function applyEdits(text, edits) { - // Apply edits in reverse on the existing text - let result = text; - for (let i = edits.length - 1; i >= 0; i--) { - let change = edits[i]; - let head = result.slice(0, change.span.start); - let tail = result.slice(change.span.start + change.span.length); - result = head + change.newText + tail; - } - return result; - } -} -function createReplacerFromDirectives(directives) { - return (str) => { - for (let i = 0; i < directives.length; i++) { - str = str.replace(directives[i][0], directives[i][1]); - } - return str; - }; -} -function createReplacer(data) { - data = data || ''; - let rawDirectives = data.split(';'); - let directives = []; - rawDirectives.forEach((rawDirective) => { - if (rawDirective.length === 0) { - return; - } - let pieces = rawDirective.split('=>'); - let findStr = pieces[0]; - let replaceStr = pieces[1]; - findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); - findStr = '\\b' + findStr + '\\b'; - directives.push([new RegExp(findStr, 'g'), replaceStr]); - }); - return createReplacerFromDirectives(directives); -} -function generateDeclarationFile(recipe, sourceFileGetter) { - const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; - let lines = recipe.split(endl); - let result = []; - let usageCounter = 0; - let usageImports = []; - let usage = []; - let failed = false; - usage.push(`var a: any;`); - usage.push(`var b: any;`); - const generateUsageImport = (moduleId) => { - let importName = 'm' + (++usageCounter); - usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`); - return importName; - }; - let enums = []; - let version = null; - lines.forEach(line => { - if (failed) { - return; - } - let m0 = line.match(/^\/\/dtsv=(\d+)$/); - if (m0) { - version = m0[1]; - } - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m1) { - let moduleId = m1[1]; - const sourceFile = sourceFileGetter(moduleId); - if (!sourceFile) { - logErr(`While handling ${line}`); - logErr(`Cannot find ${moduleId}`); - failed = true; - return; - } - const importName = generateUsageImport(moduleId); - let replacer = createReplacer(m1[2]); - let typeNames = m1[3].split(/,/); - typeNames.forEach((typeName) => { - typeName = typeName.trim(); - if (typeName.length === 0) { - return; - } - let declaration = getTopLevelDeclaration(sourceFile, typeName); - if (!declaration) { - logErr(`While handling ${line}`); - logErr(`Cannot find ${typeName}`); - failed = true; - return; - } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); - }); - return; - } - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m2) { - let moduleId = m2[1]; - const sourceFile = sourceFileGetter(moduleId); - if (!sourceFile) { - logErr(`While handling ${line}`); - logErr(`Cannot find ${moduleId}`); - failed = true; - return; - } - const importName = generateUsageImport(moduleId); - let replacer = createReplacer(m2[2]); - let typeNames = m2[3].split(/,/); - let typesToExcludeMap = {}; - let typesToExcludeArr = []; - typeNames.forEach((typeName) => { - typeName = typeName.trim(); - if (typeName.length === 0) { - return; - } - typesToExcludeMap[typeName] = true; - typesToExcludeArr.push(typeName); - }); - getAllTopLevelDeclarations(sourceFile).forEach((declaration) => { - if (isDeclaration(declaration) && declaration.name) { - if (typesToExcludeMap[declaration.name.text]) { - return; - } - } - else { - // node is ts.VariableStatement - let nodeText = getNodeText(sourceFile, declaration); - for (let i = 0; i < typesToExcludeArr.length; i++) { - if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) { - return; - } - } - } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); - }); - return; - } - result.push(line); - }); - if (failed) { - return null; - } - if (version !== dtsv) { - if (!version) { - logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`); - } - else { - logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`); - } - return null; - } - let resultTxt = result.join(endl); - resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); - resultTxt = resultTxt.replace(/\bEvent { - if (e1.enumName < e2.enumName) { - return -1; - } - if (e1.enumName > e2.enumName) { - return 1; - } - return 0; - }); - let resultEnums = [ - '/*---------------------------------------------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' * Licensed under the MIT License. See License.txt in the project root for license information.', - ' *--------------------------------------------------------------------------------------------*/', - '', - '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', - '' - ].concat(enums.map(e => e.text)).join(endl); - resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); - resultEnums = format(resultEnums, endl); - resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); - return { - result: resultTxt, - usageContent: `${usageImports.join('\n')}\n\n${usage.join('\n')}`, - enums: resultEnums - }; -} -function _run(sourceFileGetter) { - const recipe = fs.readFileSync(exports.RECIPE_PATH).toString(); - const t = generateDeclarationFile(recipe, sourceFileGetter); - if (!t) { - return null; - } - const result = t.result; - const usageContent = t.usageContent; - const enums = t.enums; - const currentContent = fs.readFileSync(DECLARATION_PATH).toString(); - const one = currentContent.replace(/\r\n/gm, '\n'); - const other = result.replace(/\r\n/gm, '\n'); - const isTheSame = (one === other); - return { - content: result, - usageContent: usageContent, - enums: enums, - filePath: DECLARATION_PATH, - isTheSame - }; -} -class FSProvider { - existsSync(filePath) { - return fs.existsSync(filePath); - } - statSync(filePath) { - return fs.statSync(filePath); - } - readFileSync(_moduleId, filePath) { - return fs.readFileSync(filePath); - } -} -exports.FSProvider = FSProvider; -class CacheEntry { - constructor(sourceFile, mtime) { - this.sourceFile = sourceFile; - this.mtime = mtime; - } -} -class DeclarationResolver { - constructor(_fsProvider) { - this._fsProvider = _fsProvider; - this._sourceFileCache = Object.create(null); - } - invalidateCache(moduleId) { - this._sourceFileCache[moduleId] = null; - } - getDeclarationSourceFile(moduleId) { - if (this._sourceFileCache[moduleId]) { - // Since we cannot trust file watching to invalidate the cache, check also the mtime - const fileName = this._getFileName(moduleId); - const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); - if (this._sourceFileCache[moduleId].mtime !== mtime) { - this._sourceFileCache[moduleId] = null; - } - } - if (!this._sourceFileCache[moduleId]) { - this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); - } - return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId].sourceFile : null; - } - _getFileName(moduleId) { - if (/\.d\.ts$/.test(moduleId)) { - return path.join(SRC, moduleId); - } - return path.join(SRC, `${moduleId}.ts`); - } - _getDeclarationSourceFile(moduleId) { - const fileName = this._getFileName(moduleId); - if (!this._fsProvider.existsSync(fileName)) { - return null; - } - const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); - if (/\.d\.ts$/.test(moduleId)) { - // const mtime = this._fsProvider.statFileSync() - const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); - return new CacheEntry(ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), mtime); - } - const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); - const fileMap = { - 'file.ts': fileContents - }; - const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); - const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text; - return new CacheEntry(ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), mtime); - } -} -exports.DeclarationResolver = DeclarationResolver; -function run3(resolver) { - const sourceFileGetter = (moduleId) => resolver.getDeclarationSourceFile(moduleId); - return _run(sourceFileGetter); -} -exports.run3 = run3; -class TypeScriptLanguageServiceHost { - constructor(libs, files, compilerOptions) { - this._libs = libs; - this._files = files; - this._compilerOptions = compilerOptions; - } - // --- language service host --------------- - getCompilationSettings() { - return this._compilerOptions; - } - getScriptFileNames() { - return ([] - .concat(Object.keys(this._libs)) - .concat(Object.keys(this._files))); - } - getScriptVersion(_fileName) { - return '1'; - } - getProjectVersion() { - return '1'; - } - getScriptSnapshot(fileName) { - if (this._files.hasOwnProperty(fileName)) { - return ts.ScriptSnapshot.fromString(this._files[fileName]); - } - else if (this._libs.hasOwnProperty(fileName)) { - return ts.ScriptSnapshot.fromString(this._libs[fileName]); - } - else { - return ts.ScriptSnapshot.fromString(''); - } - } - getScriptKind(_fileName) { - return ts.ScriptKind.TS; - } - getCurrentDirectory() { - return ''; - } - getDefaultLibFileName(_options) { - return 'defaultLib:es5'; - } - isDefaultLibFileName(fileName) { - return fileName === this.getDefaultLibFileName(this._compilerOptions); - } -} -function execute() { - let r = run3(new DeclarationResolver(new FSProvider())); - if (!r) { - throw new Error(`monaco.d.ts generation error - Cannot continue`); - } - return r; -} -exports.execute = execute; diff --git a/build/monaco/esm.core.js b/build/monaco/esm.core.js new file mode 100644 index 00000000000..b84b5fb4f41 --- /dev/null +++ b/build/monaco/esm.core.js @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Entry file for webpack bunlding. + +import * as monaco from 'monaco-editor-core'; + +self.MonacoEnvironment = { + getWorkerUrl: function (moduleId, label) { + return './editor.worker.bundle.js'; + } +}; + +monaco.editor.create(document.getElementById('container'), { + value: [ + 'var hello = "hello world";' + ].join('\n'), + language: 'javascript' +}); diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index fdcdf533406..7288af94c9d 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -3,12 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +declare let MonacoEnvironment: monaco.Environment | undefined; + declare namespace monaco { - // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. - export type Thenable = PromiseLike; + export interface Environment { + baseUrl?: string; + getWorker?(workerId: string, label: string): Worker; + getWorkerUrl?(workerId: string, label: string): string; + } + export interface IDisposable { dispose(): void; } diff --git a/build/monaco/monaco.webpack.config.js b/build/monaco/monaco.webpack.config.js new file mode 100644 index 00000000000..974a341a197 --- /dev/null +++ b/build/monaco/monaco.webpack.config.js @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); + +module.exports = { + mode: 'production', + entry: { + 'core': './build/monaco/esm.core.js', + 'editor.worker': './out-monaco-editor-core/esm/vs/editor/editor.worker.js' + }, + output: { + globalObject: 'self', + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'dist') + }, + module: { + rules: [{ + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }, { + test: /\.ttf$/, + use: ['file-loader'] + }] + }, + resolve: { + alias: { + 'monaco-editor-core': path.resolve(__dirname, '../../out-monaco-editor-core/esm/vs/editor/editor.main.js'), + } + }, + stats: { + all: false, + modules: true, + maxModules: 0, + errors: true, + warnings: true, + // our additional options + moduleTrace: true, + errorDetails: true, + chunks: true + } +}; diff --git a/build/monaco/package.json b/build/monaco/package.json index 70021689eb4..b42c6aa7344 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.19.2", + "version": "0.21.1", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", @@ -9,9 +9,9 @@ "module": "./esm/vs/editor/editor.main.js", "repository": { "type": "git", - "url": "https://github.com/Microsoft/vscode" + "url": "https://github.com/microsoft/vscode" }, "bugs": { - "url": "https://github.com/Microsoft/vscode/issues" + "url": "https://github.com/microsoft/vscode/issues" } } diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 83e31e8b853..8f8b0019a77 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -13,7 +13,7 @@ const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; * @param {*} [opts] */ function yarnInstall(location, opts) { - opts = opts || {}; + opts = opts || { env: process.env }; opts.cwd = location; opts.stdio = 'inherit'; @@ -33,9 +33,10 @@ function yarnInstall(location, opts) { yarnInstall('extensions'); // node modules shared by all extensions -yarnInstall('remote'); // node modules used by vscode server - -yarnInstall('remote/web'); // node modules used by vscode web +if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) { + yarnInstall('remote'); // node modules used by vscode server + yarnInstall('remote/web'); // node modules used by vscode web +} const allExtensionFolders = fs.readdirSync('extensions'); const extensions = allExtensionFolders.filter(e => { @@ -52,8 +53,6 @@ extensions.forEach(extension => yarnInstall(`extensions/${extension}`)); function yarnInstallBuildDependencies() { // make sure we install the deps of build/lib/watch for the system installed // node, since that is the driver of gulp - //@ts-ignore - const env = Object.assign({}, process.env); const watchPath = path.join(path.dirname(__dirname), 'lib', 'watch'); const yarnrcPath = path.join(watchPath, '.yarnrc'); @@ -66,10 +65,13 @@ target "${target}" runtime "${runtime}"`; fs.writeFileSync(yarnrcPath, yarnrc, 'utf8'); - yarnInstall(watchPath, { env }); + yarnInstall(watchPath); } yarnInstall(`build`); // node modules required for build yarnInstall('test/automation'); // node modules required for smoketest yarnInstall('test/smoke'); // node modules required for smoketest +yarnInstall('test/integration/browser'); // node modules required for integration yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron + +cp.execSync('git config pull.rebase true'); diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index eca72654382..cb88d37adef 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -23,7 +23,7 @@ if (majorYarnVersion < 1 || minorYarnVersion < 10) { err = true; } -if (!/yarn\.js$|yarnpkg$/.test(process.env['npm_execpath'])) { +if (!/yarn[\w-.]*\.js$|yarnpkg$/.test(process.env['npm_execpath'])) { console.error('\033[1;31m*** Please use yarn to install dependencies.\033[0;0m'); err = true; } diff --git a/build/package.json b/build/package.json index 77cfdaf721b..d7e68e7ce3f 100644 --- a/build/package.json +++ b/build/package.json @@ -12,6 +12,7 @@ "@types/gulp": "^4.0.5", "@types/gulp-concat": "^0.0.32", "@types/gulp-filter": "^3.0.32", + "@types/gulp-gzip": "^0.0.31", "@types/gulp-json-editor": "^2.2.31", "@types/gulp-rename": "^0.0.33", "@types/gulp-sourcemaps": "^0.0.32", @@ -33,19 +34,23 @@ "@typescript-eslint/parser": "^2.12.0", "applicationinsights": "1.0.8", "azure-storage": "^2.1.0", + "electron-osx-sign": "^0.4.16", "github-releases": "^0.4.1", + "gulp-azure-storage": "^0.11.1", "gulp-bom": "^1.0.0", + "gulp-gzip": "^1.4.2", "gulp-sourcemaps": "^1.11.0", "gulp-uglify": "^3.0.0", - "iconv-lite": "0.4.23", + "iconv-lite-umd": "0.6.8", + "jsonc-parser": "^2.3.0", "mime": "^1.3.4", "minimatch": "3.0.4", - "minimist": "^1.2.0", + "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": " 3.8.0-beta", + "typescript": "^4.1.0-dev.20201018", "vsce": "1.48.0", - "vscode-telemetry-extractor": "^1.5.4", + "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" }, "scripts": { diff --git a/build/polyfills/vscode-extension-telemetry.js b/build/polyfills/vscode-extension-telemetry.js new file mode 100644 index 00000000000..d038776c59c --- /dev/null +++ b/build/polyfills/vscode-extension-telemetry.js @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); + +let TelemetryReporter = (function () { + function TelemetryReporter(extensionId, extensionVersion, key) { + } + TelemetryReporter.prototype.updateUserOptIn = function (key) { + }; + TelemetryReporter.prototype.createAppInsightsClient = function (key) { + }; + TelemetryReporter.prototype.getCommonProperties = function () { + }; + TelemetryReporter.prototype.sendTelemetryEvent = function (eventName, properties, measurements) { + }; + TelemetryReporter.prototype.dispose = function () { + }; + TelemetryReporter.TELEMETRY_CONFIG_ID = 'telemetry'; + TelemetryReporter.TELEMETRY_CONFIG_ENABLED_ID = 'enableTelemetry'; + return TelemetryReporter; +}()); +exports.default = TelemetryReporter; diff --git a/build/polyfills/vscode-nls.js b/build/polyfills/vscode-nls.js new file mode 100644 index 00000000000..b89250102af --- /dev/null +++ b/build/polyfills/vscode-nls.js @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); + +function format(message, args) { + let result; + // if (isPseudo) { + // // FF3B and FF3D is the Unicode zenkaku representation for [ and ] + // message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; + // } + if (args.length === 0) { + result = message; + } + else { + result = message.replace(/\{(\d+)\}/g, function (match, rest) { + let index = rest[0]; + let arg = args[index]; + let replacement = match; + if (typeof arg === 'string') { + replacement = arg; + } + else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { + replacement = String(arg); + } + return replacement; + }); + } + return result; +} + +function localize(key, message) { + let args = []; + for (let _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + return format(message, args); +} + +function loadMessageBundle(file) { + return localize; +} + +let MessageFormat; +(function (MessageFormat) { + MessageFormat["file"] = "file"; + MessageFormat["bundle"] = "bundle"; + MessageFormat["both"] = "both"; +})(MessageFormat = exports.MessageFormat || (exports.MessageFormat = {})); +let BundleFormat; +(function (BundleFormat) { + // the nls.bundle format + BundleFormat["standalone"] = "standalone"; + BundleFormat["languagePack"] = "languagePack"; +})(BundleFormat = exports.BundleFormat || (exports.BundleFormat = {})); + +exports.loadMessageBundle = loadMessageBundle; +function config(opts) { + if (opts) { + if (isString(opts.locale)) { + options.locale = opts.locale.toLowerCase(); + options.language = options.locale; + resolvedLanguage = undefined; + resolvedBundles = Object.create(null); + } + if (opts.messageFormat !== undefined) { + options.messageFormat = opts.messageFormat; + } + if (opts.bundleFormat === BundleFormat.standalone && options.languagePackSupport === true) { + options.languagePackSupport = false; + } + } + isPseudo = options.locale === 'pseudo'; + return loadMessageBundle; +} +exports.config = config; diff --git a/build/tsconfig.json b/build/tsconfig.json index df15ccdd1be..f075fe3d4f5 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -14,7 +14,8 @@ "checkJs": true, "strict": true, "noUnusedLocals": true, - "noUnusedParameters": true + "noUnusedParameters": true, + "newLine": "lf" }, "include": [ "**/*.ts" diff --git a/build/win32/code.iss b/build/win32/code.iss index 15293a0c5cf..50dcf76b882 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1,7 +1,8 @@ +#define RootLicenseFileName FileExists(RepoDir + '\LICENSE.rtf') ? 'LICENSE.rtf' : 'LICENSE.txt' #define LocalizedLanguageFile(Language = "") \ DirExists(RepoDir + "\licenses") && Language != "" \ ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.rtf"') \ - : '; LicenseFile: "' + RepoDir + '\LICENSE.rtf"' + : '; LicenseFile: "' + RepoDir + '\' + RootLicenseFileName + '"' [Setup] AppId={#AppId} @@ -32,6 +33,7 @@ VersionInfoVersion={#RawVersion} ShowLanguageDialog=auto ArchitecturesAllowed={#ArchitecturesAllowed} ArchitecturesInstallIn64BitMode={#ArchitecturesInstallIn64BitMode} +WizardStyle=modern #ifdef Sign SignTool=esrp @@ -45,7 +47,7 @@ DefaultDirName={pf}\{#DirName} #endif [Languages] -Name: "english"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.isl,{#RepoDir}\build\win32\i18n\messages.en.isl" {#LocalizedLanguageFile} +Name: "english"; MessagesFile: "compiler:Default.isl,{#RepoDir}\build\win32\i18n\messages.en.isl" {#LocalizedLanguageFile} Name: "german"; MessagesFile: "compiler:Languages\German.isl,{#RepoDir}\build\win32\i18n\messages.de.isl" {#LocalizedLanguageFile("deu")} Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl,{#RepoDir}\build\win32\i18n\messages.es.isl" {#LocalizedLanguageFile("esp")} Name: "french"; MessagesFile: "compiler:Languages\French.isl,{#RepoDir}\build\win32\i18n\messages.fr.isl" {#LocalizedLanguageFile("fra")} @@ -55,6 +57,9 @@ Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl,{#RepoDir}\build\ Name: "korean"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.ko.isl,{#RepoDir}\build\win32\i18n\messages.ko.isl" {#LocalizedLanguageFile("kor")} Name: "simplifiedChinese"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.zh-cn.isl,{#RepoDir}\build\win32\i18n\messages.zh-cn.isl" {#LocalizedLanguageFile("chs")} Name: "traditionalChinese"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.zh-tw.isl,{#RepoDir}\build\win32\i18n\messages.zh-tw.isl" {#LocalizedLanguageFile("cht")} +Name: "brazilianPortuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl,{#RepoDir}\build\win32\i18n\messages.pt-br.isl" {#LocalizedLanguageFile("ptb")} +Name: "hungarian"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.hu.isl,{#RepoDir}\build\win32\i18n\messages.hu.isl" {#LocalizedLanguageFile("hun")} +Name: "turkish"; MessagesFile: "compiler:Languages\Turkish.isl,{#RepoDir}\build\win32\i18n\messages.tr.isl" {#LocalizedLanguageFile("trk")} [InstallDelete] Type: filesandordirs; Name: "{app}\resources\app\out"; Check: IsNotUpdate @@ -84,7 +89,7 @@ Source: "{#ProductJsonPath}"; DestDir: "{code:GetDestDir}\resources\app"; Flags: [Icons] Name: "{group}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; AppUserModelID: "{#AppUserId}" -Name: "{commondesktop}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; Tasks: desktopicon; AppUserModelID: "{#AppUserId}" +Name: "{autodesktop}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; Tasks: desktopicon; AppUserModelID: "{#AppUserId}" Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; Tasks: quicklaunchicon; AppUserModelID: "{#AppUserId}" [Run] @@ -103,6 +108,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ascx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASCX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.asp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -110,6 +116,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.asp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASP}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.aspx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -117,6 +124,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.aspx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASPX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -124,6 +132,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_login\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -131,6 +140,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_login\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Login}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_logout\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -138,6 +148,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_logout\OpenWith Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Logout}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_profile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -145,6 +156,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_profile\OpenWit Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bashrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -152,6 +164,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bashrc\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bib\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -159,6 +172,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bib\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,BibTeX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bowerrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -166,13 +180,22 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bowerrc\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bower RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\bower.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c++\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.c++"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.c"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\c.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -180,6 +203,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cc\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -187,6 +211,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -194,6 +219,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -201,6 +227,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljs\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ClojureScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -208,6 +235,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CLJX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clojure\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -215,6 +243,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clojure\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.code-workspace\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -222,6 +251,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.code-workspace\OpenW Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Code Workspace}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.coffee\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -229,6 +259,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.coffee\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CoffeeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.config\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -236,13 +267,22 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.config\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Configuration}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.containerfile"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Containerfile}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.cpp"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -250,6 +290,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C#}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\csharp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cshtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -257,6 +298,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cshtml\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CSHTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csproj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -264,6 +306,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csproj\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C# Project}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.css\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -271,6 +314,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.css\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CSS}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\css.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -278,6 +322,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\csharp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ctp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -285,6 +330,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ctp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CakePHP Template}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cxx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -292,6 +338,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cxx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dockerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -299,6 +346,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dockerfile\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Dockerfile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dot\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -306,6 +354,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dot\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Dot}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dtd\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -313,6 +362,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dtd\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Document Type Definition}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.editorconfig\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -320,6 +370,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.editorconfig\OpenWit Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Editor Config}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.edn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -327,6 +378,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.edn\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Extensible Data Notation}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -334,6 +386,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyaml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Hiera Eyaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -341,6 +394,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Hiera Eyaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -348,6 +402,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F#}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -355,6 +410,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsi\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Signature}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsscript\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -362,6 +418,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsscript\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -369,6 +426,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gemspec\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -376,6 +434,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gemspec\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Gemspec}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\ruby.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitattributes\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -384,6 +443,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitat Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitconfig\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -392,6 +452,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitco Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitignore\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -400,6 +461,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitig Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.go\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -407,6 +469,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.go\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Go}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\go.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -414,6 +477,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\c.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.handlebars\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -421,6 +485,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.handlebars\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Handlebars}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hbs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -428,13 +493,22 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hbs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Handlebars}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h++\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.h++"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hh\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.hh"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hpp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -442,6 +516,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hpp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.htm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -449,6 +524,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.htm\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.html\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -456,6 +532,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.html\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hxx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -463,6 +540,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hxx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ini\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -470,6 +548,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ini\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,INI}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jade\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -477,6 +556,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jade\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Jade}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\jade.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jav\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -484,6 +564,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jav\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\java.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.java\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -491,6 +572,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.java\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\java.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.js\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -498,6 +580,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.js\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -505,6 +588,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\react.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jscsrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -512,6 +596,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jscsrc\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSCS RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshintrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -519,6 +604,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshintrc\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSHint RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshtm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -526,6 +612,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshtm\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript HTML Template}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.json\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -533,6 +620,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.json\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSON}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\json.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -540,6 +628,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java Server Pages}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.less\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -547,6 +636,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.less\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,LESS}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\less.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.lua\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -554,6 +644,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.lua\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Lua}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.m\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -561,6 +652,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.m\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Objective C}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.makefile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -568,6 +660,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.makefile\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Makefile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.markdown\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -575,6 +668,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.markdown\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.md\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -582,6 +676,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.md\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdoc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -589,6 +684,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdoc\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,MDoc}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdown\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -596,6 +692,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdown\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtext\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -603,6 +700,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtext\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtxt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -610,6 +708,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtxt\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdwn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -617,6 +716,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdwn\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkd\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -624,6 +724,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkd\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkdn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -631,6 +732,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkdn\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -638,6 +740,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ml\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,OCaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mli\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -645,6 +748,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mli\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,OCaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -652,6 +756,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mjs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.npmignore\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -660,6 +765,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmig Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.php\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -667,6 +773,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.php\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PHP}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\php.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.phtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -674,6 +781,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.phtml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PHP HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -681,6 +789,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl6\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -688,6 +797,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl6\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl 6}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -695,6 +805,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm6\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -702,6 +813,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm6\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl 6 Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pod\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -709,6 +821,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pod\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl POD}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -716,6 +829,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pp\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.profile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -723,6 +837,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.profile\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.properties\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -730,6 +845,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.properties\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Properties}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ps1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -737,6 +853,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ps1\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psd1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -744,6 +861,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psd1\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell Module Manifest}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psgi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -751,6 +869,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psgi\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl CGI}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psm1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -758,6 +877,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psm1\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.py\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -765,6 +885,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.py\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Python}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\python.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.r\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -772,6 +893,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.r\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rb\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -779,6 +901,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rb\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Ruby}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\ruby.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rhistory\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -786,6 +909,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rhistory\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R History}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rprofile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -793,6 +917,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rprofile\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -800,6 +925,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Rust}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -807,6 +933,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rt\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Rich Text}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.scss\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -814,6 +941,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.scss\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Sass}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\sass.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -821,6 +949,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sh\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SH}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.shtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -828,6 +957,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.shtml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SHTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -835,6 +965,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\sql.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svg\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -842,6 +973,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svg\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVG}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -849,6 +981,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVGZ}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -856,6 +989,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tex\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -863,6 +997,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tex\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,LaTeX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ts\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -870,6 +1005,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ts\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,TypeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\typescript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -877,6 +1013,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,TypeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\react.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.txt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -884,6 +1021,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.txt\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Text}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vb\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -891,6 +1029,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vb\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Visual Basic}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vue\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -898,6 +1037,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vue\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,VUE}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\vue.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -905,6 +1045,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxi\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX Include}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxl\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -912,6 +1053,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxl\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX Localization}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -919,6 +1061,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -926,6 +1069,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xaml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,XAML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -933,6 +1077,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xml\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,XML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -940,6 +1085,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yaml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Yaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -947,6 +1093,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yml\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Yaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.zsh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -954,14 +1101,17 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.zsh\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ZSH}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,{#NameLong}}"; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico" +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe"; ValueType: none; ValueName: ""; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico" +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey @@ -1003,7 +1153,7 @@ begin Result := True; #if "user" == InstallTarget - if not WizardSilent() and IsAdminLoggedOn() then begin + if not WizardSilent() and IsAdmin() then begin if MsgBox('This User Installer is not meant to be run as an Administrator. If you would like to install VS Code for all users in this system, download the System Installer instead from https://code.visualstudio.com. Are you sure you want to continue?', mbError, MB_OKCANCEL) = IDCANCEL then begin Result := False; end; @@ -1011,7 +1161,7 @@ begin #endif #if "user" == InstallTarget - #if "ia32" == Arch + #if "ia32" == Arch || "arm64" == Arch #define IncompatibleArchRootKey "HKLM32" #else #define IncompatibleArchRootKey "HKLM64" @@ -1121,7 +1271,7 @@ begin end; end; -// http://stackoverflow.com/a/23838239/261019 +// https://stackoverflow.com/a/23838239/261019 procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String); var i, p: Integer; diff --git a/build/win32/i18n/Default.hu.isl b/build/win32/i18n/Default.hu.isl new file mode 100644 index 00000000000..8c57d20a59a --- /dev/null +++ b/build/win32/i18n/Default.hu.isl @@ -0,0 +1,366 @@ +;Inno Setup version 6.0.3+ Hungarian messages +;Based on the translation of Kornl Pl, kornelpal@gmail.com +;Istvn Szab, E-mail: istvanszabo890629@gmail.com +; +; To download user-contributed translations of this file, go to: +; http://www.jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). + +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=Magyar +LanguageID=$040E +LanguageCodePage=1250 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=8 +;WelcomeFontName=Verdana +;WelcomeFontSize=12 +;TitleFontName=Arial CE +;TitleFontSize=29 +;CopyrightFontName=Arial CE +;CopyrightFontSize=8 + +[Messages] + +; *** Application titles +SetupAppTitle=Telept +SetupWindowTitle=%1 - Telept +UninstallAppTitle=Eltvolt +UninstallAppFullTitle=%1 Eltvolt + +; *** Misc. common +InformationTitle=Informcik +ConfirmTitle=Megerst +ErrorTitle=Hiba + +; *** SetupLdr messages +SetupLdrStartupMessage=%1 teleptve lesz. Szeretn folytatni? +LdrCannotCreateTemp=tmeneti fjl ltrehozsa nem lehetsges. A telepts megszaktva +LdrCannotExecTemp=Fjl futtatsa nem lehetsges az tmeneti knyvtrban. A telepts megszaktva +HelpTextNote= + +; *** Startup error messages +LastErrorMessage=%1.%n%nHiba %2: %3 +SetupFileMissing=A(z) %1 fjl hinyzik a telept knyvtrbl. Krem hrtsa el a problmt, vagy szerezzen be egy msik pldnyt a programbl! +SetupFileCorrupt=A teleptsi fjlok srltek. Krem, szerezzen be j msolatot a programbl! +SetupFileCorruptOrWrongVer=A teleptsi fjlok srltek, vagy inkompatibilisek a telept ezen verzijval. Hrtsa el a problmt, vagy szerezzen be egy msik pldnyt a programbl! +InvalidParameter=A parancssorba tadott paramter rvnytelen:%n%n%1 +SetupAlreadyRunning=A Telept mr fut. +WindowsVersionNotSupported=A program nem tmogatja a Windows ezen verzijt. +WindowsServicePackRequired=A program futtatshoz %1 Service Pack %2 vagy jabb szksges. +NotOnThisPlatform=Ez a program nem futtathat %1 alatt. +OnlyOnThisPlatform=Ezt a programot %1 alatt kell futtatni. +OnlyOnTheseArchitectures=A program kizrlag a kvetkez processzor architektrkhoz tervezett Windows-on telepthet:%n%n%1 +WinVersionTooLowError=A program futtatshoz %1 %2 verzija vagy ksbbi szksges. +WinVersionTooHighError=Ez a program nem telepthet %1 %2 vagy ksbbire. +AdminPrivilegesRequired=Csak rendszergazdai mdban telepthet ez a program. +PowerUserPrivilegesRequired=Csak rendszergazdaknt vagy kiemelt felhasznlknt telepthet ez a program. +SetupAppRunningError=A telept gy szlelte %1 jelenleg fut.%n%nZrja be az sszes pldnyt, majd kattintson az 'OK'-ra a folytatshoz, vagy a 'Mgse'-re a kilpshez. +UninstallAppRunningError=Az eltvolt gy szlelte %1 jelenleg fut.%n%nZrja be az sszes pldnyt, majd kattintson az 'OK'-ra a folytatshoz, vagy a 'Mgse'-re a kilpshez. + +; *** Startup questions +PrivilegesRequiredOverrideTitle=Teleptsi md kivlasztsa +PrivilegesRequiredOverrideInstruction=Vlasszon teleptsi mdot +PrivilegesRequiredOverrideText1=%1 telepthet az sszes felhasznlnak (rendszergazdai jogok szksgesek), vagy csak magnak. +PrivilegesRequiredOverrideText2=%1 csak magnak telepthet, vagy az sszes felhasznlnak (rendszergazdai jogok szksgesek). +PrivilegesRequiredOverrideAllUsers=Telepts &mindenkinek +PrivilegesRequiredOverrideAllUsersRecommended=Telepts &mindenkinek (ajnlott) +PrivilegesRequiredOverrideCurrentUser=Telepts csak &nekem +PrivilegesRequiredOverrideCurrentUserRecommended=Telepts csak &nekem (ajnlott) + +; *** Misc. errors +ErrorCreatingDir=A Telept nem tudta ltrehozni a(z) "%1" knyvtrat +ErrorTooManyFilesInDir=Nem hozhat ltre fjl a(z) "%1" knyvtrban, mert az mr tl sok fjlt tartalmaz + +; *** Setup common messages +ExitSetupTitle=Kilps a teleptbl +ExitSetupMessage=A telepts mg folyamatban van. Ha most kilp, a program nem kerl teleptsre.%n%nMsik alkalommal is futtathat a telepts befejezshez%n%nKilp a teleptbl? +AboutSetupMenuItem=&Nvjegy... +AboutSetupTitle=Telept nvjegye +AboutSetupMessage=%1 %2 verzi%n%3%n%nAz %1 honlapja:%n%4 +AboutSetupNote= +TranslatorNote= + +; *** Buttons +ButtonBack=< &Vissza +ButtonNext=&Tovbb > +ButtonInstall=&Telept +ButtonOK=OK +ButtonCancel=Mgse +ButtonYes=&Igen +ButtonYesToAll=&Mindet +ButtonNo=&Nem +ButtonNoToAll=&Egyiket se +ButtonFinish=&Befejezs +ButtonBrowse=&Tallzs... +ButtonWizardBrowse=T&allzs... +ButtonNewFolder=j &knyvtr + +; *** "Select Language" dialog messages +SelectLanguageTitle=Telept nyelvi bellts +SelectLanguageLabel=Vlassza ki a telepts alatt hasznlt nyelvet. + +; *** Common wizard text +ClickNext=A folytatshoz kattintson a 'Tovbb'-ra, a kilpshez a 'Mgse'-re. +BeveledLabel= +BrowseDialogTitle=Vlasszon knyvtrt +BrowseDialogLabel=Vlasszon egy knyvtrat az albbi listbl, majd kattintson az 'OK'-ra. +NewFolderName=j knyvtr + +; *** "Welcome" wizard page +WelcomeLabel1=dvzli a(z) [name] Teleptvarzslja. +WelcomeLabel2=A(z) [name/ver] teleptsre kerl a szmtgpn.%n%nAjnlott minden, egyb fut alkalmazs bezrsa a folytats eltt. + +; *** "Password" wizard page +WizardPassword=Jelsz +PasswordLabel1=Ez a telepts jelszval vdett. +PasswordLabel3=Krem adja meg a jelszt, majd kattintson a 'Tovbb'-ra. A jelszavak kis- s nagy bet rzkenyek lehetnek. +PasswordEditLabel=&Jelsz: +IncorrectPassword=Az n ltal megadott jelsz helytelen. Prblja jra. + +; *** "License Agreement" wizard page +WizardLicense=Licencszerzds +LicenseLabel=Olvassa el figyelmesen az informcikat folytats eltt. +LicenseLabel3=Krem, olvassa el az albbi licencszerzdst. A telepts folytatshoz, el kell fogadnia a szerzdst. +LicenseAccepted=&Elfogadom a szerzdst +LicenseNotAccepted=&Nem fogadom el a szerzdst + +; *** "Information" wizard pages +WizardInfoBefore=Informcik +InfoBeforeLabel=Olvassa el a kvetkez fontos informcikat a folytats eltt. +InfoBeforeClickLabel=Ha kszen ll, kattintson a 'Tovbb'-ra. +WizardInfoAfter=Informcik +InfoAfterLabel=Olvassa el a kvetkez fontos informcikat a folytats eltt. +InfoAfterClickLabel=Ha kszen ll, kattintson a 'Tovbb'-ra. + +; *** "User Information" wizard page +WizardUserInfo=Felhasznl adatai +UserInfoDesc=Krem, adja meg az adatait +UserInfoName=&Felhasznlnv: +UserInfoOrg=&Szervezet: +UserInfoSerial=&Sorozatszm: +UserInfoNameRequired=Meg kell adnia egy nevet. + +; *** "Select Destination Location" wizard page +WizardSelectDir=Vlasszon clknyvtrat +SelectDirDesc=Hova telepljn a(z) [name]? +SelectDirLabel3=A(z) [name] az albbi knyvtrba lesz teleptve. +SelectDirBrowseLabel=A folytatshoz, kattintson a 'Tovbb'-ra. Ha msik knyvtrat vlasztana, kattintson a 'Tallzs'-ra. +DiskSpaceGBLabel=At least [gb] GB szabad terletre van szksg. +DiskSpaceMBLabel=Legalbb [mb] MB szabad terletre van szksg. +CannotInstallToNetworkDrive=A Telept nem tud hlzati meghajtra telepteni. +CannotInstallToUNCPath=A Telept nem tud hlzati UNC elrsi tra telepteni. +InvalidPath=Teljes tvonalat adjon meg, a meghajt betjelvel; pldul:%n%nC:\Alkalmazs%n%nvagy egy hlzati tvonalat a kvetkez alakban:%n%n\\kiszolgl\megoszts +InvalidDrive=A kivlasztott meghajt vagy hlzati megoszts nem ltezik vagy nem elrhet. Vlasszon egy msikat. +DiskSpaceWarningTitle=Nincs elg szabad terlet +DiskSpaceWarning=A Teleptnek legalbb %1 KB szabad lemezterletre van szksge, viszont a kivlasztott meghajtn csupn %2 KB ll rendelkezsre.%n%nMindenkppen folytatja? +DirNameTooLong=A knyvtr neve vagy az tvonal tl hossz. +InvalidDirName=A knyvtr neve rvnytelen. +BadDirName32=A knyvtrak nevei ezen karakterek egyikt sem tartalmazhatjk:%n%n%1 +DirExistsTitle=A knyvtr mr ltezik +DirExists=A knyvtr:%n%n%1%n%nmr ltezik. Mindenkpp ide akar telepteni? +DirDoesntExistTitle=A knyvtr nem ltezik +DirDoesntExist=A knyvtr:%n%n%1%n%nnem ltezik. Szeretn ltrehozni? + +; *** "Select Components" wizard page +WizardSelectComponents=sszetevk kivlasztsa +SelectComponentsDesc=Mely sszetevk kerljenek teleptsre? +SelectComponentsLabel2=Jellje ki a teleptend sszetevket; trlje a telepteni nem kvnt sszetevket. Kattintson a 'Tovbb'-ra, ha kszen ll a folytatsra. +FullInstallation=Teljes telepts +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=Szoksos telepts +CustomInstallation=Egyni telepts +NoUninstallWarningTitle=Ltez sszetev +NoUninstallWarning=A telept gy tallta, hogy a kvetkez sszetevk mr teleptve vannak a szmtgpre:%n%n%1%n%nEzen sszetevk kijellsnek trlse, nem tvoltja el azokat a szmtgprl.%n%nMindenkppen folytatja? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=A jelenlegi kijells legalbb [gb] GB lemezterletet ignyel. +ComponentsDiskSpaceMBLabel=A jelenlegi kijells legalbb [mb] MB lemezterletet ignyel. + +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=Tovbbi feladatok +SelectTasksDesc=Mely kiegszt feladatok kerljenek vgrehajtsra? +SelectTasksLabel2=Jellje ki, mely kiegszt feladatokat hajtsa vgre a Telept a(z) [name] teleptse sorn, majd kattintson a 'Tovbb'-ra. + +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup=Start Men knyvtra +SelectStartMenuFolderDesc=Hova helyezze a Telept a program parancsikonjait? +SelectStartMenuFolderLabel3=A Telept a program parancsikonjait a Start men kvetkez mappjban fogja ltrehozni. +SelectStartMenuFolderBrowseLabel=A folytatshoz kattintson a 'Tovbb'-ra. Ha msik mappt vlasztana, kattintson a 'Tallzs'-ra. +MustEnterGroupName=Meg kell adnia egy mappanevet. +GroupNameTooLong=A knyvtr neve vagy az tvonal tl hossz. +InvalidGroupName=A knyvtr neve rvnytelen. +BadGroupName=A knyvtrak nevei ezen karakterek egyikt sem tartalmazhatjk:%n%n%1 +NoProgramGroupCheck2=&Ne hozzon ltre mappt a Start menben + +; *** "Ready to Install" wizard page +WizardReady=Kszen llunk a teleptsre +ReadyLabel1=A Telept kszen ll, a(z) [name] szmtgpre teleptshez. +ReadyLabel2a=Kattintson a 'Telepts'-re a folytatshoz, vagy a "Vissza"-ra a belltsok ttekintshez vagy megvltoztatshoz. +ReadyLabel2b=Kattintson a 'Telepts'-re a folytatshoz. +ReadyMemoUserInfo=Felhasznl adatai: +ReadyMemoDir=Telepts clknyvtra: +ReadyMemoType=Telepts tpusa: +ReadyMemoComponents=Vlasztott sszetevk: +ReadyMemoGroup=Start men mappja: +ReadyMemoTasks=Kiegszt feladatok: + +; *** "Preparing to Install" wizard page +WizardPreparing=Felkszls a teleptsre +PreparingDesc=A Telept felkszl a(z) [name] szmtgpre trtn teleptshez. +PreviousInstallNotCompleted=gy korbbi program teleptse/eltvoltsa nem fejezdtt be. jra kell indtania a szmtgpt a msik telepts befejezshez.%n%nA szmtgpe jraindtsa utn ismt futtassa a Teleptt a(z) [name] teleptsnek befejezshez. +CannotContinue=A telepts nem folytathat. A kilpshez kattintson a 'Mgse'-re +ApplicationsFound=A kvetkez alkalmazsok olyan fjlokat hasznlnak, amelyeket a Teleptnek frissteni kell. Ajnlott, hogy engedlyezze a Teleptnek ezen alkalmazsok automatikus bezrst. +ApplicationsFound2=A kvetkez alkalmazsok olyan fjlokat hasznlnak, amelyeket a Teleptnek frissteni kell. Ajnlott, hogy engedlyezze a Teleptnek ezen alkalmazsok automatikus bezrst. A telepts befejezse utn a Telept megksrli az alkalmazsok jraindtst. +CloseApplications=&Alkalmazsok automatikus bezrsa +DontCloseApplications=&Ne zrja be az alkalmazsokat +ErrorCloseApplications=A Telept nem tudott minden alkalmazst automatikusan bezrni. A folytats eltt ajnlott minden, a Telept ltal frisstend fjlokat hasznl alkalmazst bezrni. +PrepareToInstallNeedsRestart=A teleptnek jra kell indtania a szmtgpet. jraindtst kveten, futtassa jbl a teleptt, a [name] teleptsnek befejezshez .%n%njra szeretn indtani most a szmtgpet? + +; *** "Installing" wizard page +WizardInstalling=Telepts +InstallingLabel=Krem vrjon, amg a(z) [name] teleptse zajlik. + +; *** "Setup Completed" wizard page +FinishedHeadingLabel=A(z) [name] teleptsnek befejezse +FinishedLabelNoIcons=A Telept vgzett a(z) [name] teleptsvel. +FinishedLabel=A Telept vgzett a(z) [name] teleptsvel. Az alkalmazst a ltrehozott ikonok kivlasztsval indthatja. +ClickFinish=Kattintson a 'Befejezs'-re a kilpshez. +FinishedRestartLabel=A(z) [name] teleptsnek befejezshez jra kell indtani a szmtgpet. jraindtja most? +FinishedRestartMessage=A(z) [name] teleptsnek befejezshez, a Teleptnek jra kell indtani a szmtgpet.%n%njraindtja most? +ShowReadmeCheck=Igen, szeretnm elolvasni a FONTOS fjlt +YesRadio=&Igen, jraindts most +NoRadio=&Nem, ksbb indtom jra +; used for example as 'Run MyProg.exe' +RunEntryExec=%1 futtatsa +; used for example as 'View Readme.txt' +RunEntryShellExec=%1 megtekintse + +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=A Teleptnek szksge van a kvetkez lemezre +SelectDiskLabel2=Helyezze be a(z) %1. lemezt s kattintson az 'OK'-ra.%n%nHa a fjlok a lemez egy a megjelentettl klnbz mappjban tallhatk, rja be a helyes tvonalat vagy kattintson a 'Tallzs'-ra. +PathLabel=&tvonal: +FileNotInDir2=A(z) "%1" fjl nem tallhat a kvetkez helyen: "%2". Helyezze be a megfelel lemezt vagy vlasszon egy msik mappt. +SelectDirectoryLabel=Adja meg a kvetkez lemez helyt. + +; *** Installation phase messages +SetupAborted=A telepts nem fejezdtt be.%n%nHrtsa el a hibt s futtassa jbl a Teleptt. +AbortRetryIgnoreSelectAction=Vlasszon mveletet +AbortRetryIgnoreRetry=&jra +AbortRetryIgnoreIgnore=&Hiba elvetse s folytats +AbortRetryIgnoreCancel=Telepts megszaktsa + +; *** Installation status messages +StatusClosingApplications=Alkalmazsok bezrsa... +StatusCreateDirs=Knyvtrak ltrehozsa... +StatusExtractFiles=Fjlok kibontsa... +StatusCreateIcons=Parancsikonok ltrehozsa... +StatusCreateIniEntries=INI bejegyzsek ltrehozsa... +StatusCreateRegistryEntries=Rendszerler bejegyzsek ltrehozsa... +StatusRegisterFiles=Fjlok regisztrlsa... +StatusSavingUninstall=Eltvolt informcik mentse... +StatusRunProgram=Telepts befejezse... +StatusRestartingApplications=Alkalmazsok jraindtsa... +StatusRollback=Vltoztatsok visszavonsa... + +; *** Misc. errors +ErrorInternal2=Bels hiba: %1 +ErrorFunctionFailedNoCode=Sikertelen %1 +ErrorFunctionFailed=Sikertelen %1; kd: %2 +ErrorFunctionFailedWithMessage=Sikertelen %1; kd: %2.%n%3 +ErrorExecutingProgram=Nem hajthat vgre a fjl:%n%1 + +; *** Registry errors +ErrorRegOpenKey=Nem nyithat meg a rendszerler kulcs:%n%1\%2 +ErrorRegCreateKey=Nem hozhat ltre a rendszerler kulcs:%n%1\%2 +ErrorRegWriteKey=Nem mdosthat a rendszerler kulcs:%n%1\%2 + +; *** INI errors +ErrorIniEntry=Bejegyzs ltrehozsa sikertelen a kvetkez INI fjlban: "%1". + +; *** File copying errors +FileAbortRetryIgnoreSkipNotRecommended=&Fjl kihagysa (nem ajnlott) +FileAbortRetryIgnoreIgnoreNotRecommended=&Hiba elvetse s folytats (nem ajnlott) +SourceIsCorrupted=A forrsfjl megsrlt +SourceDoesntExist=A(z) "%1" forrsfjl nem ltezik +ExistingFileReadOnly2=A fjl csak olvashatknt van jellve. +ExistingFileReadOnlyRetry=Csak &olvashat tulajdonsg eltvoltsa s jra prblkozs +ExistingFileReadOnlyKeepExisting=&Ltez fjl megtartsa +ErrorReadingExistingDest=Hiba lpett fel a fjl olvassa kzben: +FileExists=A fjl mr ltezik.%n%nFell kvnja rni? +ExistingFileNewer=A ltez fjl jabb a teleptsre kerlnl. Ajnlott a ltez fjl megtartsa.%n%nMeg kvnja tartani a ltez fjlt? +ErrorChangingAttr=Hiba lpett fel a fjl attribtumnak mdostsa kzben: +ErrorCreatingTemp=Hiba lpett fel a fjl teleptsi knyvtrban trtn ltrehozsa kzben: +ErrorReadingSource=Hiba lpett fel a forrsfjl olvassa kzben: +ErrorCopying=Hiba lpett fel a fjl msolsa kzben: +ErrorReplacingExistingFile=Hiba lpett fel a ltez fjl cserje kzben: +ErrorRestartReplace=A fjl cserje az jraindts utn sikertelen volt: +ErrorRenamingTemp=Hiba lpett fel fjl teleptsi knyvtrban trtn tnevezse kzben: +ErrorRegisterServer=Nem lehet regisztrlni a DLL-t/OCX-et: %1 +ErrorRegSvr32Failed=Sikertelen RegSvr32. A visszaadott kd: %1 +ErrorRegisterTypeLib=Nem lehet regisztrlni a tpustrat: %1 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32-bit +UninstallDisplayNameMark64Bit=64-bit +UninstallDisplayNameMarkAllUsers=Minden felhasznl +UninstallDisplayNameMarkCurrentUser=Jelenlegi felhasznl + +; *** Post-installation errors +ErrorOpeningReadme=Hiba lpett fel a FONTOS fjl megnyitsa kzben. +ErrorRestartingComputer=A Telept nem tudta jraindtani a szmtgpet. Indtsa jra kzileg. + +; *** Uninstaller messages +UninstallNotFound=A(z) "%1" fjl nem ltezik. Nem tvolthat el. +UninstallOpenError=A(z) "%1" fjl nem nyithat meg. Nem tvolthat el. +UninstallUnsupportedVer=A(z) "%1" eltvoltsi naplfjl formtumt nem tudja felismerni az eltvolt jelen verzija. Az eltvolts nem folytathat +UninstallUnknownEntry=Egy ismeretlen bejegyzs (%1) tallhat az eltvoltsi naplfjlban +ConfirmUninstall=Biztosan el kvnja tvoltani a(z) %1 programot s minden sszetevjt? +UninstallOnlyOnWin64=Ezt a teleptst csak 64-bites Windowson lehet eltvoltani. +OnlyAdminCanUninstall=Ezt a teleptst csak adminisztrcis jogokkal rendelkez felhasznl tvolthatja el. +UninstallStatusLabel=Legyen trelemmel, amg a(z) %1 szmtgprl trtn eltvoltsa befejezdik. +UninstalledAll=A(z) %1 sikeresen el lett tvoltva a szmtgprl. +UninstalledMost=A(z) %1 eltvoltsa befejezdtt.%n%nNhny elemet nem lehetett eltvoltani. Trlje kzileg. +UninstalledAndNeedsRestart=A(z) %1 eltvoltsnak befejezshez jra kell indtania a szmtgpt.%n%njraindtja most? +UninstallDataCorrupted=A(z) "%1" fjl srlt. Nem tvolthat el. + +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=Trli a megosztott fjlt? +ConfirmDeleteSharedFile2=A rendszer azt jelzi, hogy a kvetkez megosztott fjlra mr nincs szksge egyetlen programnak sem. Eltvoltja a megosztott fjlt?%n%nHa ms programok mg mindig hasznljk a megosztott fjlt, akkor az eltvoltsa utn lehet, hogy nem fognak megfelelen mkdni. Ha bizonytalan, vlassza a Nemet. A fjl megtartsa nem okoz problmt a rendszerben. +SharedFileNameLabel=Fjlnv: +SharedFileLocationLabel=Helye: +WizardUninstalling=Eltvolts llapota +StatusUninstalling=%1 eltvoltsa... + +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=%1 teleptse. +ShutdownBlockReasonUninstallingApp=%1 eltvoltsa. + +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. + +[CustomMessages] + +NameAndVersion=%1, verzi: %2 +AdditionalIcons=Tovbbi parancsikonok: +CreateDesktopIcon=&Asztali ikon ltrehozsa +CreateQuickLaunchIcon=&Gyorsindt parancsikon ltrehozsa +ProgramOnTheWeb=%1 az interneten +UninstallProgram=Eltvolts - %1 +LaunchProgram=Indts %1 +AssocFileExtension=A(z) %1 &trstsa a(z) %2 fjlkiterjesztssel +AssocingFileExtension=A(z) %1 trstsa a(z) %2 fjlkiterjesztssel... +AutoStartProgramGroupDescription=Indtpult: +AutoStartProgram=%1 automatikus indtsa +AddonHostProgramNotFound=A(z) %1 nem tallhat a kivlasztott knyvtrban.%n%nMindenkppen folytatja? diff --git a/build/win32/i18n/Default.isl b/build/win32/i18n/Default.isl deleted file mode 100644 index 370da6b37c7..00000000000 --- a/build/win32/i18n/Default.isl +++ /dev/null @@ -1,336 +0,0 @@ -; *** Inno Setup version 5.5.3+ English messages *** -; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ -; -; Note: When translating this text, do not add periods (.) to the end of -; messages that didn't have them already, because on those messages Inno -; Setup adds the periods automatically (appending a period would result in -; two periods being displayed). - -[LangOptions] -; The following three entries are very important. Be sure to read and -; understand the '[LangOptions] section' topic in the help file. -LanguageName=English -LanguageID=$0409 -LanguageCodePage=0 -; If the language you are translating to requires special font faces or -; sizes, uncomment any of the following entries and change them accordingly. -;DialogFontName= -;DialogFontSize=8 -;WelcomeFontName=Verdana -;WelcomeFontSize=12 -;TitleFontName=Arial -;TitleFontSize=29 -;CopyrightFontName=Arial -;CopyrightFontSize=8 - -[Messages] - -; *** Application titles -SetupAppTitle=Setup -SetupWindowTitle=Setup - %1 -UninstallAppTitle=Uninstall -UninstallAppFullTitle=%1 Uninstall - -; *** Misc. common -InformationTitle=Information -ConfirmTitle=Confirm -ErrorTitle=Error - -; *** SetupLdr messages -SetupLdrStartupMessage=This will install %1. Do you wish to continue? -LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted -LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted - -; *** Startup error messages -LastErrorMessage=%1.%n%nError %2: %3 -SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program. -SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program. -SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program. -InvalidParameter=An invalid parameter was passed on the command line:%n%n%1 -SetupAlreadyRunning=Setup is already running. -WindowsVersionNotSupported=This program does not support the version of Windows your computer is running. -WindowsServicePackRequired=This program requires %1 Service Pack %2 or later. -NotOnThisPlatform=This program will not run on %1. -OnlyOnThisPlatform=This program must be run on %1. -OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1 -MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1. -WinVersionTooLowError=This program requires %1 version %2 or later. -WinVersionTooHighError=This program cannot be installed on %1 version %2 or later. -AdminPrivilegesRequired=You must be logged in as an administrator when installing this program. -PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program. -SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. - -; *** Misc. errors -ErrorCreatingDir=Setup was unable to create the directory "%1" -ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files - -; *** Setup common messages -ExitSetupTitle=Exit Setup -ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup? -AboutSetupMenuItem=&About Setup... -AboutSetupTitle=About Setup -AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4 -AboutSetupNote= -TranslatorNote= - -; *** Buttons -ButtonBack=< &Back -ButtonNext=&Next > -ButtonInstall=&Install -ButtonOK=OK -ButtonCancel=Cancel -ButtonYes=&Yes -ButtonYesToAll=Yes to &All -ButtonNo=&No -ButtonNoToAll=N&o to All -ButtonFinish=&Finish -ButtonBrowse=&Browse... -ButtonWizardBrowse=B&rowse... -ButtonNewFolder=&Make New Folder - -; *** "Select Language" dialog messages -SelectLanguageTitle=Select Setup Language -SelectLanguageLabel=Select the language to use during the installation: - -; *** Common wizard text -ClickNext=Click Next to continue, or Cancel to exit Setup. -BeveledLabel= -BrowseDialogTitle=Browse For Folder -BrowseDialogLabel=Select a folder in the list below, then click OK. -NewFolderName=New Folder - -; *** "Welcome" wizard page -WelcomeLabel1=Welcome to the [name] Setup Wizard -WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing. - -; *** "Password" wizard page -WizardPassword=Password -PasswordLabel1=This installation is password protected. -PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive. -PasswordEditLabel=&Password: -IncorrectPassword=The password you entered is not correct. Please try again. - -; *** "License Agreement" wizard page -WizardLicense=License Agreement -LicenseLabel=Please read the following important information before continuing. -LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation. -LicenseAccepted=I &accept the agreement -LicenseNotAccepted=I &do not accept the agreement - -; *** "Information" wizard pages -WizardInfoBefore=Information -InfoBeforeLabel=Please read the following important information before continuing. -InfoBeforeClickLabel=When you are ready to continue with Setup, click Next. -WizardInfoAfter=Information -InfoAfterLabel=Please read the following important information before continuing. -InfoAfterClickLabel=When you are ready to continue with Setup, click Next. - -; *** "User Information" wizard page -WizardUserInfo=User Information -UserInfoDesc=Please enter your information. -UserInfoName=&User Name: -UserInfoOrg=&Organization: -UserInfoSerial=&Serial Number: -UserInfoNameRequired=You must enter a name. - -; *** "Select Destination Location" wizard page -WizardSelectDir=Select Destination Location -SelectDirDesc=Where should [name] be installed? -SelectDirLabel3=Setup will install [name] into the following folder. -SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. -DiskSpaceMBLabel=At least [mb] MB of free disk space is required. -CannotInstallToNetworkDrive=Setup cannot install to a network drive. -CannotInstallToUNCPath=Setup cannot install to a UNC path. -InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share -InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another. -DiskSpaceWarningTitle=Not Enough Disk Space -DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway? -DirNameTooLong=The folder name or path is too long. -InvalidDirName=The folder name is not valid. -BadDirName32=Folder names cannot include any of the following characters:%n%n%1 -DirExistsTitle=Folder Exists -DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway? -DirDoesntExistTitle=Folder Does Not Exist -DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created? - -; *** "Select Components" wizard page -WizardSelectComponents=Select Components -SelectComponentsDesc=Which components should be installed? -SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue. -FullInstallation=Full installation -; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=Compact installation -CustomInstallation=Custom installation -NoUninstallWarningTitle=Components Exist -NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway? -ComponentSize1=%1 KB -ComponentSize2=%1 MB -ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space. - -; *** "Select Additional Tasks" wizard page -WizardSelectTasks=Select Additional Tasks -SelectTasksDesc=Which additional tasks should be performed? -SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next. - -; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup=Select Start Menu Folder -SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts? -SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder. -SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. -MustEnterGroupName=You must enter a folder name. -GroupNameTooLong=The folder name or path is too long. -InvalidGroupName=The folder name is not valid. -BadGroupName=The folder name cannot include any of the following characters:%n%n%1 -NoProgramGroupCheck2=&Don't create a Start Menu folder - -; *** "Ready to Install" wizard page -WizardReady=Ready to Install -ReadyLabel1=Setup is now ready to begin installing [name] on your computer. -ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings. -ReadyLabel2b=Click Install to continue with the installation. -ReadyMemoUserInfo=User information: -ReadyMemoDir=Destination location: -ReadyMemoType=Setup type: -ReadyMemoComponents=Selected components: -ReadyMemoGroup=Start Menu folder: -ReadyMemoTasks=Additional tasks: - -; *** "Preparing to Install" wizard page -WizardPreparing=Preparing to Install -PreparingDesc=Setup is preparing to install [name] on your computer. -PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name]. -CannotContinue=Setup cannot continue. Please click Cancel to exit. -ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. -ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications. -CloseApplications=&Automatically close the applications -DontCloseApplications=&Do not close the applications -ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing. - -; *** "Installing" wizard page -WizardInstalling=Installing -InstallingLabel=Please wait while Setup installs [name] on your computer. - -; *** "Setup Completed" wizard page -FinishedHeadingLabel=Completing the [name] Setup Wizard -FinishedLabelNoIcons=Setup has finished installing [name] on your computer. -FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed icons. -ClickFinish=Click Finish to exit Setup. -FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now? -FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now? -ShowReadmeCheck=Yes, I would like to view the README file -YesRadio=&Yes, restart the computer now -NoRadio=&No, I will restart the computer later -; used for example as 'Run MyProg.exe' -RunEntryExec=Run %1 -; used for example as 'View Readme.txt' -RunEntryShellExec=View %1 - -; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=Setup Needs the Next Disk -SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse. -PathLabel=&Path: -FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder. -SelectDirectoryLabel=Please specify the location of the next disk. - -; *** Installation phase messages -SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again. -EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation. - -; *** Installation status messages -StatusClosingApplications=Closing applications... -StatusCreateDirs=Creating directories... -StatusExtractFiles=Extracting files... -StatusCreateIcons=Creating shortcuts... -StatusCreateIniEntries=Creating INI entries... -StatusCreateRegistryEntries=Creating registry entries... -StatusRegisterFiles=Registering files... -StatusSavingUninstall=Saving uninstall information... -StatusRunProgram=Finishing installation... -StatusRestartingApplications=Restarting applications... -StatusRollback=Rolling back changes... - -; *** Misc. errors -ErrorInternal2=Internal error: %1 -ErrorFunctionFailedNoCode=%1 failed -ErrorFunctionFailed=%1 failed; code %2 -ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3 -ErrorExecutingProgram=Unable to execute file:%n%1 - -; *** Registry errors -ErrorRegOpenKey=Error opening registry key:%n%1\%2 -ErrorRegCreateKey=Error creating registry key:%n%1\%2 -ErrorRegWriteKey=Error writing to registry key:%n%1\%2 - -; *** INI errors -ErrorIniEntry=Error creating INI entry in file "%1". - -; *** File copying errors -FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation. -FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation. -SourceIsCorrupted=The source file is corrupted -SourceDoesntExist=The source file "%1" does not exist -ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation. -ErrorReadingExistingDest=An error occurred while trying to read the existing file: -FileExists=The file already exists.%n%nWould you like Setup to overwrite it? -ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file? -ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file: -ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: -ErrorReadingSource=An error occurred while trying to read the source file: -ErrorCopying=An error occurred while trying to copy a file: -ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: -ErrorRestartReplace=RestartReplace failed: -ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: -ErrorRegisterServer=Unable to register the DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 failed with exit code %1 -ErrorRegisterTypeLib=Unable to register the type library: %1 - -; *** Post-installation errors -ErrorOpeningReadme=An error occurred while trying to open the README file. -ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually. - -; *** Uninstaller messages -UninstallNotFound=File "%1" does not exist. Cannot uninstall. -UninstallOpenError=File "%1" could not be opened. Cannot uninstall -UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall -UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log -ConfirmUninstall=Are you sure you want to completely remove %1? Extensions and settings will not be removed. -UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows. -OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges. -UninstallStatusLabel=Please wait while %1 is removed from your computer. -UninstalledAll=%1 was successfully removed from your computer. -UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually. -UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now? -UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall - -; *** Uninstallation phase messages -ConfirmDeleteSharedFileTitle=Remove Shared File? -ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm. -SharedFileNameLabel=File name: -SharedFileLocationLabel=Location: -WizardUninstalling=Uninstall Status -StatusUninstalling=Uninstalling %1... - -; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=Installing %1. -ShutdownBlockReasonUninstallingApp=Uninstalling %1. - -; The custom messages below aren't used by Setup itself, but if you make -; use of them in your scripts, you'll want to translate them. - -[CustomMessages] - -NameAndVersion=%1 version %2 -AdditionalIcons=Additional icons: -CreateDesktopIcon=Create a &desktop icon -CreateQuickLaunchIcon=Create a &Quick Launch icon -ProgramOnTheWeb=%1 on the Web -UninstallProgram=Uninstall %1 -LaunchProgram=Launch %1 -AssocFileExtension=&Associate %1 with the %2 file extension -AssocingFileExtension=Associating %1 with the %2 file extension... -AutoStartProgramGroupDescription=Startup: -AutoStartProgram=Automatically start %1 -AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway? diff --git a/build/win32/i18n/Default.ko.isl b/build/win32/i18n/Default.ko.isl index a7c38d12b9f..0f1b1a7ccf5 100644 --- a/build/win32/i18n/Default.ko.isl +++ b/build/win32/i18n/Default.ko.isl @@ -1,12 +1,16 @@ -; *** Inno Setup version 5.5.3+ Korean messages *** -; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; *** Inno Setup version 6.0.0+ Korean messages *** ; +; 6.0.3+ Translator: SungDong Kim (acroedit@gmail.com) +; 5.5.3+ Translator: Domddol (domddol@gmail.com) +; Translation date: MAR 04, 2014 +; Contributors: Hansoo KIM (iryna7@gmail.com), Woong-Jae An (a183393@hanmail.net) +; Storage: http://www.jrsoftware.org/files/istrans/ +; ο ѱ Ģ ؼմϴ. ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). + [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. @@ -23,50 +27,68 @@ LanguageCodePage=949 ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 + [Messages] + ; *** Application titles SetupAppTitle=ġ -SetupWindowTitle=ġ - %1 +SetupWindowTitle=%1 ġ UninstallAppTitle= UninstallAppFullTitle=%1 + ; *** Misc. common InformationTitle= ConfirmTitle=Ȯ ErrorTitle= + ; *** SetupLdr messages -SetupLdrStartupMessage=׷ %1() ġ˴ϴ. Ͻðڽϱ? -LdrCannotCreateTemp=ӽ ϴ. ġ α׷ ߴܵǾϴ. -LdrCannotExecTemp=ӽ ͸ ϴ. ġ α׷ ߴܵǾϴ. +SetupLdrStartupMessage=%1() ġմϴ, Ͻðڽϱ? +LdrCannotCreateTemp=ӽ ϴ, ġ ߴմϴ +LdrCannotExecTemp=ӽ ϴ, ġ ߴմϴ +HelpTextNote= + ; *** Startup error messages LastErrorMessage=%1.%n%n %2: %3 -SetupFileMissing= %1() ġ ͸ Ǿϴ. ذϰų α׷ . -SetupFileCorrupt=ġ ջǾϴ. α׷ . -SetupFileCorruptOrWrongVer=ġ ջǾų ġ α׷ ȣȯ ʽϴ. ذϰų α׷ . -InvalidParameter=ٿ ߸ Ű ޵:%n%n%1 -SetupAlreadyRunning=ġ α׷ ̹ Դϴ. -WindowsVersionNotSupported= α׷ ǻͿ Windows ʽϴ. -WindowsServicePackRequired= α׷ ġϷ %1 %2 ̻ ʿմϴ. -NotOnThisPlatform= α׷ %1 ʽϴ. +SetupFileMissing=%1 ʽϴ, ذ ų ο ġ α׷ Ͻñ ٶϴ. +SetupFileCorrupt=ġ ջǾϴ, ο ġ α׷ Ͻñ ٶϴ. +SetupFileCorruptOrWrongVer=ġ ջ̰ų ġ ȣȯ ʽϴ, ذ ų ο ġ α׷ Ͻñ ٶϴ. +InvalidParameter=߸ Ű Դϴ:%n%n%1 +SetupAlreadyRunning=ġ ̹ Դϴ. +WindowsVersionNotSupported= α׷ Windows ʽϴ. +WindowsServicePackRequired= α׷ Ϸ %1 sp%2 ̻̾ մϴ. +NotOnThisPlatform= α׷ %1 ۵ ʽϴ. OnlyOnThisPlatform= α׷ %1 ؾ մϴ. -OnlyOnTheseArchitectures= α׷ μ Űó %n%n%1 Windows ġ ֽϴ. -MissingWOW64APIs= Windows ġ α׷ 64Ʈ ġϴ ʿ ϴ. ذϷ %1() ġϼ. -WinVersionTooLowError= α׷ ġϷ %1 %2 ̻ ʿմϴ. -WinVersionTooHighError= α׷ %1 %2 ̻󿡼 ġ ϴ. -AdminPrivilegesRequired= α׷ ġ ڷ αؾ մϴ. -PowerUserPrivilegesRequired= α׷ ġ ڳ ׷ αؾ մϴ. -SetupAppRunningError=ġ α׷ %1() ߽ϴ.%n%n ׸ νϽ ݰ Ϸ [Ȯ], Ϸ [] Ŭϼ. -UninstallAppRunningError= ۾ %1() ߽ϴ.%n%n ׸ νϽ ݰ Ϸ [Ȯ], Ϸ [] Ŭϼ. +OnlyOnTheseArchitectures= α׷ Ʒ ó ȣȯǴ Windows ġ ֽϴ:%n%n%1 +WinVersionTooLowError= α׷ %1 %2 ̻ ʿմϴ. +WinVersionTooHighError= α׷ %1 %2 ̻󿡼 ġ ϴ. +AdminPrivilegesRequired= α׷ ġϷ ڷ αؾ մϴ. +PowerUserPrivilegesRequired= α׷ ġϷ Ǵ ڷ αؾ մϴ. +SetupAppRunningError= %1() Դϴ!%n%n װ νϽ ݾ ֽʽÿ. ׷ Ϸ "Ȯ", Ϸ "" ŬϽʽÿ. +UninstallAppRunningError= %1() Դϴ!%n%n װ νϽ ݾ ֽʽÿ. ׷ Ϸ "Ȯ", Ϸ "" ŬϽʽÿ. + +; *** Startup questions +PrivilegesRequiredOverrideTitle=ġ +PrivilegesRequiredOverrideInstruction=ġ 带 ֽʽÿ +PrivilegesRequiredOverrideText1=%1 ( ʿ) Ǵ ڿ ġմϴ. +PrivilegesRequiredOverrideText2=%1 Ǵ ( ʿ) ġմϴ. +PrivilegesRequiredOverrideAllUsers= ڿ ġ(&A) +PrivilegesRequiredOverrideAllUsersRecommended= ڿ ġ(&A) (õ) +PrivilegesRequiredOverrideCurrentUser= ڿ ġ(&M) +PrivilegesRequiredOverrideCurrentUserRecommended= ڿ ġ(&M) (õ) + ; *** Misc. errors -ErrorCreatingDir=ġ α׷ ͸ "%1"() ϴ. -ErrorTooManyFilesInDir=͸ "%1" ʹ Ƿ ͸ ϴ. +ErrorCreatingDir="%1" ϴ. +ErrorTooManyFilesInDir="%1" ʹ ϴ. + ; *** Setup common messages -ExitSetupTitle=ġ -ExitSetupMessage=ġ Ϸ ʾҽϴ. ϸ α׷ ġ ʽϴ.%n%n߿ ġ α׷ ٽ Ͽ ġ ֽϴ.%n%nġ α׷ Ͻðڽϱ? -AboutSetupMenuItem=ġ α׷ (&A)... -AboutSetupTitle=ġ α׷ -AboutSetupMessage=%1 %2%n%3%n%n%1 Ȩ:%n%4 +ExitSetupTitle=ġ Ϸ +ExitSetupMessage=ġ Ϸ ʾҽϴ, ⼭ ġ ϸ α׷ ġ ʽϴ.%n%nġ ϷϷ ߿ ٽ ġ α׷ ؾ մϴ.%n%n׷ ġ Ͻðڽϱ? +AboutSetupMenuItem=ġ (&A)... +AboutSetupTitle=ġ +AboutSetupMessage=%1 %2%n%3%n%n%1 Ȩ :%n%4 AboutSetupNote= TranslatorNote= + ; *** Buttons ButtonBack=< ڷ(&B) ButtonNext=(&N) > @@ -75,224 +97,271 @@ ButtonOK=Ȯ ButtonCancel= ButtonYes=(&Y) ButtonYesToAll= (&A) -ButtonNo=ƴϿ(&N) -ButtonNoToAll= ƴϿ(&O) -ButtonFinish=ħ(&F) +ButtonNo=ƴϿ(&N) +ButtonNoToAll= ƴϿ(&O) +ButtonFinish=(&F) ButtonBrowse=ãƺ(&B)... -ButtonWizardBrowse=ãƺ(&R) +ButtonWizardBrowse=ãƺ(&R)... ButtonNewFolder= (&M) + ; *** "Select Language" dialog messages SelectLanguageTitle=ġ -SelectLanguageLabel=ġ ߿  ϼ. +SelectLanguageLabel=ġ  Ͻʽÿ. + ; *** Common wizard text -ClickNext=Ϸ [] Ŭϰ ġ α׷ Ϸ [] Ŭϼ. +ClickNext=Ϸ "" Ŭϰ ġ Ϸ "" Ŭմϴ. BeveledLabel= BrowseDialogTitle= ãƺ -BrowseDialogLabel=Ʒ Ͽ [Ȯ] Ŭϼ. +BrowseDialogLabel=Ʒ Ͽ "Ȯ" Ŭմϴ. NewFolderName= + ; *** "Welcome" wizard page WelcomeLabel1=[name] ġ -WelcomeLabel2= ǻͿ [name/ver]() ġմϴ.%n%nϱ ٸ α׷ ݴ ϴ. +WelcomeLabel2= ǻͿ [name/ver]() ġ Դϴ.%n%nġϱ ٸ α׷ ñ ٶϴ. + ; *** "Password" wizard page -WizardPassword=ȣ -PasswordLabel1= ġ ȣ ȣǰ ֽϴ. -PasswordLabel3=Ϸ ȣ Է [] Ŭϼ. ȣ ҹڸ մϴ. -PasswordEditLabel=ȣ(&P): -IncorrectPassword=Է ȣ ߸Ǿϴ. ٽ õϼ. +WizardPassword= ȣ +PasswordLabel1= ġ ȣ ȣǾ ֽϴ. +PasswordLabel3= ȣ Էϰ "" ŬϽʽÿ. ȣ ҹڸ ؾ մϴ. +PasswordEditLabel= ȣ(&P): +IncorrectPassword= ȣ Ȯ ʽϴ, ٽ ԷϽʽÿ. + ; *** "License Agreement" wizard page WizardLicense= -LicenseLabel=ϱ ߿ о . -LicenseLabel3= о ּ. ġ Ϸ ǿ ؾ մϴ. -LicenseAccepted=࿡ (&A) -LicenseNotAccepted=࿡ (&D) +LicenseLabel=ϱ ߿ оʽÿ. +LicenseLabel3= оʽÿ, ġ Ϸ ࿡ ؾ մϴ. +LicenseAccepted=մϴ(&A) +LicenseNotAccepted= ʽϴ(&D) + ; *** "Information" wizard pages WizardInfoBefore= -InfoBeforeLabel=ϱ ߿ о . -InfoBeforeClickLabel=ġ غ Ǹ [] Ŭմϴ. +InfoBeforeLabel=ϱ ߿ оʽÿ. +InfoBeforeClickLabel=ġ Ϸ "" ŬϽʽÿ. WizardInfoAfter= -InfoAfterLabel=ϱ ߿ о . -InfoAfterClickLabel=ġ غ Ǹ [] Ŭմϴ. +InfoAfterLabel=ϱ ߿ оʽÿ. +InfoAfterClickLabel=ġ Ϸ "" ŬϽʽÿ. + ; *** "User Information" wizard page WizardUserInfo= -UserInfoDesc= Էϼ. +UserInfoDesc= ԷϽʽÿ. UserInfoName= ̸(&U): UserInfoOrg=(&O): -UserInfoSerial=Ϸ ȣ(&S): -UserInfoNameRequired≠ Էؾ մϴ. +UserInfoSerial=ø ȣ(&S): +UserInfoNameRequired= ̸ ԷϽʽÿ. + ; *** "Select Destination Location" wizard page -WizardSelectDir= ġ -SelectDirDesc=[name]() ġϽðڽϱ? -SelectDirLabel3=ġ α׷ [name]() ġմϴ. -SelectDirBrowseLabel=Ϸ [] Ŭϼ. ٸ Ϸ [ãƺ] Ŭϼ. -DiskSpaceMBLabel= [mb]MB ũ ʿմϴ. -CannotInstallToNetworkDrive=ġ α׷ Ʈũ ̺꿡 ġ ϴ. -CannotInstallToUNCPath=ġ α׷ UNC ο ġ ϴ. -InvalidPath=̺ ڿ Բ ü θ Էؾ մϴ. :%n%nC:\APP%n%nǴ UNC :%n%n\\server\share -InvalidDrive= ̺곪 UNC ų ׸ ׼ ϴ. ٸ ̺곪 UNC ϼ. -DiskSpaceWarningTitle=ũ -DiskSpaceWarning=ġ α׷ ġϷ ġ  %1KB ʿ ̺ %2KBۿ ϴ.%n%n׷ Ͻðڽϱ? +WizardSelectDir=ġ ġ +SelectDirDesc=[name] ġ ġ Ͻʽÿ. +SelectDirLabel3= [name]() ġմϴ. +SelectDirBrowseLabel=Ϸ "", ٸ Ϸ "ãƺ" ŬϽʽÿ. +DiskSpaceGBLabel= α׷ ּ [gb] GB ũ ʿմϴ. +DiskSpaceMBLabel= α׷ ּ [mb] MB ũ ʿմϴ. +CannotInstallToNetworkDrive=Ʈũ ̺꿡 ġ ϴ. +CannotInstallToUNCPath=UNC ο ġ ϴ. +InvalidPath=̺ ڸ ü θ ԷϽʽÿ.%n : C:\APP %n%nǴ, UNC θ ԷϽʽÿ.%n : \\server\share +InvalidDrive= ̺ Ǵ UNC ʰų ׼ ϴ, ٸ θ Ͻʽÿ. +DiskSpaceWarningTitle=ũ մϴ +DiskSpaceWarning=ġ ּ %1 KB ũ ʿ, ̺ %2 KB ۿ ϴ.%n%n׷ Ͻðڽϱ? DirNameTooLong= ̸ Ǵ ΰ ʹ ϴ. -InvalidDirName= ̸ ߸Ǿϴ. -BadDirName32= ̸ %n%n%1 ڸ ϴ. -DirExistsTitle= -DirExists= %n%n%1%n%n() ̹ ֽϴ. ׷ ش ġϽðڽϱ? -DirDoesntExistTitle= -DirDoesntExist= %n%n%1%n%n() ϴ. ðڽϱ? +InvalidDirName= ̸ ȿ ʽϴ. +BadDirName32= ̸ ڸ ϴ:%n%n%1 +DirExistsTitle= մϴ +DirExists= %n%n%1%n%n() ̹ մϴ, ġϽðڽϱ? +DirDoesntExistTitle= ʽϴ +DirDoesntExist= %n%n%1%n%n() ʽϴ, ðڽϱ? + ; *** "Select Components" wizard page WizardSelectComponents= -SelectComponentsDesc= Ҹ ġϽðڽϱ? -SelectComponentsLabel2=ġ Ҵ ϰ ġ Ҵ 켼. غ Ǹ [] Ŭϼ. -FullInstallation=ü ġ +SelectComponentsDesc=ġ Ҹ Ͻʽÿ. +SelectComponentsLabel2=ʿ Ҵ üũϰ ʿ Ҵ üũ մϴ, Ϸ "" ŬϽʽÿ. +FullInstallation= ġ ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=Compact ġ +CompactInstallation=ּ ġ CustomInstallation= ġ -NoUninstallWarningTitle= Ұ -NoUninstallWarning=ġ α׷ %n%n%1%n%n() ǻͿ ̹ ġǾ ߽ϴ. ̷ Ҵ ص ŵ ʽϴ.%n%n׷ Ͻðڽϱ? -ComponentSize1=%1KB -ComponentSize2=%1MB -ComponentsDiskSpaceMBLabel= ؼ  [mb]MB ũ ʿմϴ. +NoUninstallWarningTitle= Ұ մϴ +NoUninstallWarning= Ұ ̹ ġǾ ֽϴ:%n%n%1%n%n , α׷ Ž ҵ ŵ ̴ϴ.%n%n׷ Ͻðڽϱ? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceGBLabel= ּ [gb] GB ũ ʿմϴ. +ComponentsDiskSpaceMBLabel= ּ [mb] MB ũ ʿմϴ. + ; *** "Select Additional Tasks" wizard page WizardSelectTasks=߰ ۾ -SelectTasksDesc= ۾ ߰ Ͻðڽϱ? -SelectTasksLabel2=ġ α׷ [name]() ġϴ ߰ ۾ [] Ŭϼ. +SelectTasksDesc= ߰ ۾ Ͻʽÿ. +SelectTasksLabel2=[name] ġ ߰ ۾ , "" ŬϽʽÿ. + ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup= ޴ -SelectStartMenuFolderDesc=ġ α׷ α׷ ٷ ⸦ 鵵 Ͻðڽϱ? -SelectStartMenuFolderLabel3=ġ α׷ α׷ ٷ ⸦ ޴ ϴ. -SelectStartMenuFolderBrowseLabel=Ϸ [] Ŭϼ. ٸ Ϸ [ãƺ] Ŭϼ. -MustEnterGroupName= ̸ Էؾ մϴ. +SelectStartMenuFolderDesc= α׷ ٷΰ⸦ ġϰڽϱ? +SelectStartMenuFolderLabel3= ޴ α׷ ٷΰ⸦ ϴ. +SelectStartMenuFolderBrowseLabel=Ϸ "" Ŭϰ, ٸ Ϸ "ãƺ" ŬϽʽÿ. +MustEnterGroupName= ̸ ԷϽʽÿ. GroupNameTooLong= ̸ Ǵ ΰ ʹ ϴ. -InvalidGroupName= ̸ ߸Ǿϴ. -BadGroupName= ̸ %n%n%1 ڸ ϴ. +InvalidGroupName= ̸ ȿ ʽϴ. +BadGroupName= ̸ ڸ ϴ:%n%n%1 NoProgramGroupCheck2= ޴ (&D) + ; *** "Ready to Install" wizard page -WizardReady=ġ غ -ReadyLabel1= ġ α׷ ǻͿ [name] ġ غ Ǿϴ. -ReadyLabel2a=ġ Ϸ [ġ] Ŭϰ, ϰų Ϸ [ڷ] Ŭϼ. -ReadyLabel2b=ġ Ϸ [ġ] Ŭϼ. +WizardReady=ġ غ Ϸ +ReadyLabel1= ǻͿ [name]() ġ غ Ǿϴ. +ReadyLabel2a=ġ Ϸ "ġ", ϰų Ϸ "ڷ" ŬϽʽÿ. +ReadyLabel2b=ġ Ϸ "ġ" ŬϽʽÿ. ReadyMemoUserInfo= : -ReadyMemoDir= ġ: +ReadyMemoDir=ġ ġ: ReadyMemoType=ġ : ReadyMemoComponents= : ReadyMemoGroup= ޴ : ReadyMemoTasks=߰ ۾: + ; *** "Preparing to Install" wizard page WizardPreparing=ġ غ -PreparingDesc=ġ α׷ ǻͿ [name] ġ غϰ ֽϴ. -PreviousInstallNotCompleted= α׷ ġ/ ۾ Ϸ ʾҽϴ. ش ġ ϷϷ ǻ͸ ٽ ؾ մϴ.%n%nǻ͸ ٽ [name] ġ ϷϷ ġ α׷ ٽ ϼ. -CannotContinue=ġ α׷ ϴ. Ϸ [] Ŭϼ. -ApplicationsFound=ġ α׷ Ʈؾ ϴ α׷ ǰ ֽϴ. ġ α׷ ̷ α׷ ڵ ݵ ϴ ϴ. -ApplicationsFound2=ġ α׷ Ʈؾ ϴ α׷ ǰ ֽϴ. ġ α׷ ̷ α׷ ڵ ݵ ϴ ϴ. ġ ϷǸ ġ α׷ α׷ ٽ Ϸ õմϴ. -CloseApplications= α׷ ڵ ݱ(&A) -DontCloseApplications= α׷ (&D) -ErrorCloseApplications=ġ α׷ Ϻ α׷ ڵ ϴ. ϱ ġ α׷ Ʈؾ ϴ ϴ α׷ ݴ ϴ. +PreparingDesc= ǻͿ [name] ġ غϴ Դϴ. +PreviousInstallNotCompleted= α׷ ġ/ ۾ Ϸ ʾҽϴ, ϷϷ ǻ͸ ٽ ؾ մϴ.%n%nǻ͸ ٽ , ġ 縦 ٽ Ͽ [name] ġ ϷϽñ ٶϴ. +CannotContinue=ġ ϴ, "" ŬϿ ġ Ͻʽÿ. +ApplicationsFound= α׷ ġ Ʈ ʿ ϰ ֽϴ, ġ 簡 ̷ α׷ ڵ ֵ Ͻñ ٶϴ. +ApplicationsFound2= α׷ ġ Ʈ ʿ ϰ ֽϴ, ġ 簡 ̷ α׷ ڵ ֵ Ͻñ ٶϴ. ġ ϷǸ, ġ α׷ ٽ ۵ǵ õ ̴ϴ. +CloseApplications=ڵ α׷ (&A) +DontCloseApplications=α׷ (&D) +ErrorCloseApplications=ġ 簡 α׷ ڵ ϴ, ϱ ġ Ʈ ʿ ϰ ִ α׷ Ͻñ ٶϴ. +PrepareToInstallNeedsRestart=ġ ǻ͸ ؾ մϴ. [name] ġ Ϸϱ ǻ͸ ٽ Ŀ ġ 縦 ٽ ֽʽÿ.%n%n ٽ Ͻðڽϱ? + ; *** "Installing" wizard page WizardInstalling=ġ -InstallingLabel=ġ α׷ ǻͿ [name]() ġϴ ٷ ּ. +InstallingLabel= ǻͿ [name]() ġϴ ... ٷ ֽʽÿ. + ; *** "Setup Completed" wizard page -FinishedHeadingLabel=[name] 縦 Ϸϴ -FinishedLabelNoIcons=ġ α׷ ǻͿ [name]() ġ߽ϴ. -FinishedLabel=ġ α׷ ǻͿ [name]() ġ߽ϴ. ġ ٷ ⸦ Ͽ ش α׷ ֽϴ. -ClickFinish=ġ α׷ Ϸ [ħ] Ŭϼ. -FinishedRestartLabel=[name] ġ ϷϷ ġ α׷ ǻ͸ ٽ ؾ մϴ. ٽ Ͻðڽϱ? -FinishedRestartMessage=[name] ġ ϷϷ ġ α׷ ǻ͸ ٽ ؾ մϴ.%n%n ٽ Ͻðڽϱ? -ShowReadmeCheck=, README ڽϴ. -YesRadio=, ǻ͸ ٽ ϰڽϴ(&Y). -NoRadio=ƴϿ, ǻ͸ ߿ ٽ ϰڽϴ(&N). +FinishedHeadingLabel=[name] ġ Ϸ +FinishedLabelNoIcons= ǻͿ [name]() ġǾϴ. +FinishedLabel= ǻͿ [name]() ġǾϴ, α׷ ġ Ͽ ֽϴ. +ClickFinish=ġ "" ŬϽʽÿ. +FinishedRestartLabel=[name] ġ ϷϷ, ǻ͸ ٽ ؾ մϴ. ٽ Ͻðڽϱ? +FinishedRestartMessage=[name] ġ ϷϷ, ǻ͸ ٽ ؾ մϴ.%n%n ٽ Ͻðڽϱ? +ShowReadmeCheck=, README ǥմϴ +YesRadio=, ٽ մϴ(&Y) +NoRadio=ƴϿ, ߿ ٽ մϴ(&N) ; used for example as 'Run MyProg.exe' RunEntryExec=%1 ; used for example as 'View Readme.txt' -RunEntryShellExec=%1 +RunEntryShellExec=%1 ǥ + ; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=ġ α׷ ũ ʿ -SelectDiskLabel2=ũ %1() [Ȯ] Ŭϼ.%n%n ũ Ʒ ǥõ ƴ ٸ ùٸ θ Էϰų [ãƺ] Ŭϼ. +ChangeDiskTitle=ũ ʿմϴ +SelectDiskLabel2=ũ %1() ϰ "Ȯ" ŬϽʽÿ.%n%n ũ Ʒ ΰ ƴ ִ , ùٸ θ Էϰų "ãƺ" ŬϽñ ٶϴ. PathLabel=(&P): -FileNotInDir2="%2" "%1"() ã ϴ. ùٸ ũ ϰų ٸ ϼ. -SelectDirectoryLabel= ũ ġ ϼ. +FileNotInDir2=%2 %1() ġ ϴ, ùٸ ũ ϰų ٸ Ͻʽÿ. +SelectDirectoryLabel= ũ ġ Ͻʽÿ. + ; *** Installation phase messages -SetupAborted=ġ Ϸ ߽ϴ.%n%n ذ ġ α׷ ٽ ϼ. -EntryAbortRetryIgnore=ٽ õϷ [ٽ õ], ׷ Ϸ [], ġ Ϸ [ߴ] Ŭϼ. +SetupAborted=ġ Ϸ ʾҽϴ.%n%n ذ , ٽ ġ Ͻʽÿ. +AbortRetryIgnoreSelectAction=׼ ֽʽÿ. +AbortRetryIgnoreRetry=õ(&T) +AbortRetryIgnoreIgnore= ϰ (&I) +AbortRetryIgnoreCancel=ġ + ; *** Installation status messages -StatusClosingApplications= α׷ ݴ ... -StatusCreateDirs=͸ ... +StatusClosingApplications=α׷ ϴ ... +StatusCreateDirs= ... StatusExtractFiles= ϴ ... -StatusCreateIcons=ٷ ⸦ ... +StatusCreateIcons=ٷΰ⸦ ϴ ... StatusCreateIniEntries=INI ׸ ... StatusCreateRegistryEntries=Ʈ ׸ ... StatusRegisterFiles= ϴ ... StatusSavingUninstall= ϴ ... StatusRunProgram=ġ Ϸϴ ... -StatusRestartingApplications= α׷ ٽ ϴ ... -StatusRollback= ѹϴ ... +StatusRestartingApplications=α׷ ٽ ϴ ... +StatusRollback= ϴ ... + ; *** Misc. errors ErrorInternal2= : %1 ErrorFunctionFailedNoCode=%1 -ErrorFunctionFailed=%1 , ڵ %2 -ErrorFunctionFailedWithMessage=%1 , ڵ %2.%n%3 -ErrorExecutingProgram= :%n%1 +ErrorFunctionFailed=%1 ; ڵ %2 +ErrorFunctionFailedWithMessage=%1 , ڵ: %2.%n%3 +ErrorExecutingProgram= :%n%1 + ; *** Registry errors -ErrorRegOpenKey=Ʈ Ű ߻:%n%1\%2 -ErrorRegCreateKey=Ʈ Ű ߻:%n%1\%2 -ErrorRegWriteKey=Ʈ Ű ϴ ߻:%n%1\%2 +ErrorRegOpenKey=Ʈ Ű :%n%1\%2 +ErrorRegCreateKey=Ʈ Ű :%n%1\%2 +ErrorRegWriteKey=Ʈ Ű :%n%1\%2 + ; *** INI errors -ErrorIniEntry= "%1" INI ׸ ߿ ߻߽ϴ. +ErrorIniEntry=%1 Ͽ INI ׸ Դϴ. + ; *** File copying errors -FileAbortRetryIgnore=ٽ õϷ [ٽ õ], dzʶٷ []( ), ġ Ϸ [ߴ] Ŭϼ. -FileAbortRetryIgnore2=ٽ õϷ [ٽ õ], ׷ Ϸ []( ), ġ Ϸ [ߴ] Ŭϼ. -SourceIsCorrupted= ջǾϴ. -SourceDoesntExist= "%1"() ϴ. -ExistingFileReadOnly= б ǥõǾ ֽϴ.%n%nб Ư ϰ ٽ õϷ [ٽ õ], dzʶٷ [], ġ Ϸ [ߴ] Ŭϼ. -ErrorReadingExistingDest= д ߻: -FileExists=ش ̹ ֽϴ.%n%nġ α׷  Ͻðڽϱ? -ExistingFileNewer= ġ α׷ ġϷ Ϻ ֽԴϴ. մϴ.%n%n Ͻðڽϱ? -ErrorChangingAttr= Ư ϴ ߻: -ErrorCreatingTemp= ͸ ߻: -ErrorReadingSource= д ߻: -ErrorCopying= ϴ ߻: -ErrorReplacingExistingFile= ٲٴ ߻: +FileAbortRetryIgnoreSkipNotRecommended= dzʶ(&S) ( ʽϴ) +FileAbortRetryIgnoreIgnoreNotRecommended= ϰ (&I) ( ʽϴ) +SourceIsCorrupted= ջ +SourceDoesntExist= %1() +ExistingFileReadOnly2= б ̱⶧ ü ϴ. +ExistingFileReadOnlyRetry=б Ӽ ϰ ٽ õϷ(&R) +ExistingFileReadOnlyKeepExisting= (&K) +ErrorReadingExistingDest= д ߻: +FileExists= ̹ մϴ.%n%n ðڽϱ? +ExistingFileNewer= ġϷ ϴ Ϻ Դϴ, Ͻñ ٶϴ.%n%n Ͻðڽϱ? +ErrorChangingAttr= Ӽ ϴ ߻: +ErrorCreatingTemp= ߻: +ErrorReadingSource= д ߻: +ErrorCopying= ϴ ߻: +ErrorReplacingExistingFile= üϴ ߻: ErrorRestartReplace=RestartReplace : -ErrorRenamingTemp= ͸ ִ ̸ ٲٴ ߻: -ErrorRegisterServer=DLL/OCX : %1 -ErrorRegSvr32Failed= ڵ %1() Բ RegSvr32 -ErrorRegisterTypeLib= ̺귯 : %1 +ErrorRenamingTemp= ̸ ٲٴ ߻: +ErrorRegisterServer=DLL/OCX : %1 +ErrorRegSvr32Failed=RegSvr32 ڵ : %1 +ErrorRegisterTypeLib= ̺귯 Ͽ : %1 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32Ʈ +UninstallDisplayNameMark64Bit=64Ʈ +UninstallDisplayNameMarkAllUsers= +UninstallDisplayNameMarkCurrentUser= + ; *** Post-installation errors -ErrorOpeningReadme=README ߿ ߻߽ϴ. -ErrorRestartingComputer=ġ α׷ ǻ͸ ٽ ϴ. ϼ. +ErrorOpeningReadme=README ߻߽ϴ. +ErrorRestartingComputer=ǻ͸ ٽ ϴ, ٽ Ͻʽÿ. + ; *** Uninstaller messages -UninstallNotFound= "%1"() ϴ. ϴ. -UninstallOpenError= "%1"() ϴ. ϴ. -UninstallUnsupportedVer= α "%1"() α׷ ν ϴ Դϴ. ϴ. -UninstallUnknownEntry= α׿ ׸(%1) ߰ߵǾϴ. -ConfirmUninstall=%1() ش Ҹ Ͻðڽϱ? -UninstallOnlyOnWin64= ġ 64Ʈ Windows ֽϴ. -OnlyAdminCanUninstall= ġ ִ ڸ ֽϴ. -UninstallStatusLabel=ǻͿ %1() ϴ ٷ ּ. -UninstalledAll=ǻͿ %1() ߽ϴ. -UninstalledMost=%1 Ű ϷǾϴ.%n%nϺ Ҵ ϴ. ̷ ׸ ֽϴ. -UninstalledAndNeedsRestart=%1 Ÿ ϷϷ ǻ͸ ٽ ؾ մϴ.%n%n ٽ Ͻðڽϱ? -UninstallDataCorrupted="%1" ջǾϴ. ϴ. +UninstallNotFound= %1() ʱ , Ÿ ϴ. +UninstallOpenError= %1() , Ÿ ϴ. +UninstallUnsupportedVer= α "%1"() ν ̱ , Ÿ ϴ. +UninstallUnknownEntry= ׸ %1() α׿ ԵǾ ֽϴ. +ConfirmUninstall= %1() Ҹ Ͻðڽϱ? +UninstallOnlyOnWin64= α׷ 64Ʈ Windows ֽϴ. +OnlyAdminCanUninstall= α׷ Ϸ ʿմϴ. +UninstallStatusLabel= ǻͿ %1() ϴ ... ٷ ֽʽÿ. +UninstalledAll=%1() ŵǾϴ! +UninstalledMost=%1 Ű ϷǾϴ.%n%nϺ Ҵ , Ͻñ ٶϴ. +UninstalledAndNeedsRestart=%1 Ÿ ϷϷ, ǻ͸ ٽ ؾ մϴ.%n%n ٽ Ͻðڽϱ? +UninstallDataCorrupted= "%1"() ջǾ , Ÿ ϴ. + ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle= Ͻðڽϱ? -ConfirmDeleteSharedFile2=ýۿ ϴ α׷ ǥõ˴ϴ. ۾ Ͻðڽϱ?%n%n ϴ α׷ ִµ ϸ ش α׷ ùٸ ۵ ֽϴ. 𸣴 [ƴϿ] ϼ. ýۿ ״ ξ ƹ ߻ ʽϴ. +ConfirmDeleteSharedFile2=ý  α׷ ʽϴ, Ͻðڽϱ?%n%n ٸ α׷ ϰ ִ ¿ , ش α׷ ۵ , Ȯ "ƴϿ" ϼŵ ˴ϴ. ýۿ ־ ʽϴ. SharedFileNameLabel= ̸: SharedFileLocationLabel=ġ: WizardUninstalling= StatusUninstalling=%1() ϴ ... + ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=%1() ġϴ Դϴ. ShutdownBlockReasonUninstallingApp=%1() ϴ Դϴ. + ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. + [CustomMessages] + NameAndVersion=%1 %2 -AdditionalIcons=߰ ٷ : -CreateDesktopIcon= ȭ ٷ (&D) -CreateQuickLaunchIcon= ٷ (&Q) -ProgramOnTheWeb=%1 +AdditionalIcons= ߰: +CreateDesktopIcon= ȭ鿡 ٷΰ (&D) +CreateQuickLaunchIcon= (&Q) +ProgramOnTheWeb=%1 UninstallProgram=%1 -LaunchProgram=%1 -AssocFileExtension=%1() %2 Ȯ (&A) -AssocingFileExtension=%1() %2 Ȯ ... +LaunchProgram=%1 +AssocFileExtension= Ȯ %2() %1 մϴ. +AssocingFileExtension= Ȯ %2() %1 ϴ ... AutoStartProgramGroupDescription=: -AutoStartProgram=%1 ڵ -AddonHostProgramNotFound= %1() ã ϴ.%n%n׷ Ͻðڽϱ? \ No newline at end of file +AutoStartProgram=%1() ڵ +AddonHostProgramNotFound=%1() ġ ϴ.%n%n׷ Ͻðڽϱ? diff --git a/build/win32/i18n/Default.zh-cn.isl b/build/win32/i18n/Default.zh-cn.isl index e384e83d30d..5c5df9a166f 100644 --- a/build/win32/i18n/Default.zh-cn.isl +++ b/build/win32/i18n/Default.zh-cn.isl @@ -1,16 +1,17 @@ -; *** Inno Setup version 5.5.3+ Simplified Chinese messages *** +; *** Inno Setup version 6.0.3+ Chinese Simplified messages *** ; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; Maintained by Zhenghan Yang +; Email: 847320916@QQ.com +; Translation based on network resource +; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation ; -; Note: When translating this text, do not add periods (.) to the end of -; messages that didn't have them already, because on those messages Inno -; Setup adds the periods automatically (appending a period would result in -; two periods being displayed). + [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. -LanguageName=Simplified Chinese +LanguageName=简体中文 +; If Language Name display incorrect, uncomment next line +; LanguageName=<7B80><4F53><4E2D><6587> LanguageID=$0804 LanguageCodePage=936 ; If the language you are translating to requires special font faces or @@ -23,276 +24,342 @@ LanguageCodePage=936 ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 + [Messages] -; *** Application titles -SetupAppTitle=װ -SetupWindowTitle=װ - %1 -UninstallAppTitle=ж -UninstallAppFullTitle=%1 ж + +; *** 应用程序标题 +SetupAppTitle=安装 +SetupWindowTitle=安装 - %1 +UninstallAppTitle=卸载 +UninstallAppFullTitle=%1 卸载 + ; *** Misc. common -InformationTitle=Ϣ -ConfirmTitle=ȷ -ErrorTitle= +InformationTitle=信息 +ConfirmTitle=确认 +ErrorTitle=错误 + ; *** SetupLdr messages -SetupLdrStartupMessage=⽫װ %1ǷҪ? -LdrCannotCreateTemp=޷ʱļװֹ -LdrCannotExecTemp=޷ʱĿ¼ִļװֹ -; *** Startup error messages -LastErrorMessage=%1%n%n %2: %3 -SetupFileMissing=װĿ¼ȱʧļ %1ȡ¸ -SetupFileCorrupt=װļ𻵡ȡó¸ -SetupFileCorruptOrWrongVer=װļ𻵻˰װ汾ݡȡó¸ -InvalidParameter= %n%n%1 ϴһЧ -SetupAlreadyRunning=װС -WindowsVersionNotSupported=˳֧е Windows 汾 -WindowsServicePackRequired=˳Ҫ %1 %2 ߰汾 -NotOnThisPlatform=˳򽫲 %1 С -OnlyOnThisPlatform=˳ %1 С -OnlyOnTheseArchitectures=˳ɰװΪ´ϵṹƵ Windows 汾:%n%n%1 -MissingWOW64APIs=е Windows 汾װִ 64 λװĹܡҪ⣬밲װ %1 -WinVersionTooLowError=˳Ҫ %1 汾 %2 ߰汾 -WinVersionTooHighError=˳ܰװ %1 汾 %2 ߵİ汾ϡ -AdminPrivilegesRequired=ڰװ˳ʱΪԱ¼ -PowerUserPrivilegesRequired=װ˳ʱԹԱ Power User Աݵ¼ -SetupAppRunningError=װ⵽ %1 ǰС%n%nرʵȻ󵥻ȷԼ򵥻ȡ˳ -UninstallAppRunningError=жؼ⵽ %1 ǰС%n%nرʵȻ󵥻ȷԼ򵥻ȡ˳ -; *** Misc. errors -ErrorCreatingDir=װ޷Ŀ¼%1 -ErrorTooManyFilesInDir=޷Ŀ¼%1дļΪ̫ļ -; *** Setup common messages -ExitSetupTitle=˳װ -ExitSetupMessage=װδɡ˳ᰲװó%n%nʱٴаװɰװ%n%nǷ˳װ? -AboutSetupMenuItem=ڰװ(&A)... -AboutSetupTitle=ڰװ -AboutSetupMessage=%1 汾 %2%n%3%n%n%1 ҳ:%n%4 +SetupLdrStartupMessage=现在将安装 %1。您想要继续吗? +LdrCannotCreateTemp=不能创建临时文件。安装中断。 +LdrCannotExecTemp=不能执行临时目录中的文件。安装中断。 +HelpTextNote= + +; *** 启动错误消息 +LastErrorMessage=%1.%n%n错误 %2: %3 +SetupFileMissing=安装目录中的文件 %1 丢失。请修正这个问题或获取一个新的程序副本。 +SetupFileCorrupt=安装文件已损坏。请获取一个新的程序副本。 +SetupFileCorruptOrWrongVer=安装文件已损坏,或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。 +InvalidParameter=无效的命令行参数: %n%n%1 +SetupAlreadyRunning=安装程序正在运行。 +WindowsVersionNotSupported=这个程序不支持该版本的计算机运行。 +WindowsServicePackRequired=这个程序要求%1服务包%1或更高。 +NotOnThisPlatform=这个程序将不能运行于 %1。 +OnlyOnThisPlatform=这个程序必须运行于 %1。 +OnlyOnTheseArchitectures=这个程序只能在为下列处理器结构设计的 Windows 版本中进行安装:%n%n%1 +WinVersionTooLowError=这个程序需要 %1 版本 %2 或更高。 +WinVersionTooHighError=这个程序不能安装于 %1 版本 %2 或更高。 +AdminPrivilegesRequired=在安装这个程序时您必须以管理员身份登录。 +PowerUserPrivilegesRequired=在安装这个程序时您必须以管理员身份或有权限的用户组身份登录。 +SetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口,然后单击“确定”继续,或按“取消”退出。 +UninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口,然后单击“确定”继续,或按“取消”退出。 + +; *** 启动问题 +PrivilegesRequiredOverrideTitle=选择安装程序模式 +PrivilegesRequiredOverrideInstruction=选择安装模式 +PrivilegesRequiredOverrideText1=%1 可以为所有用户安装(需要管理员权限),或仅为您安装。 +PrivilegesRequiredOverrideText2=%1 只能为您安装,或为所有用户安装(需要管理员权限)。 +PrivilegesRequiredOverrideAllUsers=为所有用户安装(&A) +PrivilegesRequiredOverrideAllUsersRecommended=为所有用户安装(建议选项)(&A) +PrivilegesRequiredOverrideCurrentUser=只为我安装(&M) +PrivilegesRequiredOverrideCurrentUserRecommended=只为我安装(建议选项)(&M) + +; *** 其它错误 +ErrorCreatingDir=安装程序不能创建目录“%1”。 +ErrorTooManyFilesInDir=不能在目录“%1”中创建文件,因为里面的文件太多 + +; *** 安装程序公共消息 +ExitSetupTitle=退出安装程序 +ExitSetupMessage=安装程序未完成安装。如果您现在退出,您的程序将不能安装。%n%n您可以以后再运行安装程序完成安装。%n%n退出安装程序吗? +AboutSetupMenuItem=关于安装程序(&A)... +AboutSetupTitle=关于安装程序 +AboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页:%n%4 AboutSetupNote= TranslatorNote= -; *** Buttons -ButtonBack=< һ(&B) -ButtonNext=һ(&N) > -ButtonInstall=װ(&I) -ButtonOK=ȷ -ButtonCancel=ȡ -ButtonYes=(&Y) -ButtonYesToAll=ȫ(&A) -ButtonNo=(&N) -ButtonNoToAll=ȫ(&O) -ButtonFinish=(&F) -ButtonBrowse=(&B)... -ButtonWizardBrowse=(&R)... -ButtonNewFolder=½ļ(&M) -; *** "Select Language" dialog messages -SelectLanguageTitle=ѡװ -SelectLanguageLabel=ѡװʱҪʹõ: -; *** Common wizard text -ClickNext=һԼ򵥻ȡ˳װ + +; *** 按钮 +ButtonBack=< 上一步(&B) +ButtonNext=下一步(&N) > +ButtonInstall=安装(&I) +ButtonOK=确定 +ButtonCancel=取消 +ButtonYes=是(&Y) +ButtonYesToAll=全是(&A) +ButtonNo=否(&N) +ButtonNoToAll=全否(&O) +ButtonFinish=完成(&F) +ButtonBrowse=浏览(&B)... +ButtonWizardBrowse=浏览(&R)... +ButtonNewFolder=新建文件夹(&M) + +; *** “选择语言”对话框消息 +SelectLanguageTitle=选择安装语言 +SelectLanguageLabel=选择安装时要使用的语言。 + +; *** 公共向导文字 +ClickNext=单击“下一步”继续,或单击“取消”退出安装程序。 BeveledLabel= -BrowseDialogTitle=ļ -BrowseDialogLabel=бѡһļУȻ󵥻ȷ -NewFolderName=½ļ -; *** "Welcome" wizard page -WelcomeLabel1=ӭʹ [name] װ -WelcomeLabel2=⽫ڼϰװ [name/ver]%n%nرӦóټ -; *** "Password" wizard page -WizardPassword= -PasswordLabel1=˰װ뱣 -PasswordLabel3=ṩ룬Ȼ󵥻һԼִСд -PasswordEditLabel=(&P): -IncorrectPassword=벻ȷԡ -; *** "License Agreement" wizard page -WizardLicense=Э -LicenseLabel=ڼǰĶҪϢ -LicenseLabel3=ĶЭ顣ܴЭſɼװ -LicenseAccepted=ҽЭ(&A) -LicenseNotAccepted=ҲЭ(&D) -; *** "Information" wizard pages -WizardInfoBefore=Ϣ -InfoBeforeLabel=ڼǰĶҪϢ -InfoBeforeClickLabel=׼üװ󣬵һ -WizardInfoAfter=Ϣ -InfoAfterLabel=ڼǰĶҪϢ -InfoAfterClickLabel=׼üװ󣬵һ -; *** "User Information" wizard page -WizardUserInfo=ûϢ -UserInfoDesc=Ϣ -UserInfoName=û(&U): -UserInfoOrg=֯(&O): -UserInfoSerial=к(&S): -UserInfoNameRequired=ơ -; *** "Select Destination Location" wizard page -WizardSelectDir=ѡĿλ -SelectDirDesc=Ӧ [name] װ? -SelectDirLabel3=װὫ [name] װļС -SelectDirBrowseLabel=ҪһѡļУ -DiskSpaceMBLabel=Ҫ [mb] MB ô̿ռ䡣 -CannotInstallToNetworkDrive=װ޷װ -CannotInstallToUNCPath=װ޷װ UNC · -InvalidPath=ŵ·(:%n%nC:\APP%n%n)¸ʽ UNC ·:%n%n\\server\share -InvalidDrive=ѡ UNC ڻ򲻿ɷʡѡ -DiskSpaceWarningTitle=̿ռ䲻 -DiskSpaceWarning=װҪ %1 KB ÿռװѡ %2 KB ÿռ䡣%n%nǷҪ? -DirNameTooLong=ļƻ·̫ -InvalidDirName=ļЧ -BadDirName32=ļܰһַ:%n%n%1 -DirExistsTitle=ļд -DirExists=ļ:%n%n%1%n%nѴڡǷҪװļ? -DirDoesntExistTitle=ļв -DirDoesntExist=ļ:%n%n%1%n%nڡǷҪļ? -; *** "Select Components" wizard page -WizardSelectComponents=ѡ -SelectComponentsDesc=ӦװЩ? -SelectComponentsLabel2=ѡϣװϣװ׼󵥻һԼ -FullInstallation=ȫװ +BrowseDialogTitle=浏览文件夹 +BrowseDialogLabel=在下列列表中选择一个文件夹,然后单击“确定”。 +NewFolderName=新建文件夹 + +; *** “欢迎”向导页 +WelcomeLabel1=欢迎使用 [name] 安装向导 +WelcomeLabel2=现在将安装 [name/ver] 到您的电脑中。%n%n推荐您在继续安装前关闭所有其它应用程序。 + +; *** “密码”向导页 +WizardPassword=密码 +PasswordLabel1=这个安装程序有密码保护。 +PasswordLabel3=请输入密码,然后单击“下一步”继续。密码区分大小写。 +PasswordEditLabel=密码(&P): +IncorrectPassword=您输入的密码不正确,请重试。 + +; *** “许可协议”向导页 +WizardLicense=许可协议 +LicenseLabel=继续安装前请阅读下列重要信息。 +LicenseLabel3=请仔细阅读下列许可协议。您在继续安装前必须同意这些协议条款。 +LicenseAccepted=我同意此协议(&A) +LicenseNotAccepted=我不同意此协议(&D) + +; *** “信息”向导页 +WizardInfoBefore=信息 +InfoBeforeLabel=请在继续安装前阅读下列重要信息。 +InfoBeforeClickLabel=如果您想继续安装,单击“下一步”。 +WizardInfoAfter=信息 +InfoAfterLabel=请在继续安装前阅读下列重要信息。 +InfoAfterClickLabel=如果您想继续安装,单击“下一步”。 + +; *** “用户信息”向导页 +WizardUserInfo=用户信息 +UserInfoDesc=请输入您的信息。 +UserInfoName=用户名(&U): +UserInfoOrg=组织(&O): +UserInfoSerial=序列号(&S): +UserInfoNameRequired=您必须输入名字。 + +; *** “选择目标目录”向导面 +WizardSelectDir=选择目标位置 +SelectDirDesc=您想将 [name] 安装在什么地方? +SelectDirLabel3=安装程序将安装 [name] 到下列文件夹中。 +SelectDirBrowseLabel=单击“下一步”继续。如果您想选择其它文件夹,单击“浏览”。 +DiskSpaceGBLabel=至少需要有 [gb] GB 的可用磁盘空间。 +DiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。 +CannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。 +CannotInstallToUNCPath=安装程序无法安装到一个UNC路径。 +InvalidPath=您必须输入一个带驱动器卷标的完整路径,例如:%n%nC:\APP%n%n或下列形式的 UNC 路径:%n%n\\server\share +InvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选选择其它位置。 +DiskSpaceWarningTitle=没有足够的磁盘空间 +DiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装,但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗? +DirNameTooLong=文件夹名或路径太长。 +InvalidDirName=文件夹名是无效的。 +BadDirName32=文件夹名不能包含下列任何字符:%n%n%1 +DirExistsTitle=文件夹存在 +DirExists=文件夹:%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗? +DirDoesntExistTitle=文件夹不存在 +DirDoesntExist=文件夹:%n%n%1%n%n不存在。您想要创建此目录吗? + +; *** “选择组件”向导页 +WizardSelectComponents=选择组件 +SelectComponentsDesc=您想安装哪些程序的组件? +SelectComponentsLabel2=选择您想要安装的组件;清除您不想安装的组件。然后单击“下一步”继续。 +FullInstallation=完全安装 ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=లװ -CustomInstallation=Զ尲װ -NoUninstallWarningTitle= -NoUninstallWarning=װ⵽Ѱװ:%n%n%1%n%nȡѡЩжǡ%n%nǷҪ? +CompactInstallation=简洁安装 +CustomInstallation=自定义安装 +NoUninstallWarningTitle=组件存在 +NoUninstallWarning=安装程序侦测到下列组件已在您的电脑中安装。:%n%n%1%n%n取消选定这些组件将不能卸载它们。%n%n您一定要继续吗? ComponentSize1=%1 KB ComponentSize2=%1 MB -ComponentsDiskSpaceMBLabel=ǰѡҪ [mb] MB ̿ռ䡣 -; *** "Select Additional Tasks" wizard page -WizardSelectTasks=ѡ -SelectTasksDesc=ӦִЩ? -SelectTasksLabel2=ѡװ [name] ʱϣװִеȻ󵥻һ -; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup=ѡʼ˵ļ -SelectStartMenuFolderDesc=װӦĿݷʽõ? -SelectStartMenuFolderLabel3=װ¿ʼ˵ļдóĿݷʽ -SelectStartMenuFolderBrowseLabel=ҪһѡļУ -MustEnterGroupName=ļ -GroupNameTooLong=ļƻ·̫ -InvalidGroupName=ļЧ -BadGroupName=ļܱһַ:%n%n%1 -NoProgramGroupCheck2=ʼ˵ļ(&D) -; *** "Ready to Install" wizard page -WizardReady=װ׼ -ReadyLabel1=װ׼ڼϰװ [name] -ReadyLabel2a=װԼװ鿴κ򵥻"" -ReadyLabel2b=װԼװ -ReadyMemoUserInfo=ûϢ: -ReadyMemoDir=Ŀλ: -ReadyMemoType=װ: -ReadyMemoComponents=ѡ: -ReadyMemoGroup=ʼ˵ļ: -ReadyMemoTasks=: -; *** "Preparing to Install" wizard page -WizardPreparing=׼װ -PreparingDesc=װ׼ڼϰװ [name] -PreviousInstallNotCompleted=һİװ/ɾδɡɸðװ%n%nаװ [name] İװ -CannotContinue=װ޷뵥"ȡ"˳ -ApplicationsFound=ӦóʹҪͨװиµļװԶرЩӦó -ApplicationsFound2=ӦóʹҪͨװиµļװԶرЩӦóɰװ󣬰װ򽫳Ӧó -CloseApplications=ԶرӦó(&A) -DontCloseApplications=رӦó(&D) -ErrorCloseApplications=װ޷ԶرӦó򡣽ڼ֮ǰȹرʹͨװиµļӦó -; *** "Installing" wizard page -WizardInstalling=ڰװ -InstallingLabel=װڼϰװ [name]Եȡ -; *** "Setup Completed" wizard page -FinishedHeadingLabel= [name] װ -FinishedLabelNoIcons=װڼɰװ [name] -FinishedLabel=װڼɰװ [name]ͨѡװĿݷʽӦó -ClickFinish=ɡ˳װ -FinishedRestartLabel=Ҫ [name] İװװǷҪ? -FinishedRestartMessage=Ҫ [name] İװװ%n%nǷҪ? -ShowReadmeCheck=ǣϣ鿴 README ļ -YesRadio=ǣ(&Y) -NoRadio=ҽԺ(&N) -; used for example as 'Run MyProg.exe' -RunEntryExec= %1 -; used for example as 'View Readme.txt' -RunEntryShellExec=鿴 %1 -; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=װҪһ -SelectDiskLabel2= %1 ȷ%n%n˴ϵļļļҵȷ·򵥻 -PathLabel=·(&P): -FileNotInDir2=ڡ%2޷λļ%1ȷĴ̻ѡļС -SelectDirectoryLabel=ָһ̵λá -; *** Installation phase messages -SetupAborted=װδɡ%n%nⲢаװ -EntryAbortRetryIgnore=ԡٴγԣԡԼ򵥻ֹȡװ -; *** Installation status messages -StatusClosingApplications=ڹرӦó... -StatusCreateDirs=ڴĿ¼... -StatusExtractFiles=ڽѹļ... -StatusCreateIcons=ڴݷʽ... -StatusCreateIniEntries=ڴ INI ... -StatusCreateRegistryEntries=ڴע... -StatusRegisterFiles=עļ... -StatusSavingUninstall=ڱжϢ... -StatusRunProgram=ɰװ... -StatusRestartingApplications=Ӧó... -StatusRollback=ڻ˸... -; *** Misc. errors -ErrorInternal2=ڲ: %1 -ErrorFunctionFailedNoCode=%1 ʧ -ErrorFunctionFailed=%1 ʧܣ %2 -ErrorFunctionFailedWithMessage=%1 ʧܣ %2%n%3 -ErrorExecutingProgram=޷ִļ:%n%1 -; *** Registry errors -ErrorRegOpenKey=עʱ:%n%1\%2 -ErrorRegCreateKey=עʱ:%n%1\%2 -ErrorRegWriteKey=дעʱ:%n%1\%2 -; *** INI errors -ErrorIniEntry=ļ%1д INI ʱ -; *** File copying errors -FileAbortRetryIgnore=ԡٴβԡļ(˲)򵥻ֹȡװ -FileAbortRetryIgnore2=ԡٴβԡԼ(˲)򵥻ֹȡװ -SourceIsCorrupted=Դļ -SourceDoesntExist=Դļ%1 -ExistingFileReadOnly=ļΪֻ״̬%n%nԡɾֻԲԣԡļ򵥻ֹȡװ -ErrorReadingExistingDest=Զȡļʱ: -FileExists=ļѴڡ%n%nǷҪװ򸲸? -ExistingFileNewer=ļȰװ԰װļ¡鱣ļ%n%nǷҪļ? -ErrorChangingAttr=ԸļԳ: -ErrorCreatingTemp=ĿĿ¼ļʱ: -ErrorReadingSource=ԶȡԴļʱ: -ErrorCopying=Ըļʱ: -ErrorReplacingExistingFile=滻ļʱ: -ErrorRestartReplace=RestartReplace ʧ: -ErrorRenamingTemp=ĿĿ¼ļʱ: -ErrorRegisterServer=޷ע DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 ʧܣ˳Ϊ %1 -ErrorRegisterTypeLib=޷עͿ: %1 -; *** Post-installation errors -ErrorOpeningReadme=Դ README ļʱ -ErrorRestartingComputer=װ޷ִֶд˲ -; *** Uninstaller messages -UninstallNotFound=ļ%1ڡ޷װ -UninstallOpenError=޷ļ%1޷ж -UninstallUnsupportedVer=ж־%1ĸʽ޷˰汾жسʶ޷ж -UninstallUnknownEntry=ж־зδ֪Ŀ(%1) -ConfirmUninstall=ȷҪɾ %1 ͼȫ? -UninstallOnlyOnWin64= 64 λ Windows жش˰װ -OnlyAdminCanUninstall=йȨ޵ûſжش˰װ -UninstallStatusLabel=Ӽɾ %1Եȡ -UninstalledAll=ѳɹӼɾ %1 -UninstalledMost=%1 жɡ%n%n޷ɾһЩԪءɽֶɾ -UninstalledAndNeedsRestart=Ҫ %1 жأ%n%nǷҪ? -UninstallDataCorrupted=%1ļ𻵡޷ж -; *** Uninstallation phase messages -ConfirmDeleteSharedFileTitle=ɾļ? -ConfirmDeleteSharedFile2=ϵͳʾ¹ļٱκγʹáǷҪжɾ˹ļ?%n%nгʹôļɾܲСȷѡ񡰷񡱡ļסϵͳϲκ⡣ -SharedFileNameLabel=ļ: -SharedFileLocationLabel=λ: -WizardUninstalling=ж״̬ -StatusUninstalling=ж %1... +ComponentsDiskSpaceGBLabel=当前选择的组件至少需要 [gb] GB 的磁盘空间。 +ComponentsDiskSpaceMBLabel=当前选择的组件至少需要 [mb] MB 的磁盘空间。 + +; *** “选择附加任务”向导页 +WizardSelectTasks=选择附加任务 +SelectTasksDesc=您想要安装程序执行哪些附加任务? +SelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务,然后单击“下一步”。 + +; *** “选择开始菜单文件夹”向导页 +WizardSelectProgramGroup=选择开始菜单文件夹 +SelectStartMenuFolderDesc=您想在哪里放置程序的快捷方式? +SelectStartMenuFolderLabel3=安装程序现在将在下列开始菜单文件夹中创建程序的快捷方式。 +SelectStartMenuFolderBrowseLabel=单击“下一步”继续。如果您想选择其它文件夹,单击“浏览”。 +MustEnterGroupName=您必须输入一个文件夹名。 +GroupNameTooLong=文件夹名或路径太长。 +InvalidGroupName=文件夹名是无效的。 +BadGroupName=文件夹名不能包含下列任何字符:%n%n%1 +NoProgramGroupCheck2=不创建开始菜单文件夹(&D) + +; *** “准备安装”向导页 +WizardReady=准备安装 +ReadyLabel1=安装程序现在准备开始安装 [name] 到您的电脑中。 +ReadyLabel2a=单击“安装”继续此安装程序。如果您想要回顾或改变设置,请单击“上一步”。 +ReadyLabel2b=单击“安装”继续此安装程序? +ReadyMemoUserInfo=用户信息: +ReadyMemoDir=目标位置: +ReadyMemoType=安装类型: +ReadyMemoComponents=选定组件: +ReadyMemoGroup=开始菜单文件夹: +ReadyMemoTasks=附加任务: + +; *** “正在准备安装”向导页 +WizardPreparing=正在准备安装 +PreparingDesc=安装程序正在准备安装 [name] 到您的电脑中。 +PreviousInstallNotCompleted=先前程序的安装/卸载未完成。您需要重新启动您的电脑才能完成安装。%n%n在重新启动电脑后,再运行安装完成 [name] 的安装。 +CannotContinue=安装程序不能继续。请单击“取消”退出。 +ApplicationsFound=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。 +ApplicationsFound2=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。安装完成后,安装程序将尝试重新启动应用程序。 +CloseApplications=自动关闭该应用程序(&A) +DontCloseApplications=不要关闭该应用程序(D) +ErrorCloseApplications=安装程序无法自动关闭所有应用程序。在继续之前,我们建议您关闭所有使用需要更新的安装程序文件。 +PrepareToInstallNeedsRestart=安装程序必须重新启动计算机。重新启动计算机后,请再次运行安装程序以完成 [name] 的安装。%n%n是否立即重新启动? + +; *** “正在安装”向导页 +WizardInstalling=正在安装 +InstallingLabel=安装程序正在安装 [name] 到您的电脑中,请稍等。 + +; *** “安装完成”向导页 +FinishedHeadingLabel=[name] 安装完成 +FinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。 +FinishedLabel=安装程序已在您的电脑中安装了 [name]。此应用程序可以通过选择安装的快捷方式运行。 +ClickFinish=单击“完成”退出安装程序。 +FinishedRestartLabel=要完成 [name] 的安装,安装程序必须重新启动您的电脑。您想现在重新启动吗? +FinishedRestartMessage=要完成 [name] 的安装,安装程序必须重新启动您的电脑。%n%n您想现在重新启动吗? +ShowReadmeCheck=是,您想查阅自述文件 +YesRadio=是,立即重新启动电脑(&Y) +NoRadio=否,稍后重新启动电脑(&N) +; 用于象“运行 MyProg.exe” +RunEntryExec=运行 %1 +; 用于象“查阅 Readme.txt” +RunEntryShellExec=查阅 %1 + +; *** “安装程序需要下一张磁盘”提示 +ChangeDiskTitle=安装程序需要下一张磁盘 +SelectDiskLabel2=请插入磁盘 %1 并单击“确定”。%n%n如果这个磁盘中的文件不能在不同于下列显示的文件夹中找到,输入正确的路径或单击“浏览”。 +PathLabel=路径(&P): +FileNotInDir2=文件“%1”不能在“%2”定位。请插入正确的磁盘或选择其它文件夹。 +SelectDirectoryLabel=请指定下一张磁盘的位置。 + +; *** 安装状态消息 +SetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。 +AbortRetryIgnoreSelectAction=选项 +AbortRetryIgnoreRetry=重试(&T) +AbortRetryIgnoreIgnore=忽略错误并继续(&I) +AbortRetryIgnoreCancel=关闭安装程序 + +; *** 安装状态消息 +StatusClosingApplications=正在关闭应用程序... +StatusCreateDirs=正在创建目录... +StatusExtractFiles=正在解压缩文件... +StatusCreateIcons=正在创建快捷方式... +StatusCreateIniEntries=正在创建 INI 条目... +StatusCreateRegistryEntries=正在创建注册表条目... +StatusRegisterFiles=正在注册文件... +StatusSavingUninstall=正在保存卸载信息... +StatusRunProgram=正在完成安装... +StatusRestartingApplications=正在重启应用程序... +StatusRollback=正在撤销更改... + +; *** 其它错误 +ErrorInternal2=内部错误: %1 +ErrorFunctionFailedNoCode=%1 失败 +ErrorFunctionFailed=%1 失败;错误代码 %2 +ErrorFunctionFailedWithMessage=%1 失败;错误代码 %2.%n%3 +ErrorExecutingProgram=不能执行文件:%n%1 + +; *** 注册表错误 +ErrorRegOpenKey=打开注册表项时出错:%n%1\%2 +ErrorRegCreateKey=创建注册表项时出错:%n%1\%2 +ErrorRegWriteKey=写入注册表项时出错:%n%1\%2 + +; *** INI 错误 +ErrorIniEntry=在文件“%1”创建 INI 项目错误。 + +; *** 文件复制错误 +FileAbortRetryIgnoreSkipNotRecommended=跳过这个文件 (不推荐)(&S) +FileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续 (不推荐)(&I) +SourceIsCorrupted=源文件已损坏 +SourceDoesntExist=源文件“%1”不存在 +ExistingFileReadOnly2=无法替换现有文件,因为它是只读的。 +ExistingFileReadOnlyRetry=移除只读属性并重试(&R) +ExistingFileReadOnlyKeepExisting=保留现有文件(&K) +ErrorReadingExistingDest=尝试读取现有文件时发生一个错误: +FileExists=文件已经存在。%n%n您想要安装程序覆盖它吗? +ExistingFileNewer=现有的文件新与安装程序要安装的文件。推荐您保留现有文件。%n%n您想要保留现有的文件吗? +ErrorChangingAttr=尝试改变下列现有的文件的属性时发生一个错误: +ErrorCreatingTemp=尝试在目标目录创建文件时发生一个错误: +ErrorReadingSource=尝试读取下列源文件时发生一个错误: +ErrorCopying=尝试复制下列文件时发生一个错误: +ErrorReplacingExistingFile=尝试替换现有的文件时发生错误: +ErrorRestartReplace=重启电脑后替换文件失败: +ErrorRenamingTemp=尝试重新命名以下目标目录中的一个文件时发生错误: +ErrorRegisterServer=不能注册 DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 失败;退出代码 %1 +ErrorRegisterTypeLib=不能注册类型库: %1 + +; *** 卸载显示名字标记 +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32位 +UninstallDisplayNameMark64Bit=64位 +UninstallDisplayNameMarkAllUsers=所有用户 +UninstallDisplayNameMarkCurrentUser=当前用户 + +; *** 安装后错误 +ErrorOpeningReadme=当尝试打开自述文件时发生一个错误。 +ErrorRestartingComputer=安装程序不能重新启动电脑,请手动重启。 + +; *** 卸载消息 +UninstallNotFound=文件“%1”不存在。不能卸载。 +UninstallOpenError=文件“%1”不能打开。不能卸载。 +UninstallUnsupportedVer=卸载日志文件“%1”有未被这个版本的卸载器承认的格式。不能卸载 +UninstallUnknownEntry=在卸载日志中遇到一个未知的条目 (%1) +ConfirmUninstall=您确认想要完全删除 %1 及它的所有组件吗? +UninstallOnlyOnWin64=这个安装程序只能在 64 位 Windows 中进行卸载。 +OnlyAdminCanUninstall=这个安装的程序只能是有管理员权限的用户才能卸载。 +UninstallStatusLabel=正在从您的电脑中删除 %1,请等待。 +UninstalledAll=%1 已顺利地从您的电脑中删除。 +UninstalledMost=%1 卸载完成。%n%n有一些内容不能被删除。您可以手工删除它们。 +UninstalledAndNeedsRestart=要完成 %1 的卸载,您的电脑必须重新启动。%n%n您现在想重新启动电脑吗? +UninstallDataCorrupted=“%1”文件被破坏,不能卸载 + +; *** 卸载状态消息 +ConfirmDeleteSharedFileTitle=删除共享文件吗? +ConfirmDeleteSharedFile2=系统中包含的下列共享文件已经不被其它程序使用。您想要卸载程序删除这些共享文件吗?%n%n如果这些文件被删除,但还有程序正在使用这些文件,这些程序可能不能正确执行。如果您不能确定,选择“否”。把这些文件保留在系统中以免引起问题。 +SharedFileNameLabel=文件名: +SharedFileLocationLabel=位置: +WizardUninstalling=卸载状态 +StatusUninstalling=正在卸载 %1... + ; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=ڰװ %1 -ShutdownBlockReasonUninstallingApp=ж %1 +ShutdownBlockReasonInstallingApp=正在安装 %1. +ShutdownBlockReasonUninstallingApp=正在卸载 %1. + ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. + [CustomMessages] -NameAndVersion=%1 汾 %2 -AdditionalIcons=ݷʽ: -CreateDesktopIcon=ݷʽ(&D) -CreateQuickLaunchIcon=ݷʽ(&Q) -ProgramOnTheWeb=Web ϵ %1 -UninstallProgram=ж %1 -LaunchProgram= %1 -AssocFileExtension= %1 %2 ļչ(&A) -AssocingFileExtension= %1 %2 ļչ... -AutoStartProgramGroupDescription=: -AutoStartProgram=Զ %1 -AddonHostProgramNotFound=޷ѡļжλ %1%n%nǷҪ? \ No newline at end of file + +NameAndVersion=%1 版本 %2 +AdditionalIcons=附加快捷方式: +CreateDesktopIcon=创建桌面快捷方式(&D) +CreateQuickLaunchIcon=创建快速运行栏快捷方式(&Q) +ProgramOnTheWeb=%1 网站 +UninstallProgram=卸载 %1 +LaunchProgram=运行 %1 +AssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A) +AssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联... +AutoStartProgramGroupDescription=启动组: +AutoStartProgram=自动启动 %1 +AddonHostProgramNotFound=%1无法找到您所选择的文件夹。%n%n您想要继续吗? + diff --git a/build/win32/i18n/Default.zh-tw.isl b/build/win32/i18n/Default.zh-tw.isl index 4746349e191..fb748761f99 100644 --- a/build/win32/i18n/Default.zh-tw.isl +++ b/build/win32/i18n/Default.zh-tw.isl @@ -1,298 +1,359 @@ -; *** Inno Setup version 5.5.3+ Traditional Chinese messages *** +; *** Inno Setup version 6.0.0+ Chinese Traditional messages *** ; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; Name: John Wu, mr.johnwu@gmail.com +; Base on 5.5.3+ translations by Samuel Lee, Email: 751555749@qq.com +; Translation based on network resource ; -; Note: When translating this text, do not add periods (.) to the end of -; messages that didn't have them already, because on those messages Inno -; Setup adds the periods automatically (appending a period would result in -; two periods being displayed). + [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. -LanguageName=Traditional Chinese +; If Language Name display incorrect, uncomment next line +LanguageName=<7e41><9ad4><4e2d><6587> LanguageID=$0404 -LanguageCodePage=950 +LanguageCodepage=950 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. -;DialogFontName= -;DialogFontSize=8 -;WelcomeFontName=Verdana -;WelcomeFontSize=12 -;TitleFontName=Arial -;TitleFontSize=29 -;CopyrightFontName=Arial -;CopyrightFontSize=8 +DialogFontName=新細明體 +DialogFontSize=9 +TitleFontName=Arial +TitleFontSize=28 +WelcomeFontName=新細明體 +WelcomeFontSize=12 +CopyrightFontName=新細明體 +CopyrightFontSize=9 + [Messages] + ; *** Application titles -SetupAppTitle=w˵{ -SetupWindowTitle=w˵{ - %1 -UninstallAppTitle=Ѱw -UninstallAppFullTitle=%1 Ѱw +SetupAppTitle=安裝程式 +SetupWindowTitle=%1 安裝程式 +UninstallAppTitle=解除安裝 +UninstallAppFullTitle=解除安裝 %1 + ; *** Misc. common -InformationTitle=T -ConfirmTitle=T{ -ErrorTitle=~ +InformationTitle=訊息 +ConfirmTitle=確認 +ErrorTitle=錯誤 + ; *** SetupLdr messages -SetupLdrStartupMessage=o|w %1Cn~? -LdrCannotCreateTemp=Lkإ߼ȦsɡCwˤw -LdrCannotExecTemp=LkȦsؿɮסCwˤw +SetupLdrStartupMessage=這將會安裝 %1。您想要繼續嗎? +LdrCannotCreateTemp=無法建立暫存檔案。安裝程式將會結束。 +LdrCannotExecTemp=無法執行暫存檔案。安裝程式將會結束。 +HelpTextNote= + ; *** Startup error messages -LastErrorMessage=%1C%n%n~ %2: %3 -SetupFileMissing=w˥ؿʤɮ %1CЭץDAέso{sƥC -SetupFileCorrupt=w˵{ɮפwlCЭsoӵ{ƥC -SetupFileCorruptOrWrongVer=w˵{ɮפwlAΤۮePw˵{CЭץDAέso{sƥC -InvalidParameter=bROCWǻFLĪѼ:%n%n%1 -SetupAlreadyRunning=w˵{wb椤C -WindowsVersionNotSupported={䴩qҰ檺 Windows C -WindowsServicePackRequired={ݭn %1 Service Pack %2 ΧsC -NotOnThisPlatform={|b %1 WC -OnlyOnThisPlatform={b %1 WC -OnlyOnTheseArchitectures={uiw˦bMUCBz[c]p Windows W:%n%n%1 -MissingWOW64APIs=z檺 Windows tw˵{ 64 줸w˩һݪ\CYnץDAЦw Service Pack %1C -WinVersionTooLowError={ݭn %1 %2 ΧsC -WinVersionTooHighError={Lkw˦b %1 %2 ΧsWC -AdminPrivilegesRequired=w˦{ɡAHtκ޲znJC -PowerUserPrivilegesRequired=zw˦{ɡAHtκ޲z Power Users sժnJC -SetupAppRunningError=wˮɰ %1 ثeb椤C%n%nХߧYҦCYn~AЫ@U [Tw]; YnAЫ@U []C -UninstallAppRunningError=Ѱwˮɰ %1 ثeb椤C%n%nХߧYҦCYn~AЫ@U [Tw]; YnAЫ@U []C +LastErrorMessage=%1%n%n錯誤 %2: %3 +SetupFileMissing=安裝資料夾中遺失檔案 %1。請修正此問題或重新取得此軟體。 +SetupFileCorrupt=安裝檔案已經損毀。請重新取得此軟體。 +SetupFileCorruptOrWrongVer=安裝檔案已經損毀,或與安裝程式的版本不符。請重新取得此軟體。 +InvalidParameter=某個無效的變量已被傳遞到了命令列:%n%n%1 +SetupAlreadyRunning=安裝程式已經在執行。 +WindowsVersionNotSupported=本安裝程式並不支援目前在電腦所運行的 Windows 版本。 +WindowsServicePackRequired=本安裝程式需要 %1 Service Pack %2 或更新。 +NotOnThisPlatform=這個程式無法在 %1 執行。 +OnlyOnThisPlatform=這個程式必須在 %1 執行。 +OnlyOnTheseArchitectures=這個程式只能在專門為以下處理器架構而設計的 Windows 上安裝:%n%n%1 +WinVersionTooLowError=這個程式必須在 %1 版本 %2 或以上的系統執行。 +WinVersionTooHighError=這個程式無法安裝在 %1 版本 %2 或以上的系統。 +AdminPrivilegesRequired=您必須登入成系統管理員以安裝這個程式。 +PowerUserPrivilegesRequired=您必須登入成具有系統管理員或 Power User 權限的使用者以安裝這個程式。 +SetupAppRunningError=安裝程式偵測到 %1 正在執行。%n%n請關閉該程式後按 [確定] 繼續,或按 [取消] 離開。 +UninstallAppRunningError=解除安裝程式偵測到 %1 正在執行。%n%n請關閉該程式後按 [確定] 繼續,或按 [取消] 離開。 + +; *** Startup questions +PrivilegesRequiredOverrideTitle=選擇安裝程式安裝模式 +PrivilegesRequiredOverrideInstruction=選擇安裝模式 +PrivilegesRequiredOverrideText1=可以為所有使用者安裝 %1 (需要系統管理權限),或是僅為您安裝。 +PrivilegesRequiredOverrideText2=可以僅為您安裝 %1,或是為所有使用者安裝 (需要系統管理權限)。 +PrivilegesRequiredOverrideAllUsers=為所有使用者安裝 (&A) +PrivilegesRequiredOverrideAllUsersRecommended=為所有使用者安裝 (建議選項) (&A) +PrivilegesRequiredOverrideCurrentUser=僅為我安裝 (&M) +PrivilegesRequiredOverrideCurrentUserRecommended=僅為我安裝 (建議選項) (&M) + ; *** Misc. errors -ErrorCreatingDir=w˵{Lkإߥؿ "%1" -ErrorTooManyFilesInDir=]ؿ "%1" ]tӦhɮסAҥHLkb䤤إɮ +ErrorCreatingDir=安裝程式無法建立資料夾“%1”。 +ErrorTooManyFilesInDir=無法在資料夾“%1”內建立檔案,因為資料夾內有太多的檔案。 + ; *** Setup common messages -ExitSetupTitle=w -ExitSetupMessage=w˥CYߧYAN|w˵{C%n%nziHyAw˵{ӧwˡC%n%nnw˶? -AboutSetupMenuItem=w˵{(&A)... -AboutSetupTitle=w˵{ -AboutSetupMessage=%1 %2%n%3%n%n%1 :%n%4 +ExitSetupTitle=結束安裝程式 +ExitSetupMessage=安裝尚未完成。如果您現在結束安裝程式,這個程式將不會被安裝。%n%n您可以稍後再執行安裝程式以完成安裝程序。您現在要結束安裝程式嗎? +AboutSetupMenuItem=關於安裝程式(&A)... +AboutSetupTitle=關於安裝程式 +AboutSetupMessage=%1 版本 %2%n%3%n%n%1 網址:%n%4 AboutSetupNote= TranslatorNote= + ; *** Buttons -ButtonBack=< W@B(&B) -ButtonNext=U@B(&N) > -ButtonInstall=w(&I) -ButtonOK=Tw -ButtonCancel= -ButtonYes=O(&Y) -ButtonYesToAll=ҬO(&A) -ButtonNo=_(&N) -ButtonNoToAll=ҧ_(&O) -ButtonFinish=(&F) -ButtonBrowse=s(&B)... -ButtonWizardBrowse=s(&R)... -ButtonNewFolder=إ߷sƧ(&M) +ButtonBack=< 上一步(&B) +ButtonInstall=安裝(&I) +ButtonNext=下一步(&N) > +ButtonOK=確定 +ButtonCancel=取消 +ButtonYes=是(&Y) +ButtonYesToAll=全部皆是(&A) +ButtonNo=否(&N) +ButtonNoToAll=全部皆否(&O) +ButtonFinish=完成(&F) +ButtonBrowse=瀏覽(&B)... +ButtonWizardBrowse=瀏覽(&R)... +ButtonNewFolder=建立新資料夾(&M) + ; *** "Select Language" dialog messages -SelectLanguageTitle=w˵{y -SelectLanguageLabel=w˴ҭnϥΪy: +SelectLanguageTitle=選擇安裝語言 +SelectLanguageLabel=選擇在安裝過程中使用的語言: + ; *** Common wizard text -ClickNext=Yn~AЫ@U [U@B]; YnwˡAЫ@U []C +ClickNext=按 [下一步] 繼續安裝,或按 [取消] 結束安裝程式。 BeveledLabel= -BrowseDialogTitle=sƧ -BrowseDialogLabel=бqUCM椤ƧAM@U [Tw]C -NewFolderName=sWƧ +BrowseDialogTitle=瀏覽資料夾 +BrowseDialogLabel=在下面的資料夾列表中選擇一個資料夾,然後按 [確定]。 +NewFolderName=新資料夾 + ; *** "Welcome" wizard page -WelcomeLabel1=wϥ [name] w˺F -WelcomeLabel2=o|bzqWw [name/ver]C%n%nijzҦLε{AMA~C +WelcomeLabel1=歡迎使用 [name] 安裝程式 +WelcomeLabel2=這個安裝程式將會安裝 [name/ver] 到您的電腦。%n%n我們強烈建議您在安裝過程中關閉其它的應用程式,以避免與安裝程式發生沖突。 + ; *** "Password" wizard page -WizardPassword=KX -PasswordLabel1=w˨KXO@C -PasswordLabel3=дѱKXAM@U [U@B] H~CKXϤjpgC -PasswordEditLabel=KX(&P): -IncorrectPassword=JKXTCЦAդ@C +WizardPassword=密碼 +PasswordLabel1=這個安裝程式具有密碼保護。 +PasswordLabel3=請輸入密碼,然後按 [下一步] 繼續。密碼是區分大小寫的。 +PasswordEditLabel=密碼(&P): +IncorrectPassword=您輸入的密碼不正確,請重新輸入。 + ; *** "License Agreement" wizard page -WizardLicense=vX -LicenseLabel=Х\ŪUCnTA~C -LicenseLabel3=о\ŪUCvXCzXڡA~~wˡC -LicenseAccepted=ڱX(&A) -LicenseNotAccepted=ڤX(&D) +WizardLicense=授權合約 +LicenseLabel=請閱讀以下授權合約。 +LicenseLabel3=請閱讀以下授權合約,您必須接受合約的各項條款才能繼續安裝。 +LicenseAccepted=我同意(&A) +LicenseNotAccepted=我不同意(&D) + ; *** "Information" wizard pages -WizardInfoBefore=T -InfoBeforeLabel=Х\ŪUCnTA~C -InfoBeforeClickLabel=zdzƦnn~wˮɡAЫ@U [U@B]C -WizardInfoAfter=T -InfoAfterLabel=Х\ŪUCnTA~C -InfoAfterClickLabel=zdzƦnn~wˮɡAЫ@U [U@B]C +WizardInfoBefore=訊息 +InfoBeforeLabel=在繼續安裝之前請閱讀以下重要資訊。 +InfoBeforeClickLabel=當您準備好繼續安裝,請按 [下一步]。 +WizardInfoAfter=訊息 +InfoAfterLabel=在繼續安裝之前請閱讀以下重要資訊。 +InfoAfterClickLabel=當您準備好繼續安裝,請按 [下一步]。 + ; *** "User Information" wizard page -WizardUserInfo=ϥΪ̸T -UserInfoDesc=пJzTC -UserInfoName=ϥΪ̦W(&U): -UserInfoOrg=´(&O): -UserInfoSerial=Ǹ(&S): -UserInfoNameRequired=JW١C +WizardUserInfo=使用者資訊 +UserInfoDesc=請輸入您的資料。 +UserInfoName=使用者名稱(&U): +UserInfoOrg=組織(&O): +UserInfoSerial=序號(&S): +UserInfoNameRequired=您必須輸入您的名稱。 + ; *** "Select Destination Location" wizard page -WizardSelectDir=تam -SelectDirDesc=N [name] w˦bB? -SelectDirLabel3=w˵{|N [name] w˦bUCƧC -SelectDirBrowseLabel=Yn~AЫ@U [U@B]CYzQPƧAЫ@U [s]C -DiskSpaceMBLabel=ܤֶ [mb] MB iκϺЪŶC -CannotInstallToNetworkDrive=w˵{Lkw˨ϺоC -CannotInstallToUNCPath=w˵{Lkw˨ UNC |C -InvalidPath=J]tϺоN|AҦp:%n%nC:\APP%n%nοJUC榡 UNC |:%n%n\\A\@ -InvalidDrive=Ϻо UNC @ΤsbεLksCпLϺо UNC @ΡC -DiskSpaceWarningTitle=ϺЪŶ -DiskSpaceWarning=w˵{ܤֻݭn %1 KB iΪŶ~wˡAҿϺоiΪŶu %2 KBC%n%nn~? -DirNameTooLong=ƧW٩θ|LC -InvalidDirName=ƧWٵLġC -BadDirName32=ƧW٤o]tUC@r:%n%n%1 -DirExistsTitle=Ƨwsb -DirExists=wƧ %n%n%1%n%nCnw˨ӸƧ? -DirDoesntExistTitle=Ƨsb -DirDoesntExist=Ƨ %n%n%1%n%n sbCnإ߸ӸƧ? +WizardSelectDir=選擇目的資料夾 +SelectDirDesc=選擇安裝程式安裝 [name] 的位置。 +SelectDirLabel3=安裝程式將會把 [name] 安裝到下面的資料夾。 +SelectDirBrowseLabel=按 [下一步] 繼續,如果您想選擇另一個資料夾,請按 [瀏覽]。 +DiskSpaceMBLabel=最少需要 [mb] MB 磁碟空間。 +CannotInstallToNetworkDrive=安裝程式無法安裝於網絡磁碟機。 +CannotInstallToUNCPath=安裝程式無法安裝於 UNC 路徑。 +InvalidPath=您必須輸入完整的路徑名稱及磁碟機代碼。%n%n例如 C:\App 或 UNC 路徑格式 \\伺服器\共用資料夾。 +InvalidDrive=您選取的磁碟機或 UNC 名稱不存在或無法存取,請選擇其他的目的地。 +DiskSpaceWarningTitle=磁碟空間不足 +DiskSpaceWarning=安裝程式需要至少 %1 KB 的磁碟空間,您所選取的磁碟只有 %2 KB 可用空間。%n%n您要繼續安裝嗎? +DirNameTooLong=資料夾名稱或路徑太長。 +InvalidDirName=資料夾名稱不正確。 +BadDirName32=資料夾名稱不得包含以下特殊字元:%n%n%1 +DirExistsTitle=資料夾已經存在 +DirExists=資料夾:%n%n%1%n%n 已經存在。仍要安裝到該資料夾嗎? +DirDoesntExistTitle=資料夾不存在 +DirDoesntExist=資料夾:%n%n%1%n%n 不存在。要建立該資料夾嗎? + ; *** "Select Components" wizard page -WizardSelectComponents= -SelectComponentsDesc=w˭Ǥ? -SelectComponentsLabel2=znw˪; Mznw˪CzdzƦnn~ɡAЫ@U [U@B]C -FullInstallation=w +WizardSelectComponents=選擇元件 +SelectComponentsDesc=選擇將會被安裝的元件。 +SelectComponentsLabel2=選擇您想要安裝的元件;清除您不想安裝的元件。然後按 [下一步] 繼續安裝。 +FullInstallation=完整安裝 ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=²w -CustomInstallation=ۭqw -NoUninstallWarningTitle=w -NoUninstallWarning=w˵{zqwwˤFUC:%n%n%1%n%nNoǤä|ϤѰwˡC%n%nn~? +CompactInstallation=最小安裝 +CustomInstallation=自訂安裝 +NoUninstallWarningTitle=元件已存在 +NoUninstallWarning=安裝程式偵測到以下元件已經安裝在您的電腦上:%n%n%1%n%n取消選擇這些元件將不會移除它們。%n%n您仍然要繼續嗎? ComponentSize1=%1 KB ComponentSize2=%1 MB -ComponentsDiskSpaceMBLabel=ثeܦܤֻݭn [mb] MB ϺЪŶC +ComponentsDiskSpaceMBLabel=目前的選擇需要至少 [mb] MB 磁碟空間。 + ; *** "Select Additional Tasks" wizard page -WizardSelectTasks=Lu@ -SelectTasksDesc=ٶǨLu@? -SelectTasksLabel2=пw˵{bw [name] ɡAB~檺Lu@AM@U [U@B]C +WizardSelectTasks=選擇附加的工作 +SelectTasksDesc=選擇要執行的附加工作。 +SelectTasksLabel2=選擇安裝程式在安裝 [name] 時要執行的附加工作,然後按 [下一步]。 + ; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup= [}l] \Ƨ -SelectStartMenuFolderDesc=w˵{N{|mB? -SelectStartMenuFolderLabel3=w˵{NbUC [}l] \Ƨإߵ{|C -SelectStartMenuFolderBrowseLabel=Yn~AЫ@U [U@B]CYzQPƧAЫ@U [s]C -MustEnterGroupName=JƧW١C -GroupNameTooLong=ƧW٩θ|LC -InvalidGroupName=ƧWٵLġC -BadGroupName=ƧW٤o]tUC@r:%n%n%1 -NoProgramGroupCheck2=nإ [}l] \Ƨ(&D) +WizardSelectProgramGroup=選擇「開始」功能表的資料夾 +SelectStartMenuFolderDesc=選擇安裝程式建立程式的捷徑的位置。 +SelectStartMenuFolderLabel3=安裝程式將會把程式的捷徑建立在下面的「開始」功能表資料夾。 +SelectStartMenuFolderBrowseLabel=按 [下一步] 繼續,如果您想選擇另一個資料夾,請按 [瀏覽]。 +MustEnterGroupName=您必須輸入一個資料夾的名稱。 +GroupNameTooLong=資料夾名稱或路徑太長。 +InvalidGroupName=資料夾名稱不正確。 +BadGroupName=資料夾名稱不得包含下列字元:%n%n%1 +NoProgramGroupCheck2=不要在「開始」功能表中建立資料夾(&D) + ; *** "Ready to Install" wizard page -WizardReady=wi}lw -ReadyLabel1=w˵{{bwi}lN [name] w˨zqWC -ReadyLabel2a=Yn~wˡAЫ@U [w]; Yn˾\ܧ]wAЫ@U [W@B]C -ReadyLabel2b=Yn~wˡAЫ@U [w]C -ReadyMemoUserInfo=ϥΪ̸T: -ReadyMemoDir=تam: -ReadyMemoType=w: -ReadyMemoComponents=: -ReadyMemoGroup=[}l] \Ƨ: -ReadyMemoTasks=Lu@: +WizardReady=準備安裝 +ReadyLabel1=安裝程式將開始安裝 [name] 到您的電腦中。 +ReadyLabel2a=按下 [安裝] 繼續安裝,或按 [上一步] 重新檢視或設定各選項的內容。 +ReadyLabel2b=按下 [安裝] 繼續安裝。 +ReadyMemoUserInfo=使用者資訊 +ReadyMemoDir=目的資料夾: +ReadyMemoType=安裝型態: +ReadyMemoComponents=選擇的元件: +ReadyMemoGroup=「開始」功能表資料夾: +ReadyMemoTasks=附加工作: + ; *** "Preparing to Install" wizard page -WizardPreparing=bdzƦw -PreparingDesc=w˵{bdzƱN [name] w˨zqWC -PreviousInstallNotCompleted=W@ӵ{w/|CsҰʹqA~৹ӦwˡC%n%nЦbsҰʹqAsw˵{AH [name] wˡC -CannotContinue=w˵{Lk~CЫ@U [] HC -ApplicationsFound=w˵{sUCε{bϥΪ@ɮסCijz\w˵{۰oε{C -ApplicationsFound2=w˵{sUCε{bϥΪ@ɮסCijz\w˵{۰oε{Cw˧Aw˵{N|խsҰʳoε{C -CloseApplications=۰ε{(&A) -DontCloseApplications=nε{(&D) -ErrorCloseApplications=w˵{Lk۰Ҧε{CijzҦbϥΦw˵{sɮתε{AMA~C +WizardPreparing=準備安裝程式 +PreparingDesc=安裝程式準備將 [name] 安裝到您的電腦上。 +PreviousInstallNotCompleted=先前的安裝/ 解除安裝尚未完成,您必須重新啟動電腦以完成該安裝。%n%n在重新啟動電腦之後,請再執行這個程式來安裝 [name]。 +CannotContinue=安裝程式無法繼續。請按 [取消] 離開。 +ApplicationsFound=下面的應用程式正在使用安裝程式所需要更新的文檔。建議您允許安裝程式自動關閉這些應用程式。 +ApplicationsFound2=下面的應用程式正在使用安裝程式所需要更新的文檔。建議您允許安裝程式自動關閉這些應用程式。當安裝過程結束後,本安裝程式將會嘗試重新開啟該應用程式。 +CloseApplications=關閉應用程式(&A) +DontCloseApplications=不要關閉應用程式 (&D) +ErrorCloseApplications=安裝程式無法自動關閉所有應用程式。建議您在繼續前先關閉所有應用程式使用的檔案。 + ; *** "Installing" wizard page -WizardInstalling=wˤ -InstallingLabel=еyԡAw˵{bN [name] w˨zqWC +WizardInstalling=正在安裝 +InstallingLabel=請稍候,安裝程式正在將 [name] 安裝到您的電腦上 + ; *** "Setup Completed" wizard page -FinishedHeadingLabel=b [name] w˺F -FinishedLabelNoIcons=w˵{wzqW [name] wˡC -FinishedLabel=w˵{wzqW [name] wˡCziHҦw˪|ӱҰε{C -ClickFinish=Ы@U []AHwˡC -FinishedRestartLabel=w˵{sҰʱzqA~৹ [name] wˡCnߧYsҰʶ? -FinishedRestartMessage=w˵{sҰʱzqA~৹ [name] wˡC%n%nnߧYsҰʶ? -ShowReadmeCheck=OAڭn˵Ūɮ -YesRadio=OAߧYsҰʹq(&Y) -NoRadio=_AyԦAsҰʹq(&N) +FinishedHeadingLabel=安裝完成 +FinishedLabelNoIcons=安裝程式已經將 [name] 安裝在您的電腦上。 +FinishedLabel=安裝程式已經將 [name] 安裝在您的電腦中,您可以選擇程式的圖示來執行該應用程式。 +ClickFinish=按 [完成] 以結束安裝程式。 +FinishedRestartLabel=要完成 [name] 的安裝,安裝程式必須重新啟動您的電腦。您想要現在重新啟動電腦嗎? +FinishedRestartMessage=要完成 [name] 的安裝,安裝程式必須重新啟動您的電腦。%n%n您想要現在重新啟動電腦嗎? +ShowReadmeCheck=是,我要閱讀讀我檔案。 +YesRadio=是,立即重新啟動電腦(&Y) +NoRadio=否,我稍後重新啟動電腦(&N) ; used for example as 'Run MyProg.exe' -RunEntryExec= %1 +RunEntryExec=執行 %1 ; used for example as 'View Readme.txt' -RunEntryShellExec=˵ %1 -; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=w˵{ݭnU@iϤC -SelectDiskLabel2=дJϤ %1AM@U [Tw]C%n%nYϤWɮץiHbUCܤƧH~ƧAпJT|AΫ@U [s]C -PathLabel=|(&P): -FileNotInDir2=b "%2" 䤣ɮ "%1"CдJTϤAοLƧC -SelectDirectoryLabel=ЫwU@iϤmC +RunEntryShellExec=檢視 %1 + +; *** "Setup Needs the Next Disk" +ChangeDiskTitle=安裝程式需要下一張磁片 +SelectDiskLabel2=請插入磁片 %1,然後按 [確定]。%n%n如果檔案不在以下所顯示的資料夾之中,請輸入正確的資料夾名稱或按 [瀏覽] 選取。 +PathLabel=路徑(&P): +FileNotInDir2=檔案“%1”無法在“%2”找到。請插入正確的磁片或選擇其它的資料夾。 +SelectDirectoryLabel=請指定下一張磁片的位置。 + ; *** Installation phase messages -SetupAborted=w˥wC%n%nЭץDAAsw˵{C -EntryAbortRetryIgnore=YnAդ@AЫ@U []; Yn~AЫ@U []; YnwˡAЫ@U []C +SetupAborted=安裝沒有完成。%n%n請更正問題後重新安裝一次。 +AbortRetryIgnoreSelectAction=選取動作 +AbortRetryIgnoreRetry=請再試一次 (&T) +AbortRetryIgnoreIgnore=略過錯誤並繼續 (&I) +AbortRetryIgnoreCancel=取消安裝 + ; *** Installation status messages -StatusClosingApplications=bε{... -StatusCreateDirs=bإߥؿ... -StatusExtractFiles=bYɮ... -StatusCreateIcons=bإ߱|... -StatusCreateIniEntries=bإ INI ... -StatusCreateRegistryEntries=bإߵn... -StatusRegisterFiles=bnɮ... -StatusSavingUninstall=bxsѰw˸T... -StatusRunProgram=bw... -StatusRestartingApplications=bsҰε{... -StatusRollback=b_ܧ... +StatusClosingApplications=正在關閉應用程式... +StatusCreateDirs=正在建立資料夾... +StatusExtractFiles=正在解壓縮檔案... +StatusCreateIcons=正在建立程式集圖示... +StatusCreateIniEntries=寫入 INI 檔案的項目... +StatusCreateRegistryEntries=正在更新系統登錄... +StatusRegisterFiles=正在登錄檔案... +StatusSavingUninstall=儲存解除安裝資訊... +StatusRunProgram=正在完成安裝... +StatusRestartingApplications=正在重新開啟應用程式... +StatusRollback=正在復原變更... + ; *** Misc. errors -ErrorInternal2=~: %1 -ErrorFunctionFailedNoCode=%1 -ErrorFunctionFailed=%1 ; NX %2 -ErrorFunctionFailedWithMessage=%1 ; NX %2C%n%3 -ErrorExecutingProgram=Lkɮ:%n%1 +ErrorInternal2=內部錯誤: %1 +ErrorFunctionFailedNoCode=%1 失敗 +ErrorFunctionFailed=%1 失敗;代碼 %2 +ErrorFunctionFailedWithMessage=%1 失敗;代碼 %2.%n%3 +ErrorExecutingProgram=無法執行檔案:%n%1 + ; *** Registry errors -ErrorRegOpenKey=}ҵnXɵoͿ~:%n%1\%2 -ErrorRegCreateKey=إߵnXɵoͿ~:%n%1\%2 -ErrorRegWriteKey=gJnXɵoͿ~:%n%1\%2 +ErrorRegOpenKey=無法開啟登錄鍵:%n%1\%2 +ErrorRegCreateKey=無法建立登錄項目:%n%1\%2 +ErrorRegWriteKey=無法變更登錄項目:%n%1\%2 + ; *** INI errors -ErrorIniEntry=bɮ "%1" إ INI خɵoͿ~C +ErrorIniEntry=在檔案“%1”建立 INI 項目錯誤。 + ; *** File copying errors -FileAbortRetryIgnore=YnAդ@AЫ@U []; YnLɮסAЫ@U [] (ijϥ); YnwˡAЫ@U []C -FileAbortRetryIgnore2=YnAդ@AЫ@U []; Yn~AЫ@U [] (ijϥ); YnwˡAЫ@U []C -SourceIsCorrupted=l{ɤwl -SourceDoesntExist=l{ "%1" sb -ExistingFileReadOnly={ɮפwаOŪC%n%nYnŪݩʡAMAդ@AЫ@U []; YnLɮסAЫ@U []; YnwˡAЫ@U []C -ErrorReadingExistingDest=Ū{ɮ׮ɵoͿ~: -FileExists=wɮסC%n%nnѦw˵{[Hмg? -ExistingFileNewer={ɮ׸w˵{զw˪ɮ׷sCijzOd{ɮסC%n%nnOd{ɮ׶? -ErrorChangingAttr=ܧ{ɮתݩʮɵoͿ~: -ErrorCreatingTemp=զbتaؿإɮ׮ɵoͿ~: -ErrorReadingSource=Ūl{ɮɵoͿ~: -ErrorCopying=սƻsɮ׮ɵoͿ~: -ErrorReplacingExistingFile=ըN{ɮ׮ɵoͿ~: -ErrorRestartReplace=RestartReplace : -ErrorRenamingTemp=խsRWتaؿɮ׮ɵoͿ~: -ErrorRegisterServer=Lkn DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 ѡANX %1 -ErrorRegisterTypeLib=Lkn{w: %1 +FileAbortRetryIgnoreSkipNotRecommended=略過這個檔案 (不建議) (&S) +FileAbortRetryIgnoreIgnoreNotRecommended=略過錯誤並繼續 (不建議) (&I) +SourceDoesntExist=來源檔案“%1”不存在。 +SourceIsCorrupted=來源檔案已經損毀。 +ExistingFileReadOnly2=無法取代現有檔案,因為檔案已標示為唯讀。 +ExistingFileReadOnlyRetry=移除唯讀屬性並重試 (&R) +ExistingFileReadOnlyKeepExisting=保留現有檔案 (&K) +ErrorReadingExistingDest=讀取一個已存在的檔案時發生錯誤: +FileExists=檔案已經存在。%n%n 要讓安裝程式加以覆寫嗎? +ExistingFileNewer=存在的檔案版本比較新,建議您保留目前已存在的檔案。%n%n您要保留目前已存在的檔案嗎? +ErrorChangingAttr=在變更檔案屬性時發生錯誤: +ErrorCreatingTemp=在目的資料夾中建立檔案時發生錯誤: +ErrorReadingSource=讀取原始檔案時發生錯誤: +ErrorCopying=復制檔案時發生錯誤: +ErrorReplacingExistingFile=取代檔案時發生錯誤: +ErrorRestartReplace=重新啟動電腦後取代檔案失敗: +ErrorRenamingTemp=在目的資料夾變更檔案名稱時發生錯誤: +ErrorRegisterServer=無法注冊 DLL/OCX 檔案: %1。 +ErrorRegSvr32Failed=RegSvr32 失敗;退出代碼 %1 +ErrorRegisterTypeLib=無法注冊類型庫: %1。 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32-bit +UninstallDisplayNameMark64Bit=64-bit +UninstallDisplayNameMarkAllUsers=所有使用者 +UninstallDisplayNameMarkCurrentUser=目前使用者 + ; *** Post-installation errors -ErrorOpeningReadme=ն}Ūɮ׮ɵoͿ~C -ErrorRestartingComputer=w˵{LksҰʹqCФʰ榹@~C +ErrorOpeningReadme=開啟讀我檔案時發生錯誤。 +ErrorRestartingComputer=安裝程式無法重新啟動電腦,請以手動方式自行重新啟動電腦。 + ; *** Uninstaller messages -UninstallNotFound=Sɮ "%1"CLkѰwˡC -UninstallOpenError=Lk}ɮ "%1"CLkѰw -UninstallUnsupportedVer=Ѱw˵{LkѸѰw˰O "%1" 榡CLkѰw -UninstallUnknownEntry=bѰw˰O줣 (%1) -ConfirmUninstall=Twn %1 ΨҦ? -UninstallOnlyOnWin64=uib 64 줸 Windows WѰw˦wˡC -OnlyAdminCanUninstall=uƨtκ޲zvϥΪ̡A~Ѱw˦wˡC -UninstallStatusLabel=bqzq %1AеyԡC -UninstalledAll=w\qzq %1C -UninstalledMost=Ѱw %1 wC%n%nصLkCziHʥ[HC -UninstalledAndNeedsRestart=Yn %1 ѰwˡAsҰʱzqC%n%nnߧYsҰʶ? -UninstallDataCorrupted="%1" ɮפwlCLkѰw +UninstallNotFound=檔案“%1”不存在,無法移除程式。 +UninstallOpenError=無法開啟檔案“%1”,無法移除程式。 +UninstallUnsupportedVer=這個版本的解除安裝程式無法辨識記錄檔 “%1” 之格式,無法解除安裝。 +UninstallUnknownEntry=解除安裝記錄檔中發現未知的記錄 (%1)。 +ConfirmUninstall=您確定要完全移除 %1 及其相關的檔案嗎? +UninstallOnlyOnWin64=這個程式只能在 64 位元的 Windows 上解除安裝。 +OnlyAdminCanUninstall=這個程式要具備系統管理員權限的使用者方可解除安裝。 +UninstallStatusLabel=正在從您的電腦移除 %1 中,請稍候... +UninstalledAll=%1 已經成功從您的電腦中移除。 +UninstalledMost=%1 解除安裝完成。%n%n某些檔案及元件無法移除,您可以自行刪除這些檔案。 +UninstalledAndNeedsRestart=要完成 %1 的解除安裝程序,您必須重新啟動電腦。%n%n您想要現在重新啟動電腦嗎? +UninstallDataCorrupted=檔案“%1”已經損毀,無法解除安裝。 + ; *** Uninstallation phase messages -ConfirmDeleteSharedFileTitle=n@ɮ׶? -ConfirmDeleteSharedFile2=tΫXwL{bϥΤUC@ɮסCznѰwˡAH@ɮ׶?%n%np{bϥΦɮצӱNɮײAoǵ{iLk`B@CYTwAп [_]CNɮ׫OdbtΤWä|y󤣨}vTC -SharedFileNameLabel=ɮצW: -SharedFileLocationLabel=m: -WizardUninstalling=Ѱw˪A -StatusUninstalling=bѰw %1... +ConfirmDeleteSharedFileTitle=移除共用檔案 +ConfirmDeleteSharedFile2=系統顯示下列共用檔案已不再被任何程式所使用,您要移除這些檔案嗎?%n%n%1%n%n倘若您移除了以上檔案但仍有程式需要使用它們,將造成這些程式無法正常執行,因此您若無法確定請選擇 [否]。保留這些檔案在您的系統中不會造成任何損害。 +SharedFileNameLabel=檔案名稱: +SharedFileLocationLabel=位置: +WizardUninstalling=解除安裝狀態 +StatusUninstalling=正在解除安裝 %1... + ; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=bw %1C -ShutdownBlockReasonUninstallingApp=bѰw %1C +ShutdownBlockReasonInstallingApp=正在安裝 %1. +ShutdownBlockReasonUninstallingApp=正在解除安裝 %1. + ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. + [CustomMessages] -NameAndVersion=%1 %2 -AdditionalIcons=L|: -CreateDesktopIcon=إ߮ୱ|(&D) -CreateQuickLaunchIcon=إߧֳtҰʱ|(&Q) -ProgramOnTheWeb=Web W %1 -UninstallProgram=Ѱw %1 -LaunchProgram=Ұ %1 -AssocFileExtension=p %1 P %2 ɦW(&A) -AssocingFileExtension=bإ %1 P %2 ɦWpK -AutoStartProgramGroupDescription=Ұ: -AutoStartProgram=۰ʱҰ %1 -AddonHostProgramNotFound=bƧ䤣 %1C%n%nn~? \ No newline at end of file + +NameAndVersion=%1 版本 %2 +AdditionalIcons=附加圖示: +CreateDesktopIcon=建立桌面圖示(&D) +CreateQuickLaunchIcon=建立快速啟動圖示(&Q) +ProgramOnTheWeb=%1 的網站 +UninstallProgram=解除安裝 %1 +LaunchProgram=啟動 %1 +AssocFileExtension=將 %1 與檔案副檔名 %2 產生關聯(&A) +AssocingFileExtension=正在將 %1 與檔案副檔名 %2 產生關聯... +AutoStartProgramGroupDescription=開啟: +AutoStartProgram=自動開啟 %1 +AddonHostProgramNotFound=%1 無法在您所選的資料夾中找到。%n%n您是否還要繼續? diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl index 12189c080d4..986eba00d3e 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -1,4 +1,11 @@ +[Messages] +FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed shortcuts. +ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components? + [CustomMessages] +AdditionalIcons=Additional icons: +CreateDesktopIcon=Create a &desktop icon +CreateQuickLaunchIcon=Create a &Quick Launch icon AddContextMenuFiles=Add "Open with %1" action to Windows Explorer file context menu AddContextMenuFolders=Add "Open with %1" action to Windows Explorer directory context menu AssociateWithFiles=Register %1 as an editor for supported file types diff --git a/build/yarn.lock b/build/yarn.lock index 6b7c3ea0068..afb6ff0b7c8 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -158,6 +158,13 @@ "@types/node" "*" "@types/vinyl" "*" +"@types/gulp-gzip@^0.0.31": + version "0.0.31" + resolved "https://registry.yarnpkg.com/@types/gulp-gzip/-/gulp-gzip-0.0.31.tgz#9358def25540442138fd61a7227f40d4c1e26760" + integrity sha512-KQjHz1FTqLse8/EiktfhN/vo33vamX4gVAQhMTp55STDBA7UToW5CJqYyP7iRorkHK9/aJ2nL2hLkNZkY4M8+w== + dependencies: + "@types/node" "*" + "@types/gulp-json-editor@^2.2.31": version "2.2.31" resolved "https://registry.yarnpkg.com/@types/gulp-json-editor/-/gulp-json-editor-2.2.31.tgz#3c1a8950556c109a0e2d0ab11d5f2a2443665ed2" @@ -394,6 +401,11 @@ acorn@4.X: resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= +agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -412,6 +424,23 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + +ansi-colors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== + dependencies: + ansi-wrap "^0.1.0" + ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" @@ -424,16 +453,40 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-wrap@0.1.0: +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +append-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" + integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= + dependencies: + buffer-equal "^1.0.0" + applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -450,6 +503,16 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + array-back@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" @@ -535,6 +598,11 @@ aws4@^1.6.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w== +aws4@^1.8.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" + integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + azure-storage@^2.1.0: version "2.6.0" resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.6.0.tgz#84747ee54a4bd194bb960f89f3eff89d67acf1cf" @@ -552,11 +620,33 @@ azure-storage@^2.1.0: xml2js "0.2.7" xmlbuilder "0.4.3" +azure-storage@^2.10.2: + version "2.10.3" + resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.3.tgz#c5966bf929d87587d78f6847040ea9a4b1d4a50a" + integrity sha512-IGLs5Xj6kO8Ii90KerQrrwuJKexLgSwYC4oLWmc11mzKe7Jt2E5IVg+ZQ8K53YWZACtVTMBNO3iGuA+4ipjJxQ== + dependencies: + browserify-mime "~1.2.9" + extend "^3.0.2" + json-edm-parser "0.1.2" + md5.js "1.3.4" + readable-stream "~2.0.0" + request "^2.86.0" + underscore "~1.8.3" + uuid "^3.0.0" + validator "~9.4.1" + xml2js "0.2.8" + xmlbuilder "^9.0.7" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.2.3: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -569,6 +659,11 @@ beeper@^1.0.0: resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= +bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -615,16 +710,49 @@ browserify-mime@~1.2.9: resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f" integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8= +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= +buffer-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" + integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +bytes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -653,16 +781,49 @@ cheerio@^1.0.0-rc.1: lodash "^4.15.0" parse5 "^3.0.1" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= +clone-stats@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + clone@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +cloneable-readable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" + integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== + dependencies: + inherits "^2.0.1" + process-nextick-args "^2.0.0" + readable-stream "^2.3.5" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -673,6 +834,18 @@ code-block-writer@9.4.1: resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-9.4.1.tgz#1448fca79dfc7a3649000f4c85be6bc770604c4c" integrity sha512-LHAB+DL4YZDcwK8y/kAxZ0Lf/ncwLh/Ux4cTVWbPwIdrf1gPxXiPcwpz8r8/KqXu1aD+Raz46EOxDjFlbyO6bA== +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -697,6 +870,13 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + command-line-args@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.1.1.tgz#88e793e5bb3ceb30754a86863f0401ac92fd369a" @@ -717,6 +897,11 @@ commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== +compare-version@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" + integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA= + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -729,6 +914,13 @@ convert-source-map@1.X: dependencies: safe-buffer "~5.1.1" +convert-source-map@^1.5.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -805,25 +997,42 @@ debug-fabulous@0.0.X: lazy-debug-legacy "0.0.X" object-assign "4.1.0" -debug@2.X: +debug@2.X, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^4.1.1: +debug@4, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.6.tgz#a2646cb7ec3d5d7774614670a7a65de0c173edbc" + integrity sha1-omRst+w9XXd0YUZwp6Zd4MFz7bw= + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -911,6 +1120,21 @@ duplexer2@0.0.2: dependencies: readable-stream "~1.1.9" +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -918,7 +1142,24 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -end-of-stream@^1.1.0: +electron-osx-sign@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.16.tgz#0be8e579b2d9fa4c12d2a21f063898294b3434aa" + integrity sha512-ziMWfc3NmQlwnWLW6EaZq8nH2BWVng/atX5GWsGwhexJYpdW6hsg//MkAfRTRx1kR3Veiqkeiog1ibkbA4x0rg== + dependencies: + bluebird "^3.5.0" + compare-version "^0.1.2" + debug "^2.6.8" + isbinaryfile "^3.0.2" + minimist "^1.2.0" + plist "^3.0.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -930,6 +1171,33 @@ entities@^1.1.1, entities@~1.1.1: resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== +es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -960,6 +1228,19 @@ estraverse@^4.1.0, estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +event-stream@3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -981,6 +1262,11 @@ extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + extend@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" @@ -1005,11 +1291,26 @@ fancy-log@^1.1.0: color-support "^1.1.3" time-stamp "^1.0.0" +fancy-log@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-glob@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602" @@ -1055,6 +1356,22 @@ find-replace@^3.0.0: dependencies: array-back "^3.0.1" +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flush-write-stream@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -1078,6 +1395,20 @@ form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1087,11 +1418,29 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-mkdirp-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" + integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= + dependencies: + graceful-fs "^4.1.11" + through2 "^2.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1116,6 +1465,14 @@ github-releases@^0.4.1: prettyjson "1.2.1" request "2.81.0" +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + glob-parent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" @@ -1123,6 +1480,22 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" +glob-stream@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" + integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= + dependencies: + extend "^3.0.0" + glob "^7.1.1" + glob-parent "^3.1.0" + is-negated-glob "^1.0.0" + ordered-read-streams "^1.0.0" + pumpify "^1.3.5" + readable-stream "^2.1.5" + remove-trailing-separator "^1.0.1" + to-absolute-glob "^2.0.0" + unique-stream "^2.0.2" + glob@^7.0.6: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -1135,10 +1508,10 @@ glob@^7.0.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== +glob@^7.1.1, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1147,10 +1520,10 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1185,11 +1558,32 @@ graceful-fs@4.X: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= +graceful-fs@^4.0.0, graceful-fs@^4.1.11: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== +gulp-azure-storage@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/gulp-azure-storage/-/gulp-azure-storage-0.11.1.tgz#0e5f5d0f789da11206f1e5a9311a6cf7107877d7" + integrity sha512-csOwItwZV1P9GLsORVQy+CFwjYDdHNrBol89JlHdlhGx0fTgJBc1COTRZbjGRyRjgdUuVguo3YLl4ToJ10/SIQ== + dependencies: + azure-storage "^2.10.2" + delayed-stream "0.0.6" + event-stream "3.3.4" + mime "^1.3.4" + progress "^1.1.8" + queue "^3.0.10" + streamifier "^0.1.1" + vinyl "^2.2.0" + vinyl-fs "^3.0.3" + yargs "^15.3.0" + gulp-bom@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gulp-bom/-/gulp-bom-1.0.0.tgz#38a183a07187bd57a7922d37977441f379df2abf" @@ -1198,6 +1592,18 @@ gulp-bom@^1.0.0: gulp-util "^3.0.0" through2 "^2.0.0" +gulp-gzip@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/gulp-gzip/-/gulp-gzip-1.4.2.tgz#0422a94014248655b5b1a9eea1c2abee1d4f4337" + integrity sha512-ZIxfkUwk2XmZPTT9pPHrHUQlZMyp9nPhg2sfoeN27mBGpi7OaHnOD+WCN41NXjfJQ69lV1nQ9LLm1hYxx4h3UQ== + dependencies: + ansi-colors "^1.0.1" + bytes "^3.0.0" + fancy-log "^1.3.2" + plugin-error "^1.0.0" + stream-to-array "^2.3.0" + through2 "^2.0.3" + gulp-sourcemaps@^1.11.0: version "1.12.1" resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.12.1.tgz#b437d1f3d980cf26e81184823718ce15ae6597b6" @@ -1288,6 +1694,14 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -1302,6 +1716,18 @@ has-gulplog@^0.1.0: dependencies: sparkles "^1.0.0" +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + hash-base@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" @@ -1370,12 +1796,18 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== +https-proxy-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" + integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== dependencies: - safer-buffer ">= 2.1.2 < 3" + agent-base "5" + debug "4" + +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== ignore@^5.1.1: version "5.1.2" @@ -1390,7 +1822,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@~2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1408,6 +1840,21 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" @@ -1415,11 +1862,23 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^2.1.1: +is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -1432,6 +1891,11 @@ is-negated-glob@^1.0.0: resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -1444,6 +1908,13 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" @@ -1456,6 +1927,13 @@ is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -1468,11 +1946,16 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-utf8@^0.2.0: +is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-valid-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" + integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= + is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -1488,6 +1971,13 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isbinaryfile@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" + integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw== + dependencies: + buffer-alloc "^1.2.0" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1520,11 +2010,21 @@ json-schema-traverse@^0.3.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -1537,6 +2037,11 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +jsonc-parser@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee" + integrity sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1569,6 +2074,20 @@ lazy-debug-legacy@0.0.X: resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" integrity sha1-U3cWwHduTPeePtG2IfdljCkRsbE= +lazystream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= + dependencies: + readable-stream "^2.0.5" + +lead@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" + integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= + dependencies: + flush-write-stream "^1.0.2" + linkify-it@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" @@ -1576,6 +2095,13 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -1686,9 +2212,9 @@ lodash.unescape@4.0.1: integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= lodash@^4.15.0, lodash@^4.17.10: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== macos-release@^2.2.0: version "2.3.0" @@ -1707,6 +2233,11 @@ make-error@^1.2.0: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + markdown-it@^8.3.1: version "8.4.2" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" @@ -1744,6 +2275,11 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" @@ -1768,6 +2304,13 @@ mime-types@~2.1.17: dependencies: mime-db "~1.33.0" +mime-types@~2.1.19: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + mime@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -1780,10 +2323,10 @@ minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.0, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f" + integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw== minimist@~0.0.1: version "0.0.10" @@ -1838,13 +2381,20 @@ node-fetch@^2.6.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -normalize-path@^2.0.1: +normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" +now-and-later@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" + integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== + dependencies: + once "^1.3.2" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -1864,6 +2414,11 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + object-assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" @@ -1874,7 +2429,27 @@ object-assign@^3.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= -once@^1.3.0, once@^1.3.1, once@^1.4.0: +object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.0.4, object.assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -1889,6 +2464,13 @@ optimist@0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +ordered-read-streams@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" + integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= + dependencies: + readable-stream "^2.0.1" + os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -1920,6 +2502,30 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse-semver@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8" @@ -1934,6 +2540,16 @@ parse5@^3.0.1: dependencies: "@types/node" "*" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1949,6 +2565,13 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -1969,6 +2592,25 @@ picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== +plist@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" + integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== + dependencies: + base64-js "^1.2.3" + xmlbuilder "^9.0.7" + xmldom "0.1.x" + +plugin-error@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" + integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== + dependencies: + ansi-colors "^1.0.1" + arr-diff "^4.0.0" + arr-union "^3.1.0" + extend-shallow "^3.0.2" + prettyjson@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289" @@ -1982,6 +2624,11 @@ priorityqueuejs@^1.0.0: resolved "https://registry.yarnpkg.com/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz#2ee4f23c2560913e08c07ce5ccdd6de3df2c5af8" integrity sha1-LuTyPCVgkT4IwHzlzN1t498sWvg= +process-nextick-args@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -1992,6 +2639,29 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -2000,11 +2670,25 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +pumpify@^1.3.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + q@^1.0.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -2020,6 +2704,18 @@ qs@~6.5.1: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +queue@^3.0.10: + version "3.1.0" + resolved "https://registry.yarnpkg.com/queue/-/queue-3.1.0.tgz#6c49d01f009e2256788789f2bffac6b8b9990585" + integrity sha1-bEnQHwCeIlZ4h4nyv/rGuLmZBYU= + dependencies: + inherits "~2.0.0" + read@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" @@ -2027,6 +2723,19 @@ read@^1.0.7: dependencies: mute-stream "~0.0.4" +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + 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" + readable-stream@^2.1.5: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -2071,6 +2780,23 @@ readable-stream@~2.0.0: string_decoder "~0.10.x" util-deprecate "~1.0.1" +remove-bom-buffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" + integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== + dependencies: + is-buffer "^1.1.5" + is-utf8 "^0.2.1" + +remove-bom-stream@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" + integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= + dependencies: + remove-bom-buffer "^3.0.0" + safe-buffer "^5.1.0" + through2 "^2.0.3" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -2081,6 +2807,11 @@ replace-ext@0.0.1: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + request@2.81.0, request@~2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" @@ -2137,6 +2868,49 @@ request@^2.85.0: tunnel-agent "^0.6.0" uuid "^3.1.0" +request@^2.86.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + 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.3" + 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.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-options@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" + integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= + dependencies: + value-or-function "^3.0.0" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -2157,6 +2931,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@^5.1.2: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" @@ -2167,16 +2946,16 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - sax@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.2.tgz#735ffaa39a1cff8ffb9598f0223abdb03a9fb2ea" integrity sha1-c1/6o5oc/4/7lZjwIjq9sDqfsuo= +sax@0.5.x: + version "0.5.8" + resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" + integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= + sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -2202,6 +2981,11 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -2277,6 +3061,13 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -2297,6 +3088,55 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M= + dependencies: + any-promise "^1.1.0" + +streamifier@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f" + integrity sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8= + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46" + integrity sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +string.prototype.trimstart@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz#22d45da81015309cd0cdd79787e8919fc5c613e7" + integrity sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + string_decoder@^1.1.1, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -2321,6 +3161,13 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-bom@2.X: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -2356,6 +3203,14 @@ terser@4.3.8: source-map "~0.6.1" source-map-support "~0.5.12" +through2-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" + integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + through2@2.X, through2@^2.0.0, through2@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" @@ -2364,6 +3219,19 @@ through2@2.X, through2@^2.0.0, through2@^2.0.3: readable-stream "^2.1.5" xtend "~4.0.1" +through2@~2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@2, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" @@ -2376,6 +3244,14 @@ tmp@0.0.29: dependencies: os-tmpdir "~1.0.1" +to-absolute-glob@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" + integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= + dependencies: + is-absolute "^1.0.0" + is-negated-glob "^1.0.0" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2383,6 +3259,13 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +to-through@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" + integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= + dependencies: + through2 "^2.0.3" + tough-cookie@~2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" @@ -2397,6 +3280,14 @@ tough-cookie@~2.3.3: dependencies: punycode "^1.4.1" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + ts-morph@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-3.1.3.tgz#bbfa1d14481ee23bdd1c030340ccf4a243cfc844" @@ -2453,16 +3344,16 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -"typescript@ 3.8.0-beta": - version "3.8.0-beta" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-beta.tgz#acdcaf9f24c7e20b1ff0a6329d1e8d63691e2e13" - integrity sha512-mQEmQUJg0CQBhf/GSVnGscKv/jrKsrLxE01AhdjYmBNoXX2Iah3i38ufxXByXacK6Fc5Nr9oMz7MjpjgddiknA== - typescript@^3.0.1: version "3.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== +typescript@^4.1.0-dev.20201018: + version "4.1.0-dev.20201018" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20201018.tgz#1a4b8e3f9b640218a44299773371354d75bcfa34" + integrity sha512-cOFYP1I+IrMWa6ZfefxcacZha1pQMxrq8DGMBLkvrl8k3CqIdD8APq9LXaMj/PWrB8IPgDprY6jHwqiHg0/oGA== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" @@ -2496,11 +3387,26 @@ underscore@^1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== +unique-stream@^2.0.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" + integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== + dependencies: + json-stable-stringify-without-jsonify "^1.0.1" + through2-filter "^3.0.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -2536,6 +3442,16 @@ validator@~3.35.0: resolved "https://registry.yarnpkg.com/validator/-/validator-3.35.0.tgz#3f07249402c1fc8fc093c32c6e43d72a79cca1dc" integrity sha1-PwcklALB/I/Ak8MsbkPXKnnModw= +validator@~9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" + integrity sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA== + +value-or-function@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" + integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -2545,6 +3461,42 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vinyl-fs@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" + integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== + dependencies: + fs-mkdirp-stream "^1.0.0" + glob-stream "^6.1.0" + graceful-fs "^4.0.0" + is-valid-glob "^1.0.0" + lazystream "^1.0.0" + lead "^1.0.0" + object.assign "^4.0.4" + pumpify "^1.3.5" + readable-stream "^2.3.3" + remove-bom-buffer "^3.0.0" + remove-bom-stream "^1.2.0" + resolve-options "^1.1.0" + through2 "^2.0.0" + to-through "^2.0.0" + value-or-function "^3.0.0" + vinyl "^2.0.0" + vinyl-sourcemap "^1.1.0" + +vinyl-sourcemap@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" + integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= + dependencies: + append-buffer "^1.0.2" + convert-source-map "^1.5.0" + graceful-fs "^4.1.6" + normalize-path "^2.1.1" + now-and-later "^2.0.0" + remove-bom-buffer "^3.0.0" + vinyl "^2.0.0" + vinyl-sourcemaps-apply@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" @@ -2570,6 +3522,18 @@ vinyl@^0.5.0: clone-stats "^0.0.1" replace-ext "0.0.1" +vinyl@^2.0.0, vinyl@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" + integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== + dependencies: + clone "^2.1.1" + clone-buffer "^1.0.0" + clone-stats "^1.0.0" + cloneable-readable "^1.0.0" + remove-trailing-separator "^1.0.1" + replace-ext "^1.0.0" + vsce@1.48.0: version "1.48.0" resolved "https://registry.yarnpkg.com/vsce/-/vsce-1.48.0.tgz#31c1a4c6909c3b8bdc48b3d32cc8c8e94c7113a2" @@ -2593,19 +3557,22 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-ripgrep@^1.5.6: - version "1.5.7" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.7.tgz#acb6b548af488a4bca5d0f1bb5faf761343289ce" - integrity sha512-/Vsz/+k8kTvui0q3O74pif9FK0nKopgFTiGNVvxicZANxtSA8J8gUE9GQ/4dpi7D/2yI/YVORszwVskFbz46hQ== +vscode-ripgrep@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.6.2.tgz#fb912c7465699f10ce0218a6676cc632c77369b4" + integrity sha512-jkZEWnQFcE+QuQFfxQXWcWtDafTmgkp3DjMKawDkajZwgnDlGKpFp15ybKrZNVTi1SLEF/12BzxYSZVVZ2XrkA== + dependencies: + https-proxy-agent "^4.0.0" + proxy-from-env "^1.1.0" -vscode-telemetry-extractor@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.5.4.tgz#bcb0d17667fa1b77715e3a3bf372ade18f846782" - integrity sha512-MN9LNPo0Rc6cy3sIWTAG97PTWkEKdRnP0VeYoS8vjKSNtG9CAsrUxHgFfYoHm2vNK/ijd0a4NzETyVGO2kT6hw== +vscode-telemetry-extractor@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.6.0.tgz#e9d9c1d24863cce8d3d715f0287de3b31eb90c56" + integrity sha512-zSxvkbyAMa1lTRGIHfGg7gW2e9Sey+2zGYD19uNWCsVEfoXAr2NB6uzb0sNHtbZ2SSqxSePmFXzBAavsudT5fw== dependencies: command-line-args "^5.1.1" ts-morph "^3.1.3" - vscode-ripgrep "^1.5.6" + vscode-ripgrep "^1.6.2" vso-node-api@6.1.2-preview: version "6.1.2-preview" @@ -2617,6 +3584,11 @@ vso-node-api@6.1.2-preview: typed-rest-client "^0.9.0" underscore "^1.8.3" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -2636,6 +3608,15 @@ wordwrap@~0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -2648,6 +3629,13 @@ xml2js@0.2.7: dependencies: sax "0.5.2" +xml2js@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.8.tgz#9b81690931631ff09d1957549faf54f4f980b3c2" + integrity sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I= + dependencies: + sax "0.5.x" + xml2js@^0.4.17: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" @@ -2661,16 +3649,61 @@ xmlbuilder@0.4.3: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.3.tgz#c4614ba74e0ad196e609c9272cd9e1ddb28a8a58" integrity sha1-xGFLp04K0ZbmCcknLNnh3bKKilg= +xmlbuilder@^9.0.7: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + xmlbuilder@~9.0.1: version "9.0.4" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" integrity sha1-UZy0ymhtAFqEINNJbz8MruzKWA8= +xmldom@0.1.x: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + +xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.3.0: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yauzl@^2.3.1: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" diff --git a/cglicenses.json b/cglicenses.json index 1e9287cbaf2..56f92bef6c5 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -19,7 +19,7 @@ ] }, { - // Reason: The license at https://github.com/Microsoft/TypeScript/blob/master/LICENSE.txt + // Reason: The license at https://github.com/microsoft/TypeScript/blob/master/LICENSE.txt // does not include a clear Copyright statement. "name": "typescript", "prependLicenseText": [ @@ -81,5 +81,313 @@ "prependLicenseText": [ "Copyright (c) Microsoft Corporation. All rights reserved." ] + }, + { + // Reason: The license at https://github.com/rbuckton/reflect-metadata/blob/master/LICENSE + // does not include a clear Copyright statement (it's in https://github.com/rbuckton/reflect-metadata/blob/master/CopyrightNotice.txt). + "name": "reflect-metadata", + "prependLicenseText": [ + "Copyright (c) Microsoft Corporation. All rights reserved." + ] + }, + { + // Reason: The license at https://github.com/reem/rust-unreachable/blob/master/LICENSE-MIT + // cannot be found by the OSS tool automatically. + "name": "reem/rust-unreachable", + "fullLicenseText": [ + "Copyright (c) 2015 The rust-unreachable Developers", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/reem/rust-void/blob/master/LICENSE-MIT + // cannot be found by the OSS tool automatically. + "name": "reem/rust-void", + "fullLicenseText": [ + "Copyright (c) 2015 The rust-void Developers", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/mrhooray/crc-rs/blob/master/LICENSE-MIT + // cannot be found by the OSS tool automatically. + "name": "mrhooray/crc-rs", + "fullLicenseText": [ + "MIT License", + "", + "Copyright (c) 2017 crc-rs Developers", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/floatdrop/pinkie/blob/master/license + // cannot be found by the OSS tool automatically. + "name": "pinkie", + "fullLicenseText": [ + "The MIT License (MIT)", + "", + "Copyright (c) Vsevolod Strukchinsky (github.com/floatdrop)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE." + ] + }, + { + "name": "big-integer", + "prependLicenseText": [ + "Copyright released to public domain" + ] + }, + { + // Reason: The license at https://github.com/justmoon/node-extend/blob/main/LICENSE + // cannot be found by the OSS tool automatically. + "name": "extend", + "fullLicenseText": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2014 Stefan Thomas", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of this software and associated documentation files (the", + "\"Software\"), to deal in the Software without restriction, including", + "without limitation the rights to use, copy, modify, merge, publish,", + "distribute, sublicense, and/or sell copies of the Software, and to", + "permit persons to whom the Software is furnished to do so, subject to", + "the following conditions:", + "", + "The above copyright notice and this permission notice shall be", + "included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/retep998/winapi-rs/blob/0.3/LICENSE-MIT + // cannot be found by the OSS tool automatically. + "name": "retep998/winapi-rs", + "fullLicenseText": [ + "Copyright (c) 2015-2018 The winapi-rs Developers", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/digitaldesignlabs/es6-promisify/blob/main/LICENSE + // cannot be found by the OSS tool automatically. + "name": "es6-promisify", + "fullLicenseText": [ + "Copyright (c) 2014 Mike Hall / Digital Design Labs", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/zkat/json-parse-better-errors/blob/latest/LICENSE.md + // cannot be found by the OSS tool automatically. + "name": "json-parse-better-errors", + "fullLicenseText": [ + "Copyright 2017 Kat Marchán", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the", + "\"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute,", + "sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following", + "conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE", + "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS", + "OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR", + "OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/time-rs/time/blob/main/LICENSE-MIT + // cannot be found by the OSS tool automatically. + "name": "time-rs/time", + "fullLicenseText": [ + "Copyright (c) 2019 Jacob Pratt", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/colorjs/color-name/blob/master/LICENSE + // cannot be found by the OSS tool automatically. + "name": "color-name", + "fullLicenseText": [ + "The MIT License (MIT)", + "Copyright (c) 2015 Dmitry Ivanov", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The license cannot be found by the tool due to access controls on the repository + "name": "tas-client-umd", + "fullLicenseText": [ + "MIT License", + "Copyright (c) 2020 - present Microsoft Corporation", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/acornjs/acorn/blob/master/acorn/LICENSE + // cannot be found by the OSS tool automatically. + "name": "acorn", + "fullLicenseText": [ + "MIT License", + "Copyright (C) 2012-2018 by various contributors (see AUTHORS)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // Reason: The license at https://github.com/acornjs/acorn/blob/master/acorn-loose/LICENSE + // cannot be found by the OSS tool automatically. + "name": "acorn-loose", + "fullLicenseText": [ + "MIT License", + "Copyright (C) 2012-2018 by various contributors (see AUTHORS)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] } -] +] \ No newline at end of file diff --git a/cgmanifest.json b/cgmanifest.json index e9dfd3d6ef3..bd95a0d80b0 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf" + "commitHash": "894fb9eb56c6cbda65e3c3ae9ada6d4cb5850cc9" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "78.0.3904.130" + "version": "83.0.4103.122" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a" + "commitHash": "9622fed3fb2cffcea9efff6c8cb4cc2def99d75d" } }, "isOnlyProductionDependency": true, - "version": "12.8.1" + "version": "12.14.1" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "bef0dd868b7d6d32716c319664ed480f2ae17396" + "commitHash": "0b5f24002b4f18adee112ed39fe269aa51f6705c" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "7.1.7" + "version": "9.3.3" }, { "component": { @@ -77,6 +77,40 @@ } }, "isOnlyProductionDependency": true, + "licenseDetail": [ + "Inno Setup License", + "==================", + "", + "Except where otherwise noted, all of the documentation and software included in the Inno Setup", + "package is copyrighted by Jordan Russell.", + "", + "Copyright (C) 1997-2020 Jordan Russell. All rights reserved.", + "Portions Copyright (C) 2000-2020 Martijn Laan. All rights reserved.", + "", + "This software is provided \"as-is,\" without any express or implied warranty. In no event shall the", + "author be held liable for any damages arising from the use of this software.", + "", + "Permission is granted to anyone to use this software for any purpose, including commercial", + "applications, and to alter and redistribute it, provided that the following conditions are met:", + "", + "1. All redistributions of source code files must retain all copyright notices that are currently in", + " place, and this list of conditions without modification.", + "", + "2. All redistributions in binary form must retain all occurrences of the above copyright notice and", + " web site addresses that are currently in place (for example, in the About boxes).", + "", + "3. The origin of this software must not be misrepresented; you must not claim that you wrote the", + " original software. If you use this software to distribute a product, an acknowledgment in the", + " product documentation would be appreciated but is not required.", + "", + "4. Modified versions in source or binary form must be plainly marked as such, and must not be", + " misrepresented as being the original software.", + "", + "", + "Jordan Russell", + "jr-2010 AT jrsoftware.org", + "https://jrsoftware.org/" + ], "version": "5.5.6" }, { @@ -98,7 +132,7 @@ "git": { "name": "vscode-codicons", "repositoryUrl": "https://github.com/microsoft/vscode-codicons", - "commitHash": "65d11e0839d0ce09faa1a159dc0b3c0bd1aa50be" + "commitHash": "f0caa623812a8ed5059516277675b4158d4c4867" } }, "license": "MIT and Creative Commons Attribution 4.0", @@ -499,7 +533,7 @@ "git": { "name": "ripgrep", "repositoryUrl": "https://github.com/BurntSushi/ripgrep", - "commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" + "commitHash": "973de50c9ef451da2cfcdfa86f2b2711d8d6ff48" } }, "isOnlyProductionDependency": true, diff --git a/extensions/bat/language-configuration.json b/extensions/bat/language-configuration.json index 17bc92f6a91..e205fb5f5c4 100644 --- a/extensions/bat/language-configuration.json +++ b/extensions/bat/language-configuration.json @@ -1,6 +1,6 @@ { "comments": { - "lineComment": "REM" + "lineComment": "@REM" }, "brackets": [ ["{", "}"], @@ -22,8 +22,8 @@ ], "folding": { "markers": { - "start": "^\\s*(::\\s*|REM\\s+)#region", - "end": "^\\s*(::\\s*|REM\\s+)#endregion" + "start": "^\\s*(::|REM|@REM)\\s*#region", + "end": "^\\s*(::|REM|@REM)\\s*#endregion" } } } diff --git a/extensions/bat/package.json b/extensions/bat/package.json index 00bd84e4ae4..dc543aef402 100644 --- a/extensions/bat/package.json +++ b/extensions/bat/package.json @@ -23,7 +23,7 @@ }], "snippets": [{ "language": "bat", - "path": "./snippets/batchfile.snippets.json" + "path": "./snippets/batchfile.code-snippets" }] } -} \ No newline at end of file +} diff --git a/extensions/bat/snippets/batchfile.snippets.json b/extensions/bat/snippets/batchfile.code-snippets similarity index 100% rename from extensions/bat/snippets/batchfile.snippets.json rename to extensions/bat/snippets/batchfile.code-snippets diff --git a/extensions/bat/test/colorize-results/test_bat.json b/extensions/bat/test/colorize-results/test_bat.json index 97155fafc8b..eb76b0e1d04 100644 --- a/extensions/bat/test/colorize-results/test_bat.json +++ b/extensions/bat/test/colorize-results/test_bat.json @@ -488,7 +488,7 @@ "t": "source.batchfile constant.character.escape.batchfile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -510,7 +510,7 @@ "t": "source.batchfile constant.character.escape.batchfile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" diff --git a/extensions/cgmanifest.json b/extensions/cgmanifest.json index 6c12dba86ad..03cf0cef986 100644 --- a/extensions/cgmanifest.json +++ b/extensions/cgmanifest.json @@ -5,7 +5,7 @@ "type": "git", "git": { "name": "typescript", - "repositoryUrl": "https://github.com/Microsoft/TypeScript", + "repositoryUrl": "https://github.com/microsoft/TypeScript", "commitHash": "54426a14f4c232da8e563d20ca8e71263e1c96b5" } }, diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index df5f8ff7c1f..9bc96cca5d8 100644 --- a/extensions/coffeescript/package.json +++ b/extensions/coffeescript/package.json @@ -28,7 +28,7 @@ ], "snippets": [{ "language": "coffeescript", - "path": "./snippets/coffeescript.snippets.json" + "path": "./snippets/coffeescript.code-snippets" }] } -} \ No newline at end of file +} diff --git a/extensions/coffeescript/snippets/coffeescript.snippets.json b/extensions/coffeescript/snippets/coffeescript.code-snippets similarity index 100% rename from extensions/coffeescript/snippets/coffeescript.snippets.json rename to extensions/coffeescript/snippets/coffeescript.code-snippets diff --git a/extensions/coffeescript/test/colorize-results/test_coffee.json b/extensions/coffeescript/test/colorize-results/test_coffee.json index 7c72176431d..9647307f073 100644 --- a/extensions/coffeescript/test/colorize-results/test_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test_coffee.json @@ -1555,7 +1555,7 @@ "t": "source.coffee string.regexp.multiline.coffee keyword.control.anchor.regexp", "r": { "dark_plus": "keyword.control.anchor.regexp: #DCDCAA", - "light_plus": "keyword.control.anchor.regexp: #FF0000", + "light_plus": "keyword.control.anchor.regexp: #EE0000", "dark_vs": "keyword.control: #569CD6", "light_vs": "keyword.control: #0000FF", "hc_black": "keyword.control: #C586C0" @@ -1605,4 +1605,4 @@ "hc_black": "string.regexp: #D16969" } } -] +] \ No newline at end of file diff --git a/extensions/configuration-editing/.vscodeignore b/extensions/configuration-editing/.vscodeignore index f12c396fd04..de8e6dc5913 100644 --- a/extensions/configuration-editing/.vscodeignore +++ b/extensions/configuration-editing/.vscodeignore @@ -3,4 +3,5 @@ src/** tsconfig.json out/** extension.webpack.config.js +extension-browser.webpack.config.js yarn.lock diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.js b/extensions/configuration-editing/extension-browser.webpack.config.js similarity index 57% rename from src/vs/code/electron-browser/processExplorer/processExplorer.js rename to extensions/configuration-editing/extension-browser.webpack.config.js index d05816f9915..de941625373 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.js +++ b/extensions/configuration-editing/extension-browser.webpack.config.js @@ -4,10 +4,18 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check + 'use strict'; -const bootstrapWindow = require('../../../../bootstrap-window'); +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/configurationEditingMain.ts' + }, + output: { + filename: 'configurationEditingMain.js' + } +}); -bootstrapWindow.load(['vs/code/electron-browser/processExplorer/processExplorerMain'], function (processExplorer, configuration) { - processExplorer.startup(configuration.data); -}, { forceEnableDeveloperKeybindings: true }); \ No newline at end of file diff --git a/extensions/configuration-editing/extension.webpack.config.js b/extensions/configuration-editing/extension.webpack.config.js index b474e65cbb1..1b18dd86a99 100644 --- a/extensions/configuration-editing/extension.webpack.config.js +++ b/extensions/configuration-editing/extension.webpack.config.js @@ -12,7 +12,10 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/extension.ts', + extension: './src/configurationEditingMain.ts', + }, + output: { + filename: 'configurationEditingMain.js' }, resolve: { mainFields: ['module', 'main'] diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index be2d2e8ebae..3afe9fa4332 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -12,14 +12,15 @@ "onLanguage:json", "onLanguage:jsonc" ], - "main": "./out/extension", + "main": "./out/configurationEditingMain", + "browser": "./dist/browser/configurationEditingMain", "scripts": { "compile": "gulp compile-extension:configuration-editing", "watch": "gulp watch-extension:configuration-editing" }, "dependencies": { - "jsonc-parser": "^2.1.1", - "vscode-nls": "^4.0.0" + "jsonc-parser": "^2.2.1", + "vscode-nls": "^4.1.1" }, "contributes": { "languages": [ @@ -53,7 +54,7 @@ "url": "vscode://schemas/keybindings" }, { - "fileMatch": "vscode://defaultsettings/*/*.json", + "fileMatch": "vscode://defaultsettings/*.json", "url": "vscode://schemas/settings/default" }, { @@ -96,6 +97,10 @@ "fileMatch": "%APP_SETTINGS_HOME%/snippets/*.json", "url": "vscode://schemas/snippets" }, + { + "fileMatch": "%APP_SETTINGS_HOME%/sync/snippets/preview/*.json", + "url": "vscode://schemas/snippets" + }, { "fileMatch": "**/*.code-snippets", "url": "vscode://schemas/global-snippets" @@ -112,6 +117,10 @@ "fileMatch": "/.devcontainer.json", "url": "./schemas/devContainer.schema.json" }, + { + "fileMatch": "%APP_SETTINGS_HOME%/globalStorage/ms-vscode-remote.remote-containers/nameConfigs/*.json", + "url": "./schemas/attachContainer.schema.json" + }, { "fileMatch": "%APP_SETTINGS_HOME%/globalStorage/ms-vscode-remote.remote-containers/imageConfigs/*.json", "url": "./schemas/attachContainer.schema.json" diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index 58e59d72ee0..b408c46f42e 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -2,6 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "description": "Configures an attached to container", "allowComments": true, + "allowTrailingCommas": true, "type": "object", "definitions": { "attachContainer": { @@ -20,7 +21,7 @@ }, "settings": { "$ref": "vscode://schemas/settings/machine", - "description": "Machine specific settings that should be copied into the container." + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time." }, "remoteEnv": { "type": "object", @@ -41,8 +42,28 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$", - "errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" } } } diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 292fe629937..4719d53e6db 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -2,6 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "description": "Defines a dev container", "allowComments": true, + "allowTrailingCommas": true, "type": "object", "definitions": { "devContainerCommon": { @@ -16,19 +17,21 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$", - "errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, "settings": { "$ref": "vscode://schemas/settings/machine", - "description": "Machine specific settings that should be copied into the container." + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." }, "forwardPorts": { "type": "array", "description": "Ports that are forwarded from the container to the local machine.", "items": { - "type": "integer" + "type": "integer", + "maximum": 65535, + "minimum": 0 } }, "remoteEnv": { @@ -45,6 +48,16 @@ "type": "string", "description": "The user VS Code Server will be started with. The default is the same user as the container." }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, "postCreateCommand": { "type": [ "string", @@ -55,9 +68,43 @@ "type": "string" } }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, "devPort": { "type": "integer", "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "description": "Codespaces-specific configuration." } } }, @@ -129,21 +176,89 @@ } } }, - "dockerFileContainer": { + "dockerfileContainer": { + "oneOf": [ + { + "type": "object", + "properties": { + "build": { + "type": "object", + "description": "Docker build-related options.", + "allOf": [ + { + "type": "object", + "properties": { + "dockerfile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + } + }, + "required": [ + "dockerfile" + ] + }, + { + "$ref": "#/definitions/buildOptions" + } + ] + } + }, + "required": [ + "build" + ] + }, + { + "allOf": [ + { + "type": "object", + "properties": { + "dockerFile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + } + }, + "required": [ + "dockerFile" + ] + }, + { + "type": "object", + "properties": { + "build": { + "description": "Docker build-related options.", + "$ref": "#/definitions/buildOptions" + } + } + } + ] + } + ] + }, + "buildOptions": { "type": "object", "properties": { - "dockerFile": { + "target": { "type": "string", - "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + "description": "Target stage in a multi-stage build." }, - "context": { - "type": "string", - "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + "args": { + "type": "object", + "additionalProperties": { + "type": [ + "string" + ] + }, + "description": "Build arguments." } - }, - "required": [ - "dockerFile" - ] + } }, "imageContainer": { "type": "object", @@ -183,7 +298,7 @@ }, "workspaceFolder": { "type": "string", - "description": "The path of the workspace folder inside the container." + "description": "The path of the workspace folder inside the container. This is typically the target path of a volume mount in the docker-compose.yml." }, "shutdownAction": { "type": "string", @@ -201,33 +316,42 @@ ] } }, - "allOf": [ + "oneOf": [ { - "oneOf": [ + "allOf": [ { - "allOf": [ + "oneOf": [ { - "oneOf": [ + "allOf": [ { - "$ref": "#/definitions/dockerFileContainer" + "oneOf": [ + { + "$ref": "#/definitions/dockerfileContainer" + }, + { + "$ref": "#/definitions/imageContainer" + } + ] }, { - "$ref": "#/definitions/imageContainer" + "$ref": "#/definitions/nonComposeBase" } ] }, { - "$ref": "#/definitions/nonComposeBase" + "$ref": "#/definitions/composeContainer" } ] }, { - "$ref": "#/definitions/composeContainer" + "$ref": "#/definitions/devContainerCommon" } ] }, { - "$ref": "#/definitions/devContainerCommon" + "type": "object", + "$ref": "#/definitions/devContainerCommon", + "additionalProperties": false } ] } diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/configurationEditingMain.ts similarity index 83% rename from extensions/configuration-editing/src/extension.ts rename to extensions/configuration-editing/src/configurationEditingMain.ts index 644e9bb1192..966073e23f8 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -7,6 +7,7 @@ import { getLocation, parse, visit } from 'jsonc-parser'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { SettingsDocument } from './settingsDocumentHelper'; +import { provideInstalledExtensionProposals } from './extensionsProposals'; const localize = nls.loadMessageBundle(); export function activate(context: vscode.ExtensionContext): void { @@ -80,7 +81,7 @@ function registerExtensionsCompletionsInExtensionsDocument(): vscode.Disposable const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); if (location.path[0] === 'recommendations') { const extensionsContent = parse(document.getText()); - return provideInstalledExtensionProposals(extensionsContent, range); + return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], range, false); } return []; } @@ -94,41 +95,13 @@ function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); if (location.path[0] === 'extensions' && location.path[1] === 'recommendations') { const extensionsContent = parse(document.getText())['extensions']; - return provideInstalledExtensionProposals(extensionsContent, range); + return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], range, false); } return []; } }); } -function provideInstalledExtensionProposals(extensionsContent: IExtensionsContent, range: vscode.Range): vscode.ProviderResult { - const alreadyEnteredExtensions = extensionsContent && extensionsContent.recommendations || []; - if (Array.isArray(alreadyEnteredExtensions)) { - const knownExtensionProposals = vscode.extensions.all.filter(e => - !(e.id.startsWith('vscode.') - || e.id === 'Microsoft.vscode-markdown' - || alreadyEnteredExtensions.indexOf(e.id) > -1)); - if (knownExtensionProposals.length) { - return knownExtensionProposals.map(e => { - const item = new vscode.CompletionItem(e.id); - const insertText = `"${e.id}"`; - item.kind = vscode.CompletionItemKind.Value; - item.insertText = insertText; - item.range = range; - item.filterText = insertText; - return item; - }); - } else { - const example = new vscode.CompletionItem(localize('exampleExtension', "Example")); - example.insertText = '"vscode.csharp"'; - example.kind = vscode.CompletionItemKind.Value; - example.range = range; - return [example]; - } - } - return undefined; -} - vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, { provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult { const result: vscode.SymbolInformation[] = []; diff --git a/extensions/configuration-editing/src/extensionsProposals.ts b/extensions/configuration-editing/src/extensionsProposals.ts new file mode 100644 index 00000000000..60533ae2975 --- /dev/null +++ b/extensions/configuration-editing/src/extensionsProposals.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +const localize = nls.loadMessageBundle(); + + +export function provideInstalledExtensionProposals(existing: string[], range: vscode.Range, includeBuiltinExtensions: boolean): vscode.ProviderResult { + if (Array.isArray(existing)) { + const extensions = includeBuiltinExtensions ? vscode.extensions.all : vscode.extensions.all.filter(e => !(e.id.startsWith('vscode.') || e.id === 'Microsoft.vscode-markdown')); + const knownExtensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1); + if (knownExtensionProposals.length) { + return knownExtensionProposals.map(e => { + const item = new vscode.CompletionItem(e.id); + const insertText = `"${e.id}"`; + item.kind = vscode.CompletionItemKind.Value; + item.insertText = insertText; + item.range = range; + item.filterText = insertText; + return item; + }); + } else { + const example = new vscode.CompletionItem(localize('exampleExtension', "Example")); + example.insertText = '"vscode.csharp"'; + example.kind = vscode.CompletionItemKind.Value; + example.range = range; + return [example]; + } + } + return undefined; +} + diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index 741e4e500a9..5e466c2eb6f 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getLocation, Location } from 'jsonc-parser'; +import { getLocation, Location, parse } from 'jsonc-parser'; import * as nls from 'vscode-nls'; +import { provideInstalledExtensionProposals } from './extensionsProposals'; const localize = nls.loadMessageBundle(); @@ -13,7 +14,7 @@ export class SettingsDocument { constructor(private document: vscode.TextDocument) { } - public provideCompletionItems(position: vscode.Position, _token: vscode.CancellationToken): vscode.ProviderResult { + public provideCompletionItems(position: vscode.Position, _token: vscode.CancellationToken): vscode.ProviderResult { const location = getLocation(this.document.getText(), this.document.offsetAt(position)); const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); @@ -41,6 +42,15 @@ export class SettingsDocument { }); } + // settingsSync.ignoredExtensions + if (location.path[0] === 'settingsSync.ignoredExtensions') { + let ignoredExtensions = []; + try { + ignoredExtensions = parse(this.document.getText())['settingsSync.ignoredExtensions']; + } catch (e) {/* ignore error */ } + return provideInstalledExtensionProposals(ignoredExtensions, range, true); + } + return this.provideLanguageOverridesCompletionItems(location, position); } @@ -86,7 +96,7 @@ export class SettingsDocument { })); } else { // Value - return this.provideLanguageCompletionItems(location, range); + return this.provideLanguageCompletionItemsForLanguageOverrides(location, range); } } @@ -158,6 +168,11 @@ export class SettingsDocument { } private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable { + return vscode.languages.getLanguages() + .then(languages => languages.map(l => this.newSimpleCompletionItem(formatFunc(l), range))); + } + + private provideLanguageCompletionItemsForLanguageOverrides(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable { return vscode.languages.getLanguages().then(languages => { const completionItems = []; const configuration = vscode.workspace.getConfiguration(); @@ -182,7 +197,7 @@ export class SettingsDocument { let text = this.document.getText(range); if (text && text.trim().startsWith('[')) { range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + text.indexOf('[')), range.end); - return this.provideLanguageCompletionItems(location, range, language => `"[${language}]"`); + return this.provideLanguageCompletionItemsForLanguageOverrides(location, range, language => `"[${language}]"`); } range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); @@ -208,8 +223,8 @@ export class SettingsDocument { if (location.path.length === 1 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) { // Suggestion model word matching includes closed sqaure bracket and ending quote // Hence include them in the proposal to replace - let range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); - return this.provideLanguageCompletionItems(location, range, language => `"[${language}]"`); + const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + return this.provideLanguageCompletionItemsForLanguageOverrides(location, range, language => `"[${language}]"`); } return Promise.resolve([]); } diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index d5aafed1189..36aab5fd224 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -7,12 +7,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== -jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index c216c10420b..070312a63e4 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -19,7 +19,7 @@ "git": { "name": "textmate/c.tmbundle", "repositoryUrl": "https://github.com/textmate/c.tmbundle", - "commitHash": "9aa365882274ca52f01722f3dbb169b9539a20ee" + "commitHash": "60daf83b9d45329524f7847a75e9298b3aae5805" } }, "licenseDetail": [ diff --git a/extensions/cpp/package.json b/extensions/cpp/package.json index c3ed8538bd8..1c8a244d285 100644 --- a/extensions/cpp/package.json +++ b/extensions/cpp/package.json @@ -34,7 +34,8 @@ ".c++", ".hpp", ".hh", - ".hxx", + ".hxx", + ".h++", ".h", ".ii", ".ino", @@ -75,11 +76,11 @@ "snippets": [ { "language": "c", - "path": "./snippets/c.json" + "path": "./snippets/c.code-snippets" }, { "language": "cpp", - "path": "./snippets/cpp.json" + "path": "./snippets/cpp.code-snippets" } ] } diff --git a/extensions/cpp/snippets/c.json b/extensions/cpp/snippets/c.code-snippets similarity index 100% rename from extensions/cpp/snippets/c.json rename to extensions/cpp/snippets/c.code-snippets diff --git a/extensions/cpp/snippets/cpp.json b/extensions/cpp/snippets/cpp.code-snippets similarity index 100% rename from extensions/cpp/snippets/cpp.json rename to extensions/cpp/snippets/cpp.code-snippets diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index bb037f684a5..eef07eeb53d 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.tmLanguage.json @@ -3194,4 +3194,4 @@ "name": "punctuation.vararg-ellipses.c" } } -} \ No newline at end of file +} diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index e1ff11c5d9b..4c0e9a582cf 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -16217,4 +16217,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/cpp/syntaxes/cpp.tmLanguage.json b/extensions/cpp/syntaxes/cpp.tmLanguage.json index f013cbb8732..bd4e7ebeba0 100644 --- a/extensions/cpp/syntaxes/cpp.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.tmLanguage.json @@ -16217,4 +16217,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/cpp/syntaxes/platform.tmLanguage.json b/extensions/cpp/syntaxes/platform.tmLanguage.json index 14fb45016c0..0147e6629a2 100644 --- a/extensions/cpp/syntaxes/platform.tmLanguage.json +++ b/extensions/cpp/syntaxes/platform.tmLanguage.json @@ -4,23 +4,131 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/c.tmbundle/commit/9aa365882274ca52f01722f3dbb169b9539a20ee", + "version": "https://github.com/textmate/c.tmbundle/commit/60daf83b9d45329524f7847a75e9298b3aae5805", "name": "Platform", "scopeName": "source.c.platform", - "comment": "This file was generated with clang-C using MacOSX10.12.sdk", + "comment": "This file was generated with clang-C using MacOSX10.15.sdk", "patterns": [ + { + "match": "\\bkAudioUnitSubType_3DMixer\\b", + "name": "invalid.deprecated.10.10.support.constant.c" + }, { "match": "\\bkCF(?:CalendarUnitWeek|Gregorian(?:AllUnits|Units(?:Days|Hours|M(?:inutes|onths)|Seconds|Years)))\\b", "name": "invalid.deprecated.10.10.support.constant.cf.c" }, + { + "match": "\\bLS(?:ApplicationParameters|LaunchFSRefSpec)\\b", + "name": "invalid.deprecated.10.10.support.type.c" + }, { "match": "\\bCFGregorian(?:Date|Units)\\b", "name": "invalid.deprecated.10.10.support.type.cf.c" }, { - "match": "\\bkCFURL(?:CustomIconKey|EffectiveIconKey|LabelColorKey)\\b", + "match": "\\bkLSItem(?:ContentType|Display(?:Kind|Name)|Extension(?:IsHidden)?|File(?:Creator|Type)|IsInvisible|QuarantineProperties|RoleHandlerDisplayName)\\b", + "name": "invalid.deprecated.10.10.support.variable.c" + }, + { + "match": "\\bk(?:AudioUnitProperty_(?:3DMixer(?:AttenuationCurve|Distance(?:Atten|Params)|RenderingFlags)|D(?:istanceAttenuationData|opplerShift)|ReverbPreset)|CT(?:Adobe(?:CNS1CharacterCollection|GB1CharacterCollection|Japan(?:1CharacterCollection|2CharacterCollection)|Korea1CharacterCollection)|CenterTextAlignment|Font(?:A(?:lertHeaderFontType|pplicationFontType)|ControlContentFontType|DefaultOrientation|EmphasizedSystem(?:DetailFontType|FontType)|HorizontalOrientation|LabelFontType|M(?:e(?:nu(?:Item(?:CmdKeyFontType|FontType|MarkFontType)|TitleFontType)|ssageFontType)|ini(?:EmphasizedSystemFontType|SystemFontType))|NoFontType|P(?:aletteFontType|ushButtonFontType)|S(?:mall(?:EmphasizedSystemFontType|SystemFontType|ToolbarFontType)|ystem(?:DetailFontType|FontType))|Tool(?:TipFontType|barFontType)|U(?:serF(?:ixedPitchFontType|ontType)|tilityWindowTitleFontType)|V(?:erticalOrientation|iewsFontType)|WindowTitleFontType)|IdentityMappingCharacterCollection|JustifiedTextAlignment|LeftTextAlignment|NaturalTextAlignment|RightTextAlignment)|LS(?:HandlerOptions(?:Default|IgnoreCreator)|ItemInfo(?:App(?:IsScriptable|Prefers(?:Classic|Native))|ExtensionIsHidden|Is(?:A(?:liasFile|pplication)|C(?:lassicApp|ontainer)|Invisible|NativeApp|P(?:ackage|lainFile)|Symlink|Volume))|Launch(?:InClassic|StartClassic)|Request(?:A(?:ll(?:Flags|Info)|ppTypeFlags)|BasicFlagsOnly|Extension(?:FlagsOnly)?|IconAndKind|TypeCreator)))\\b", + "name": "invalid.deprecated.10.11.support.constant.c" + }, + { + "match": "\\b(?:AUDistanceAttenuationData|LSItemInfoRecord)\\b", + "name": "invalid.deprecated.10.11.support.type.c" + }, + { + "match": "\\bk(?:C(?:F(?:FTPResource(?:Group|Link|Mod(?:Date|e)|Name|Owner|Size|Type)|Stream(?:NetworkServiceTypeVoIP|Property(?:FTP(?:AttemptPersistentConnection|F(?:etchResourceInfo|ileTransferOffset)|P(?:assword|roxy(?:Host|P(?:assword|ort)|User)?)|ResourceSize|Use(?:PassiveMode|rName))|HTTP(?:AttemptPersistentConnection|Final(?:Request|URL)|Proxy(?:Host|Port)?|Re(?:questBytesWrittenCount|sponseHeader)|S(?:Proxy(?:Host|Port)|houldAutoredirect)))))|GImagePropertyExifSubsecTimeOrginal|TCharacterShapeAttributeName)|IOSurfaceIsGlobal|LSSharedFileList(?:Favorite(?:Items|Volumes)|Item(?:BeforeFirst|Hidden|Last)|LoginItemHidden|Recent(?:ApplicationItems|DocumentItems|ItemsMaxAmount|ServerItems)|SessionLoginItems|Volumes(?:ComputerVisible|NetworkVisible))|SecUseNoAuthenticationUI)\\b", + "name": "invalid.deprecated.10.11.support.variable.c" + }, + { + "match": "\\bkLSLaunch(?:HasUntrustedContents|InhibitBGOnly|NoParams)\\b", + "name": "invalid.deprecated.10.12.support.constant.c" + }, + { + "match": "\\bOSSpinLock\\b", + "name": "invalid.deprecated.10.12.support.type.c" + }, + { + "match": "\\bkSC(?:EntNetPPTP|NetworkInterfaceTypePPTP|ValNetInterfaceSubTypePPTP)\\b", + "name": "invalid.deprecated.10.12.support.variable.c" + }, + { + "match": "\\bkCF(?:StreamSocketSecurityLevelSSLv(?:2|3)|URL(?:CustomIconKey|EffectiveIconKey|LabelColorKey))\\b", "name": "invalid.deprecated.10.12.support.variable.cf.c" }, + { + "match": "\\bk(?:C(?:FNetDiagnostic(?:Connection(?:Down|Indeterminate|Up)|Err|NoErr)|TFontManagerAutoActivationPromptUser)|SecAccessControlTouchID(?:Any|CurrentSet))\\b", + "name": "invalid.deprecated.10.13.support.constant.c" + }, + { + "match": "\\bCFNetDiagnosticStatus\\b", + "name": "invalid.deprecated.10.13.support.type.c" + }, + { + "match": "\\bkS(?:CPropNetInterfaceSupportsModemOnHold|SLSessionConfig_(?:3DES_fallback|RC4_fallback|TLSv1_(?:3DES_fallback|RC4_fallback)|default)|ecTrustCertificateTransparencyWhiteList)\\b", + "name": "invalid.deprecated.10.13.support.variable.c" + }, + { + "match": "\\bk(?:CVOpenGL(?:Buffer(?:Height|InternalFormat|MaximumMipmapLevel|PoolM(?:aximumBufferAgeKey|inimumBufferCountKey)|Target|Width)|TextureCacheChromaSamplingMode(?:Automatic|BestPerformance|HighestQuality|Key))|SecAttrAccessibleAlways(?:ThisDeviceOnly)?)\\b", + "name": "invalid.deprecated.10.14.support.variable.c" + }, + { + "match": "\\bk(?:DTLSProtocol1(?:2)?|S(?:SL(?:Aborted|C(?:l(?:ient(?:Cert(?:None|Re(?:jected|quested)|Sent)|Side)|osed)|onnected)|DatagramType|Handshake|Idle|Protocol(?:2|3(?:Only)?|All|Unknown)|S(?:e(?:rverSide|ssionOption(?:Allow(?:Renegotiation|ServerIdentityChange)|BreakOn(?:C(?:ertRequested|lient(?:Auth|Hello))|ServerAuth)|EnableSessionTickets|Fal(?:lback|seStart)|SendOneByteRecord))|treamType))|ecDataAccessEvent(?:Mask)?)|TLSProtocol(?:1(?:1|2|3|Only)?|MaxSupported))\\b", + "name": "invalid.deprecated.10.15.support.constant.c" + }, + { + "match": "\\bk(?:MIDIPropertyNameConfiguration|S(?:CPropNetPPP(?:AuthEAPPlugins|Plugins)|SLSessionConfig_(?:ATSv1(?:_noPFS)?|TLSv1_fallback|anonymous|legacy(?:_DHE)?|standard)))\\b", + "name": "invalid.deprecated.10.15.support.variable.c" + }, + { + "match": "\\bkCGColorSpace(?:DisplayP3_PQ_EOTF|ITUR_2020_PQ_EOTF)\\b", + "name": "invalid.deprecated.10.16.support.variable.quartz.c" + }, + { + "match": "\\bkMIDIProperty(?:FactoryPatchNameFile|UserPatchNameFile)\\b", + "name": "invalid.deprecated.10.2.support.variable.c" + }, + { + "match": "\\bkCFNetServiceFlagIsRegistrationDomain\\b", + "name": "invalid.deprecated.10.4.support.constant.c" + }, + { + "match": "\\bk(?:MDItemFS(?:Exists|Is(?:Readable|Writeable))|S(?:CPropUsersConsoleUser(?:GID|Name|UID)|KLanguageTypes))\\b", + "name": "invalid.deprecated.10.4.support.variable.c" + }, + { + "match": "\\bkMDItemSupportFileType\\b", + "name": "invalid.deprecated.10.5.support.variable.c" + }, + { + "match": "\\b(?:CM(?:2(?:Header|ProfileHandle)|4Header|A(?:daptationMatrixType|ppleProfileHeader)|B(?:itmap(?:C(?:allBack(?:ProcPtr|UPP)|olorSpace))?|ufferLocation)|C(?:MY(?:Color|KColor)|hromaticAdaptation|o(?:lor|ncat(?:CallBack(?:ProcPtr|UPP)|ProfileSet))|urveType)|D(?:at(?:aType|eTime(?:Type)?)|evice(?:I(?:D|nfoPtr)|Profile(?:ArrayPtr|I(?:D|nfo)|Scope)|State)|isplayIDType)|F(?:ixedXY(?:Color|ZColor)|loatBitmap)|GrayColor|H(?:LSColor|SVColor|andleLocation)|I(?:ntentCRDVMSize|terateDevice(?:InfoProcPtr|ProfileProcPtr))|L(?:ab(?:Color|ToLabProcPtr)|u(?:t(?:16Type|8Type)|vColor))|M(?:I(?:nfo|terate(?:ProcPtr|UPP))|akeAndModel(?:Type)?|easurementType|ulti(?:Funct(?:CLUTType|LutB2AType)|LocalizedUniCode(?:EntryRec|Type)|channel(?:5Color|6Color|7Color|8Color)))|Na(?:medColor(?:2(?:EntryType|Type)|Type)?|tiveDisplayInfo(?:Type)?)|P(?:S2CRDVMSizeType|a(?:rametricCurveType|thLocation)|rof(?:Loc|ile(?:Iterate(?:Data|ProcPtr|UPP)|Location|MD5(?:Ptr)?|Ref|SequenceDescType)))|RGBColor|S(?:15Fixed16ArrayType|creening(?:ChannelRec|Type)|ignatureType)|T(?:ag(?:ElemTable|Record)|ext(?:DescriptionType|Type))|U(?:16Fixed16ArrayType|Int(?:16ArrayType|32ArrayType|64ArrayType|8ArrayType)|crBgType|nicodeTextType)|Vi(?:deoCardGamma(?:Formula|T(?:able|ype))?|ewingConditionsType)|WorldRef|XYZType|YxyColor)|NCM(?:ConcatProfileS(?:et|pec)|DeviceProfileInfo))\\b", + "name": "invalid.deprecated.10.6.support.type.c" + }, + { + "match": "\\bkC(?:FStream(?:PropertySSLPeerCertificates|SSLAllows(?:AnyRoot|Expired(?:Certificates|Roots)))|VImageBufferTransferFunction_(?:EBU_3213|SMPTE_C))\\b", + "name": "invalid.deprecated.10.6.support.variable.c" + }, + { + "match": "\\b(?:AudioFileFDFTable(?:Extended)?|C(?:E_(?:A(?:ccessDescription|uthority(?:InfoAccess|KeyID))|BasicConstraints|C(?:RLDist(?:PointsSyntax|ributionPoint)|ertPolicies|rl(?:Dist(?:ReasonFlags|ributionPointNameType)|Reason))|D(?:ata(?:AndType)?|istributionPointName)|General(?:Name(?:s)?|Subtree(?:s)?)|I(?:nhibitAnyPolicy|ssuingDistributionPoint)|KeyUsage|N(?:ame(?:Constraints|RegistrationAuthorities)|etscapeCertType)|OtherName|Policy(?:Constraints|Information|Mapping(?:s)?|QualifierInfo)|QC_Statement(?:s)?|S(?:emanticsInformation|ubjectKeyID))|SSM_(?:A(?:C(?:CESS_CREDENTIALS(?:_PTR)?|L_(?:E(?:DIT(?:_PTR)?|NTRY_(?:IN(?:FO(?:_PTR)?|PUT(?:_PTR)?)|PROTOTYPE(?:_PTR)?))|OWNER_PROTOTYPE(?:_PTR)?|SUBJECT_CALLBACK|VALIDITY_PERIOD(?:_PTR)?))|PI_M(?:EMORY_FUNCS(?:_PTR)?|oduleEventHandler)|UTHORIZATIONGROUP(?:_PTR)?)|BASE_CERTS(?:_PTR)?|C(?:ALLBACK|ERT(?:GROUP(?:_PTR)?|_(?:BUNDLE(?:_(?:HEADER(?:_PTR)?|PTR))?|PAIR(?:_PTR)?))|HALLENGE_CALLBACK|ONTEXT(?:_(?:ATTRIBUTE(?:_PTR)?|PTR))?|R(?:L(?:GROUP(?:_PTR)?|_PAIR(?:_PTR)?)|YPTO_DATA(?:_PTR)?)|SP_OPERATIONAL_STATISTICS(?:_PTR)?)|D(?:AT(?:A_PTR|E(?:_PTR)?)|B(?:INFO(?:_PTR)?|_(?:ATTRIBUTE_(?:DATA(?:_PTR)?|INFO(?:_PTR)?)|INDEX_INFO(?:_PTR)?|PARSING_MODULE_INFO(?:_PTR)?|RECORD_(?:ATTRIBUTE_(?:DATA(?:_PTR)?|INFO(?:_PTR)?)|INDEX_INFO(?:_PTR)?)|SCHEMA_(?:ATTRIBUTE_INFO(?:_PTR)?|INDEX_INFO(?:_PTR)?)|UNIQUE_RECORD(?:_PTR)?))|L_DB_(?:HANDLE(?:_PTR)?|LIST(?:_PTR)?))|E(?:NCODED_C(?:ERT(?:_PTR)?|RL(?:_PTR)?)|VIDENCE(?:_PTR)?)|F(?:IELD(?:GROUP(?:_PTR)?|_PTR)?|UNC_NAME_ADDR(?:_PTR)?)|GUID(?:_PTR)?|K(?:E(?:A_DERIVE_PARAMS(?:_PTR)?|Y(?:HEADER(?:_PTR)?|_(?:PTR|SIZE(?:_PTR)?))?)|R(?:SUBSERVICE(?:_PTR)?|_(?:NAME|P(?:OLICY_(?:INFO(?:_PTR)?|LIST_ITEM(?:_PTR)?)|ROFILE(?:_PTR)?)|WRAPPEDPRODUCT_INFO(?:_PTR)?)))|LIST(?:_(?:ELEMENT|PTR))?|M(?:ANAGER_(?:EVENT_NOTIFICATION(?:_PTR)?|REGISTRATION_INFO(?:_PTR)?)|EMORY_FUNCS(?:_PTR)?|ODULE_FUNCS(?:_PTR)?)|N(?:AME_LIST(?:_PTR)?|ET_ADDRESS(?:_PTR)?)|OID_PTR|P(?:ARSED_C(?:ERT(?:_PTR)?|RL(?:_PTR)?)|KCS(?:1_OAEP_PARAMS(?:_PTR)?|5_PBKDF(?:1_PARAMS(?:_PTR)?|2_PARAMS(?:_PTR)?)))|QUERY(?:_(?:LIMITS(?:_PTR)?|PTR|SIZE_DATA(?:_PTR)?))?|R(?:ANGE(?:_PTR)?|ESOURCE_CONTROL_CONTEXT(?:_PTR)?)|S(?:AMPLE(?:GROUP(?:_PTR)?|_PTR)?|ELECTION_PREDICATE(?:_PTR)?|PI_(?:AC_FUNCS(?:_PTR)?|C(?:L_FUNCS(?:_PTR)?|SP_FUNCS(?:_PTR)?)|DL_FUNCS(?:_PTR)?|KR_FUNCS(?:_PTR)?|ModuleEventHandler|TP_FUNCS(?:_PTR)?)|TATE_FUNCS(?:_PTR)?|UBSERVICE_UID(?:_PTR)?)|T(?:P_(?:A(?:PPLE_EVIDENCE_INFO|UTHORITY_ID(?:_PTR)?)|C(?:ALLERAUTH_CONTEXT(?:_PTR)?|ERT(?:CHANGE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|ISSUE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|NOTARIZE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|RECLAIM_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|VERIFY_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?))|ONFIRM_RESPONSE(?:_PTR)?|RLISSUE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?))|POLICYINFO(?:_PTR)?|RE(?:QUEST_SET(?:_PTR)?|SULT_SET(?:_PTR)?)|VERIF(?:ICATION_RESULTS_CALLBACK|Y_CONTEXT(?:_(?:PTR|RESULT(?:_PTR)?))?))|UPLE(?:GROUP(?:_PTR)?|_PTR)?)|UPCALLS(?:_(?:CALLOC|FREE|MALLOC|PTR|REALLOC))?|VERSION(?:_PTR)?|WRAP_KEY(?:_PTR)?|X509(?:EXT_(?:BASICCONSTRAINTS(?:_PTR)?|P(?:AIR(?:_PTR)?|OLICY(?:INFO(?:_PTR)?|QUALIFIER(?:INFO(?:_PTR)?|S(?:_PTR)?)))|TAGandVALUE(?:_PTR)?)|_(?:ALGORITHM_IDENTIFIER_PTR|EXTENSION(?:S(?:_PTR)?|_PTR)?|NAME(?:_PTR)?|R(?:DN(?:_PTR)?|EVOKED_CERT_(?:ENTRY(?:_PTR)?|LIST(?:_PTR)?))|S(?:IGN(?:ATURE(?:_PTR)?|ED_C(?:ERTIFICATE(?:_PTR)?|RL(?:_PTR)?))|UBJECT_PUBLIC_KEY_INFO_PTR)|T(?:BS_CERT(?:IFICATE(?:_PTR)?|LIST(?:_PTR)?)|IME(?:_PTR)?|YPE_VALUE_PAIR(?:_PTR)?)|VALIDITY(?:_PTR)?))))|MDS_(?:DB_HANDLE|FUNCS(?:_PTR)?)|cssm_(?:ac(?:cess_credentials|l_(?:e(?:dit|ntry_(?:in(?:fo|put)|prototype))|owner_prototype|validity_period))|base_certs|c(?:ert(?:_(?:bundle(?:_header)?|pair)|group)|ontext(?:_attribute)?|r(?:l(?:_pair|group)|ypto_data))|d(?:b(?:_(?:attribute_(?:data|info)|index_info|parsing_module_info|record_(?:attribute_(?:data|info)|index_info)|schema_attribute_info|unique_record)|info)|l_db_list)|e(?:ncoded_c(?:ert|rl)|vidence)|field(?:group)?|k(?:e(?:a_derive_params|y(?:header)?)|r(?:_(?:p(?:olicy_(?:info|list_item)|rofile)|wrappedproductinfo)|subservice))|list_element|m(?:anager_(?:event_notification|registration_info)|odule_funcs)|net_address|pkcs(?:1_oaep_params|5_pbkdf(?:1_params|2_params))|query(?:_limits)?|resource_control_context|s(?:ample(?:group)?|election_predicate|pi_(?:ac_funcs|c(?:l_funcs|sp_funcs)|dl_funcs|kr_funcs|tp_funcs)|tate_funcs|ubservice_uid)|t(?:p_(?:authority_id|c(?:allerauth_context|ert(?:change_(?:input|output)|issue_(?:input|output)|notarize_(?:input|output)|reclaim_(?:input|output)|verify_(?:input|output))|onfirm_response|rlissue_(?:input|output))|policyinfo|request_set|verify_context(?:_result)?)|uplegroup)|upcalls|x509(?:_(?:extension(?:TagAndValue|s)?|name|r(?:dn|evoked_cert_(?:entry|list))|sign(?:ature|ed_c(?:ertificate|rl))|t(?:bs_cert(?:ificate|list)|ime|ype_value_pair))|ext_(?:basicConstraints|p(?:air|olicy(?:Info|Qualifier(?:Info|s))))))|mds_funcs|x509_validity)\\b", + "name": "invalid.deprecated.10.7.support.type.c" + }, + { + "match": "\\b(?:CSSMOID_(?:A(?:D(?:C_CERT_POLICY|_(?:CA_(?:ISSUERS|REPOSITORY)|OCSP|TIME_STAMPING))|NSI_(?:DH_(?:EPHEM(?:_SHA1)?|HYBRID(?:1(?:_SHA1)?|2(?:_SHA1)?|_ONEFLOW)|ONE_FLOW(?:_SHA1)?|PUB_NUMBER|STATIC(?:_SHA1)?)|MQV(?:1(?:_SHA1)?|2(?:_SHA1)?))|PPLE(?:ID_(?:CERT_POLICY|SHARING_CERT_POLICY)|_(?:ASC|CERT_POLICY|E(?:CDSA|KU_(?:CODE_SIGNING(?:_DEV)?|ICHAT_(?:ENCRYPTION|SIGNING)|P(?:ASSBOOK_SIGNING|ROFILE_SIGNING)|QA_PROFILE_SIGNING|RESOURCE_SIGNING|SYSTEM_IDENTITY)|XTENSION(?:_(?:A(?:AI_INTERMEDIATE|DC_(?:APPLE_SIGNING|DEV_SIGNING)|PPLE(?:ID_(?:INTERMEDIATE|SHARING)|_SIGNING))|CODE_SIGNING|DEVELOPER_AUTHENTICATION|ESCROW_SERVICE|I(?:NTERMEDIATE_MARKER|TMS_INTERMEDIATE)|MACAPPSTORE_RECEIPT|P(?:ASSBOOK_SIGNING|ROVISIONING_PROFILE_SIGNING)|S(?:ERVER_AUTHENTICATION|YSINT2_INTERMEDIATE)|WWDR_INTERMEDIATE))?)|FEE(?:D(?:EXP)?|_(?:MD5|SHA1))?|ISIGN|TP_(?:APPLEID_SHARING|C(?:ODE_SIGN(?:ING)?|SR_GEN)|E(?:AP|SCROW_SERVICE)|I(?:CHAT|P_SEC)|LOCAL_CERT_GEN|M(?:ACAPPSTORE_RECEIPT|OBILE_STORE)|P(?:A(?:CKAGE_SIGNING|SSBOOK_SIGNING)|CS_ESCROW_SERVICE|KINIT_(?:CLIENT|SERVER)|RO(?:FILE_SIGNING|VISIONING_PROFILE_SIGNING))|QA_PROFILE_SIGNING|RE(?:SOURCE_SIGN|VOCATION(?:_(?:CRL|OCSP))?)|S(?:MIME|SL|W_UPDATE_SIGNING)|T(?:EST_MOBILE_STORE|IMESTAMPING))|X509_BASIC))|liasedEntryName|uthority(?:InfoAccess|KeyIdentifier|RevocationList))|B(?:asicConstraints|iometricInfo|usinessCategory)|C(?:ACertificate|SSMKeyStruct|ert(?:Issuer|i(?:com(?:EllCurve)?|ficate(?:Policies|RevocationList)))|hallengePassword|lientAuth|o(?:llective(?:FacsimileTelephoneNumber|InternationalISDNNumber|Organization(?:Name|alUnitName)|P(?:hysicalDeliveryOfficeName|ost(?:OfficeBox|al(?:Address|Code)))|St(?:ateProvinceName|reetAddress)|Tele(?:phoneNumber|x(?:Number|TerminalIdentifier)))|mmonName|ntentType|unt(?:erSignature|ryName))|r(?:l(?:DistributionPoints|Number|Reason)|ossCertificatePair))|D(?:ES_CBC|H|NQualifier|OTMAC_CERT(?:_(?:E(?:MAIL_(?:ENCRYPT|SIGN)|XTENSION)|IDENTITY|POLICY|REQ(?:_(?:ARCHIVE_(?:FETCH|LIST|REMOVE|STORE)|EMAIL_(?:ENCRYPT|SIGN)|IDENTITY|SHARED_SERVICES|VALUE_(?:ASYNC|HOSTNAME|IS_PENDING|PASSWORD|RENEW|USERNAME)))?))?|SA(?:_(?:CMS|JDK))?|e(?:ltaCrlIndicator|s(?:cription|tinationIndicator))|istinguishedName|omainComponent)|E(?:CDSA_WithS(?:HA(?:1|2(?:24|56)|384|512)|pecified)|KU_IPSec|TSI_QCS_QC_(?:COMPLIANCE|LIMIT_VALUE|RETENTION|SSCD)|mail(?:Address|Protection)|nhancedSearchGuide|xtended(?:CertificateAttributes|KeyUsage(?:Any)?|UseCodeSigning))|FacsimileTelephoneNumber|G(?:enerationQualifier|ivenName)|Ho(?:ldInstructionCode|useIdentifier)|I(?:n(?:hibitAnyPolicy|itials|ternationalISDNNumber|validityDate)|ssu(?:erAltName|ingDistributionPoint(?:s)?))|K(?:ERBv5_PKINIT_(?:AUTH_DATA|DH_KEY_DATA|KP_(?:CLIENT_AUTH|KDC)|RKEY_DATA)|eyUsage|nowledgeInformation)|LocalityName|M(?:ACAPPSTORE_(?:CERT_POLICY|RECEIPT_CERT_POLICY)|D(?:2(?:WithRSA)?|4(?:WithRSA)?|5(?:WithRSA)?)|OBILE_STORE_SIGNING_POLICY|e(?:mber|ssageDigest)|icrosoftSGC)|N(?:ame(?:Constraints)?|etscape(?:Cert(?:Sequence|Type)|SGC))|O(?:AEP_(?:ID_PSPECIFIED|MGF1)|CSPSigning|ID_QCS_SYNTAX_V(?:1|2)|bjectClass|rganization(?:Name|alUnitName)|wner)|P(?:DA_(?:COUNTRY_(?:CITIZEN|RESIDENCE)|DATE_OF_BIRTH|GENDER|PLACE_OF_BIRTH)|K(?:CS(?:12_(?:c(?:ertBag|rlBag)|keyBag|pbe(?:WithSHAAnd(?:128BitRC(?:2CBC|4)|2Key3DESCBC|3Key3DESCBC|40BitRC4)|withSHAAnd40BitRC2CBC)|s(?:afeContentsBag|ecretBag|hroudedKeyBag))|3|5_(?:D(?:ES_EDE3_CBC|IGEST_ALG)|ENCRYPT_ALG|HMAC_SHA1|PB(?:ES2|KDF2|MAC1)|RC(?:2_CBC|5_CBC)|pbeWith(?:MD(?:2And(?:DES|RC2)|5And(?:DES|RC2))|SHA1And(?:DES|RC2)))|7_(?:D(?:ata(?:WithAttributes)?|igestedData)|En(?:crypted(?:Data|PrivateKeyInfo)|velopedData)|Signed(?:AndEnvelopedData|Data))|9_(?:C(?:ertTypes|rlTypes)|FriendlyName|Id_Ct_TSTInfo|LocalKeyId|SdsiCertificate|TimeStampToken|X509C(?:ertificate|rl)))|IX_OCSP(?:_(?:ARCHIVE_CUTOFF|BASIC|CRL|NO(?:CHECK|NCE)|RESPONSE|SERVICE_LOCATOR))?)|hysicalDeliveryOfficeName|o(?:licy(?:Constraints|Mappings)|st(?:OfficeBox|al(?:Address|Code)))|r(?:e(?:ferredDeliveryMethod|sentationAddress)|ivateKeyUsagePeriod|otocolInformation))|Q(?:C_Statements|T_(?:CPS|UNOTICE))|R(?:SA(?:WithOAEP)?|egisteredAddress|oleOccupant)|S(?:HA(?:1(?:With(?:DSA(?:_(?:CMS|JDK))?|RSA(?:_OIW)?))?|2(?:24(?:WithRSA)?|56(?:WithRSA)?)|384(?:WithRSA)?|512(?:WithRSA)?)|e(?:archGuide|eAlso|r(?:ialNumber|verAuth))|igningTime|t(?:ateProvinceName|reetAddress)|u(?:bject(?:AltName|DirectoryAttributes|EmailAddress|InfoAccess|KeyIdentifier|Picture|SignatureBitmap)|pportedApplicationContext|rname))|T(?:EST_MOBILE_STORE_SIGNING_POLICY|ele(?:phoneNumber|x(?:Number|TerminalIdentifier))|i(?:meStamping|tle))|U(?:n(?:ique(?:Identifier|Member)|structured(?:Address|Name))|se(?:Exemptions|r(?:Certificate|ID|Password)))|X(?:509V(?:1(?:C(?:RL(?:Issuer(?:Name(?:CStruct|LDAP)|Struct)|N(?:extUpdate|umberOfRevokedCertEntries)|Revoked(?:Certificates(?:CStruct|Struct)|Entry(?:CStruct|RevocationDate|S(?:erialNumber|truct)))|ThisUpdate)|ertificate(?:IssuerUniqueId|SubjectUniqueId))|IssuerName(?:CStruct|LDAP|Std)?|S(?:erialNumber|ignature(?:Algorithm(?:Parameters|TBS)?|CStruct|Struct)?|ubject(?:Name(?:CStruct|LDAP|Std)?|PublicKey(?:Algorithm(?:Parameters)?|CStruct)?))|V(?:alidityNot(?:After|Before)|ersion))|2CRL(?:AllExtensions(?:CStruct|Struct)|Extension(?:Critical|Id|Type)|NumberOfExtensions|RevokedEntry(?:AllExtensions(?:CStruct|Struct)|Extension(?:Critical|Id|Type|Value)|NumberOfExtensions|SingleExtension(?:CStruct|Struct))|Si(?:gnedCrl(?:CStruct|Struct)|ngleExtension(?:CStruct|Struct))|TbsCertList(?:CStruct|Struct)|Version)|3(?:Certificate(?:CStruct|Extension(?:C(?:Struct|ritical)|Id|Struct|Type|Value|s(?:CStruct|Struct))|NumberOfExtensions)?|SignedCertificate(?:CStruct)?))|9_62(?:_(?:C_TwoCurve|EllCurve|FieldType|P(?:rimeCurve|ubKeyType)|SigType))?|_121Address)|ecPublicKey|sec(?:p(?:1(?:12r(?:1|2)|28r(?:1|2)|60(?:k1|r(?:1|2))|92(?:k1|r1))|2(?:24(?:k1|r1)|56(?:k1|r1))|384r1|521r1)|t(?:1(?:13r(?:1|2)|31r(?:1|2)|63(?:k1|r(?:1|2))|93r(?:1|2))|2(?:3(?:3(?:k1|r1)|9k1)|83(?:k1|r1))|409(?:k1|r1)|571(?:k1|r1))))|k(?:MDItemLabel(?:I(?:D|con)|Kind|UUID)|SCPropNetSMBNetBIOSScope))\\b", + "name": "invalid.deprecated.10.7.support.variable.c" + }, + { + "match": "\\b(?:false32b|gestaltSystemVersion(?:BugFix|M(?:ajor|inor))?|kCT(?:FontTableOptionExcludeSynthetic|ParagraphStyleSpecifierLineSpacing)|true32b)\\b", + "name": "invalid.deprecated.10.8.support.constant.c" + }, + { + "match": "\\bWSMethodInvocation(?:CallBackProcPtr|DeserializationProcPtr|SerializationProcPtr)\\b", + "name": "invalid.deprecated.10.8.support.type.c" + }, + { + "match": "\\b(?:k(?:CTTypesetterOptionDisableBidiProcessing|FSOperation(?:Bytes(?:CompleteKey|RemainingKey)|Objects(?:CompleteKey|RemainingKey)|T(?:hroughputKey|otal(?:BytesKey|ObjectsKey|UserVisibleObjectsKey))|UserVisibleObjects(?:CompleteKey|RemainingKey))|LSSharedFileListVolumesIDiskVisible|WS(?:Debug(?:Incoming(?:Body|Headers)|Outgoing(?:Body|Headers))|Fault(?:Code|Extra|String)|HTTP(?:ExtraHeaders|FollowsRedirects|Message|Proxy|ResponseMessage|Version)|MethodInvocation(?:Result(?:ParameterName)?|TimeoutValue)|NetworkStreamFaultString|Record(?:NamespaceURI|ParameterOrder|Type)|S(?:OAP(?:1999Protocol|2001Protocol|BodyEncodingStyle|Me(?:ssageHeaders|thodNamespaceURI)|Style(?:Doc|RPC))|treamError(?:Domain|Error|Message))|XMLRPCProtocol))|pi)\\b", + "name": "invalid.deprecated.10.8.support.variable.c" + }, { "match": "\\bkCFURLUbiquitousItemPercent(?:DownloadedKey|UploadedKey)\\b", "name": "invalid.deprecated.10.8.support.variable.cf.c" @@ -29,6 +137,10 @@ "match": "\\bkCGWindowWorkspace\\b", "name": "invalid.deprecated.10.8.support.variable.quartz.c" }, + { + "match": "\\bk(?:AUVoiceIOProperty_VoiceProcessingQuality|Sec(?:AppleSharePasswordItemClass|TrustResultConfirm))\\b", + "name": "invalid.deprecated.10.9.support.constant.c" + }, { "match": "\\bkCFURL(?:BookmarkCreationPreferFileIDResolutionMask|HFSPathStyle|ImproperArgumentsError|PropertyKeyUnavailableError|Re(?:moteHostUnavailableError|source(?:AccessViolationError|NotFoundError))|TimeoutError|Unknown(?:Error|PropertyKeyError|SchemeError))\\b", "name": "invalid.deprecated.10.9.support.constant.cf.c" @@ -38,17 +150,57 @@ "name": "invalid.deprecated.10.9.support.constant.quartz.c" }, { - "match": "\\bCFURLError\\b", - "name": "invalid.deprecated.10.9.support.type.cf.c" + "match": "\\bSecTrustUserSetting\\b", + "name": "invalid.deprecated.10.9.support.type.c" }, { - "match": "\\bCGTextEncoding\\b", - "name": "invalid.deprecated.10.9.support.type.quartz.c" + "match": "\\b(?:XPC_ACTIVITY_REQUIRE_(?:BATTERY_LEVEL|HDD_SPINNING)|k(?:LSSharedFileListGlobalLoginItems|S(?:C(?:PropNetAirPort(?:A(?:llowNetCreation|uthPassword(?:Encryption)?)|JoinMode|P(?:owerEnabled|referredNetwork)|SavePasswords)|ValNetAirPort(?:AuthPasswordEncryptionKeychain|JoinMode(?:Automatic|Preferred|R(?:anked|ecent)|Strongest)))|ecPolicyAppleiChat)))\\b", + "name": "invalid.deprecated.10.9.support.variable.c" }, { "match": "\\bkCFURL(?:File(?:DirectoryContents|Exists|L(?:astModificationTime|ength)|OwnerID|POSIXMode)|HTTPStatus(?:Code|Line)|UbiquitousItemIsDownloadedKey)\\b", "name": "invalid.deprecated.10.9.support.variable.cf.c" }, + { + "match": "\\bk3DMixerParam_(?:GlobalReverbGain|M(?:axGain|inGain)|O(?:bstructionAttenuation|cclusionAttenuation)|ReverbBlend)\\b", + "name": "invalid.deprecated.tba.support.constant.c" + }, + { + "match": "\\bkQL(?:Preview(?:ContentIDScheme|OptionCursorKey|Property(?:Attachment(?:DataKey|sKey)|BaseBundlePathKey|CursorKey|DisplayNameKey|HeightKey|MIMETypeKey|PDFStyleKey|StringEncodingKey|WidthKey))|ThumbnailProperty(?:Ba(?:dgeImageKey|seBundlePathKey)|ExtensionKey))\\b", + "name": "invalid.deprecated.tba.support.variable.c" + }, + { + "match": "\\b(?:QOS_CLASS_(?:BACKGROUND|DEFAULT|U(?:NSPECIFIED|SER_IN(?:ITIATED|TERACTIVE)|TILITY))|k(?:C(?:GLCP(?:AbortOnGPURestartStatusBlacklisted|ContextPriorityRequest|GPURestartStatus|Support(?:GPURestart|SeparateAddressSpace))|TRuby(?:Alignment(?:Auto|Center|Distribute(?:Letter|Space)|End|Invalid|LineEdge|Start)|Overhang(?:Auto|End|Invalid|None|Start)|Position(?:After|Before|Count|In(?:line|terCharacter))))|FSEventStreamEventFlagItemIs(?:Hardlink|LastHardlink)|SecAccessControlUserPresence))\\b", + "name": "support.constant.10.10.c" + }, + { + "match": "\\bk(?:A(?:XValueType(?:AXError|C(?:FRange|G(?:Point|Rect|Size))|Illegal)|udioComponent(?:Flag_(?:CanLoadInProcess|IsV3AudioUnit|RequiresAsyncInstantiation)|Instantiation_Load(?:InProcess|OutOfProcess)))|SecAccessControlDevicePasscode)\\b", + "name": "support.constant.10.11.c" + }, + { + "match": "\\bkSec(?:AccessControl(?:A(?:nd|pplicationPassword)|Or|PrivateKeyUsage)|KeyOperationType(?:Decrypt|Encrypt|KeyExchange|Sign|Verify))\\b", + "name": "support.constant.10.12.c" + }, + { + "match": "\\bk(?:CGLRPRe(?:gistryID(?:High|Low)|movable)|FSEventStream(?:CreateFlagUseExtendedData|EventFlagItemCloned)|SecAccessControlBiometry(?:Any|CurrentSet))\\b", + "name": "support.constant.10.13.c" + }, + { + "match": "\\bk(?:3DMixerParam_(?:BusEnable|DryWetReverbBlend|GlobalReverbGainInDecibels|M(?:axGainInDecibels|inGainInDecibels)|O(?:bstructionAttenuationInDecibels|cclusionAttenuationInDecibels))|AudioQueueProperty_ChannelAssignments|JSTypeSymbol|SecAccessControlWatch)\\b", + "name": "support.constant.10.15.c" + }, + { + "match": "\\bk(?:AudioComponentFlag_SandboxSafe|CGL(?:PFASupportsAutomaticGraphicsSwitching|Renderer(?:ATIRadeonX4000ID|IntelHD5000ID)))\\b", + "name": "support.constant.10.8.c" + }, + { + "match": "\\bk(?:AXPriority(?:High|Low|Medium)|CGL(?:OGLPVersion_GL4_Core|R(?:PMajorGLVersion|endererGeForceID))|FSEventStream(?:CreateFlagMarkSelf|EventFlagOwnEvent))\\b", + "name": "support.constant.10.9.c" + }, + { + "match": "\\b(?:A(?:APNot(?:CreatedErr|FoundErr)|C(?:E(?:2Type|8Type)|L_(?:A(?:DD_(?:FILE|SUBDIRECTORY)|PPEND_DATA)|CHANGE_OWNER|DELETE(?:_CHILD)?|E(?:NTRY_(?:DIRECTORY_INHERIT|FILE_INHERIT|INHERITED|LIMIT_INHERIT|ONLY_INHERIT)|X(?:ECUTE|TENDED_(?:ALLOW|DENY)))|F(?:IRST_ENTRY|LAG_(?:DEFER_INHERIT|NO_INHERIT))|L(?:AST_ENTRY|IST_DIRECTORY)|NEXT_ENTRY|READ_(?:ATTRIBUTES|DATA|EXTATTRIBUTES|SECURITY)|S(?:EARCH|YNCHRONIZE)|TYPE_(?:A(?:CCESS|FS)|CODA|DEFAULT|EXTENDED|N(?:TFS|WFS))|UNDEFINED_TAG|WRITE_(?:ATTRIBUTES|DATA|EXTATTRIBUTES|SECURITY)))|IF(?:C(?:ID|Version1)|FID)|SD(?:Bad(?:ForkErr|HeaderErr)|EntryNotFoundErr)|VAudioSessionError(?:Code(?:BadParam|Cannot(?:InterruptOthers|Start(?:Playing|Recording))|ExpiredSession|I(?:n(?:compatibleCategory|sufficientPriority)|sBusy)|M(?:ediaServicesFailed|issingEntitlement)|None|ResourceNotAvailable|S(?:essionNotActive|iriIsRecording)|Unspecified)|InsufficientPriority)|nnotationID|ppl(?:eShareMediaType|icationSpecificID)|u(?:dioRecordingID|thorID))|C(?:DEFNFnd|E_CDNT_(?:FullName|NameRelativeToCrlIssuer)|S(?:SM(?:ERR_(?:A(?:C_(?:DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:BASE_ACLS|C(?:L_HANDLE|ONTEXT_HANDLE)|D(?:ATA|B_(?:HANDLE|LIST(?:_POINTER)?)|L_HANDLE)|ENCODING|INPUT_POINTER|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|OINTER)|REQUEST(?:OR|_DESCRIPTOR)|T(?:P_HANDLE|UPLE_CREDENTIALS)|VALIDITY_PERIOD)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR)|NO_USER_INTERACTION|OS_ACCESS_DENIED|SE(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|USER_CANCELED)|PPLE(?:DL_(?:DISK_FULL|FILE_TOO_BIG|IN(?:COMPATIBLE_(?:DATABASE_BLOB|KEY_BLOB)|VALID_(?:DATABASE_BLOB|KEY_BLOB|OPEN_PARAMETERS))|QUOTA_EXCEEDED)|TP_(?:BAD_CERT_FROM_ISSUER|C(?:A_PIN_MISMATCH|ERT_NOT_FOUND_FROM_ISSUER|ODE_SIGN_DEVELOPMENT|RL_(?:BAD_URI|EXPIRED|INVALID_ANCHOR_CERT|NOT_(?:FOUND|TRUSTED|VALID_YET)|POLICY_FAIL|SERVER_DOWN)|S_(?:BAD_(?:CERT_CHAIN_LENGTH|PATH_LENGTH)|NO_(?:BASIC_CONSTRAINTS|EXTENDED_KEY_USAGE)))|EXT_KEYUSAGE_NOT_CRITICAL|HOSTNAME_MISMATCH|I(?:D(?:ENTIFIER_MISSING|P_FAIL)|N(?:COMPLETE_REVOCATION_CHECK|VALID_(?:AUTHORITY_ID|CA|E(?:MPTY_SUBJECT|XTENDED_KEY_USAGE)|ID_LINKAGE|KEY_USAGE|ROOT|SUBJECT_ID)))|MISSING_REQUIRED_EXTENSION|N(?:ETWORK_FAILURE|O_BASIC_CONSTRAINTS)|OCSP_(?:BAD_RE(?:QUEST|SPONSE)|INVALID_ANCHOR_CERT|NO(?:NCE_MISMATCH|T_TRUSTED|_SIGNER)|RESP_(?:INTERNAL_ERR|MALFORMED_REQ|SIG_REQUIRED|TRY_LATER|UNAUTHORIZED)|S(?:IG_ERROR|TATUS_UNRECOGNIZED)|UNAVAILABLE)|PATH_LEN_CONSTRAINT|RS_BAD_(?:CERT_CHAIN_LENGTH|EXTENDED_KEY_USAGE)|S(?:MIME_(?:BAD_(?:EXT_KEY_USE|KEY_USE)|EMAIL_ADDRS_NOT_FOUND|KEYUSAGE_NOT_CRITICAL|NO_EMAIL_ADDRS|SUBJ_ALT_NAME_NOT_CRIT)|SL_BAD_EXT_KEY_USE)|TRUST_SETTING_DENY|UNKNOWN_(?:C(?:ERT_EXTEN|R(?:ITICAL_EXTEN|L_EXTEN))|QUAL_CERT_STATEMENT))|_DOTMAC_(?:CSR_VERIFY_FAIL|FAILED_CONSISTENCY_CHECK|NO_REQ_PENDING|REQ_(?:IS_PENDING|QUEUED|REDIRECT|SERVER_(?:A(?:LREADY_EXIST|UTH)|ERR|NOT_AVAIL|PARAM|SERVICE_ERROR|UNIMPL)))))|C(?:L_(?:CRL_ALREADY_SIGNED|DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:BUNDLE_(?:INFO|POINTER)|C(?:ACHE_HANDLE|ERT(?:GROUP_POINTER|_POINTER)|ONTEXT_HANDLE|RL_(?:INDEX|POINTER))|DATA|FIELD_POINTER|INPUT_POINTER|NUMBER_OF_FIELDS|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|OINTER)|RESULTS_HANDLE|SCOPE)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR)|NO_(?:FIELD_VALUES|USER_INTERACTION)|OS_ACCESS_DENIED|S(?:COPE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE))|U(?:NKNOWN_(?:FORMAT|TAG)|SER_CANCELED)|VERIFICATION_FAILURE)|S(?:P(?:DL_APPLE_DL_CONVERSION_ERROR|_(?:A(?:CL_(?:ADD_FAILED|BASE_CERTS_NOT_SUPPORTED|CHA(?:LLENGE_CALLBACK_FAILED|NGE_FAILED)|DELETE_FAILED|ENTRY_TAG_NOT_FOUND|REPLACE_FAILED|SUBJECT_TYPE_NOT_SUPPORTED)|L(?:GID_MISMATCH|READY_LOGGED_IN)|PPLE_(?:ADD_APPLICATION_ACL_SUBJECT|INVALID_KEY_(?:END_DATE|START_DATE)|PUBLIC_KEY_INCOMPLETE|S(?:IGNATURE_MISMATCH|SLv2_ROLLBACK))|TTACH_HANDLE_BUSY)|BLOCK_SIZE_MISMATCH|CRYPTO_DATA_CALLBACK_FAILED|DEVICE_(?:ERROR|FAILED|MEMORY_ERROR|RESET|VERIFY_FAILED)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:PUT_LENGTH_ERROR|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:A(?:C(?:CESS_CREDENTIALS|L_(?:BASE_CERTS|CHALLENGE_CALLBACK|E(?:DIT_MODE|NTRY_TAG)|SUBJECT_VALUE))|LGORITHM|TTR_(?:A(?:CCESS_CREDENTIALS|LG_PARAMS)|B(?:ASE|LOCK_SIZE)|DL_DB_HANDLE|E(?:FFECTIVE_BITS|ND_DATE)|I(?:NIT_VECTOR|TERATION_COUNT)|KEY(?:_(?:LENGTH|TYPE))?|LABEL|MODE|OUTPUT_SIZE|P(?:A(?:DDING|SSPHRASE)|RI(?:ME|VATE_KEY_FORMAT)|UBLIC_KEY_FORMAT)|R(?:ANDOM|OUNDS)|S(?:ALT|EED|TART_DATE|UBPRIME|YMMETRIC_KEY_FORMAT)|VERSION|WRAPPED_KEY_FORMAT))|C(?:ONTEXT(?:_HANDLE)?|RYPTO_DATA)|D(?:ATA(?:_COUNT)?|IGEST_ALGORITHM)|INPUT_(?:POINTER|VECTOR)|KEY(?:ATTR_MASK|USAGE_MASK|_(?:CLASS|FORMAT|LABEL|POINTER|REFERENCE))?|LOGIN_NAME|NEW_ACL_(?:ENTRY|OWNER)|OUTPUT_(?:POINTER|VECTOR)|P(?:ASSTHROUGH_ID|OINTER)|S(?:AMPLE_VALUE|IGNATURE))|_DARK_WAKE)|KEY_(?:BLOB_TYPE_INCORRECT|HEADER_INCONSISTENT|LABEL_ALREADY_EXISTS|USAGE_INCORRECT)|M(?:DS_ERROR|EMORY_ERROR|ISSING_ATTR_(?:A(?:CCESS_CREDENTIALS|LG_PARAMS)|B(?:ASE|LOCK_SIZE)|DL_DB_HANDLE|E(?:FFECTIVE_BITS|ND_DATE)|I(?:NIT_VECTOR|TERATION_COUNT)|KEY(?:_(?:LENGTH|TYPE))?|LABEL|MODE|OUTPUT_SIZE|P(?:A(?:DDING|SSPHRASE)|RI(?:ME|VATE_KEY_FORMAT)|UBLIC_KEY_FORMAT)|R(?:ANDOM|OUNDS)|S(?:ALT|EED|TART_DATE|UBPRIME|YMMETRIC_KEY_FORMAT)|VERSION|WRAPPED_KEY_FORMAT))|NO(?:T_LOGGED_IN|_USER_INTERACTION)|O(?:BJECT_(?:ACL_(?:NOT_SUPPORTED|REQUIRED)|MANIP_AUTH_DENIED|USE_AUTH_DENIED)|PERATION_AUTH_DENIED|S_ACCESS_DENIED|UTPUT_LENGTH_ERROR)|P(?:RIV(?:ATE_KEY_(?:ALREADY_EXISTS|NOT_FOUND)|ILEGE_NOT_(?:GRANTED|SUPPORTED))|UBLIC_KEY_INCONSISTENT)|QUERY_SIZE_UNKNOWN|S(?:AMPLE_VALUE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|TAGED_OPERATION_(?:IN_PROGRESS|NOT_STARTED))|U(?:NSUPPORTED_KEY(?:ATTR_MASK|USAGE_MASK|_(?:FORMAT|LABEL|SIZE))|SER_CANCELED)|VE(?:CTOR_OF_BUFS_UNSUPPORTED|RIFY_FAILED)))|SM_(?:A(?:DDIN_(?:AUTHENTICATE_FAILED|LOAD_FAILED|UNLOAD_FAILED)|TTRIBUTE_NOT_IN_CONTEXT)|BUFFER_TOO_SMALL|DEVICE_(?:FAILED|RESET)|E(?:MM_(?:AUTHENTICATE_FAILED|LOAD_FAILED|UNLOAD_FAILED)|VENT_NOTIFICATION_CALLBACK_NOT_FOUND)|FUNCTION_(?:FAILED|INTEGRITY_FAIL|NOT_IMPLEMENTED)|IN(?:COMPATIBLE_VERSION|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:A(?:DDIN_(?:FUNCTION_TABLE|HANDLE)|TTRIBUTE)|CONTEXT_HANDLE|GUID|HANDLE_USAGE|INPUT_POINTER|KEY_HIERARCHY|OUTPUT_POINTER|P(?:OINTER|VC)|S(?:ERVICE_MASK|UBSERVICEID))|_DARK_WAKE)|LIB_REF_NOT_FOUND|M(?:DS_ERROR|EMORY_ERROR|ODULE_(?:MAN(?:AGER_(?:INITIALIZE_FAIL|NOT_FOUND)|IFEST_VERIFY_FAILED)|NOT_LOADED))|NO(?:T_INITIALIZED|_USER_INTERACTION)|OS_ACCESS_DENIED|P(?:RIVILEGE_NOT_GRANTED|VC_(?:ALREADY_CONFIGURED|REFERENT_NOT_FOUND))|S(?:COPE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE))|USER_CANCELED)))|DL_(?:ACL_(?:ADD_FAILED|BASE_CERTS_NOT_SUPPORTED|CHA(?:LLENGE_CALLBACK_FAILED|NGE_FAILED)|DELETE_FAILED|ENTRY_TAG_NOT_FOUND|REPLACE_FAILED|SUBJECT_TYPE_NOT_SUPPORTED)|D(?:ATA(?:BASE_CORRUPT|STORE_(?:ALREADY_EXISTS|DOESNOT_EXIST|IS_OPEN))|B_LOCKED|EVICE_(?:FAILED|RESET))|ENDOFDATA|F(?:IELD_SPECIFIED_MULTIPLE|UNCTION_(?:FAILED|NOT_IMPLEMENTED))|IN(?:COMPATIBLE_FIELD_FORMAT|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:AC(?:CESS_(?:CREDENTIALS|REQUEST)|L_(?:BASE_CERTS|CHALLENGE_CALLBACK|E(?:DIT_MODE|NTRY_TAG)|SUBJECT_VALUE))|C(?:L_HANDLE|SP_HANDLE)|D(?:B_(?:HANDLE|L(?:IST_POINTER|OCATION)|NAME)|L_HANDLE)|FIELD_NAME|IN(?:DEX_INFO|PUT_POINTER)|MODIFY_MODE|NE(?:TWORK_ADDR|W_(?:ACL_(?:ENTRY|OWNER)|OWNER))|O(?:PEN_PARAMETERS|UTPUT_POINTER)|P(?:A(?:RSING_MODULE|SSTHROUGH_ID)|OINTER)|QUERY|RE(?:CORD(?:TYPE|_(?:INDEX|UID))|SULTS_HANDLE)|S(?:AMPLE_VALUE|ELECTION_TAG)|UNIQUE_INDEX_DATA|VALUE)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR|ISSING_VALUE|ULTIPLE_VALUES_UNSUPPORTED)|NO_USER_INTERACTION|O(?:BJECT_(?:ACL_(?:NOT_SUPPORTED|REQUIRED)|MANIP_AUTH_DENIED|USE_AUTH_DENIED)|PERATION_AUTH_DENIED|S_ACCESS_DENIED)|RECORD_(?:MODIFIED|NOT_FOUND)|S(?:AMPLE_VALUE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|TALE_UNIQUE_RECORD)|U(?:NSUPPORTED_(?:FIELD_FORMAT|INDEX_INFO|LOCALITY|NUM_(?:ATTRIBUTES|INDEXES|RECORDTYPES|SELECTION_PREDS)|OPERATOR|QUERY(?:_LIMITS)?|RECORDTYPE)|SER_CANCELED))|TP_(?:AUTHENTICATION_FAILED|C(?:ERT(?:GROUP_INCOMPLETE|IFICATE_CANT_OPERATE|_(?:EXPIRED|NOT_VALID_YET|REVOKED|SUSPENDED))|RL_ALREADY_SIGNED)|DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:SUFFICIENT_C(?:LIENT_IDENTIFICATION|REDENTIALS)|TERNAL_ERROR|VALID_(?:A(?:CTION(?:_DATA)?|NCHOR_CERT|UTHORITY)|C(?:ALL(?:BACK|ERAUTH_CONTEXT_POINTER)|ERT(?:GROUP(?:_POINTER)?|IFICATE|_(?:AUTHORITY|POINTER))|L_HANDLE|ONTEXT_HANDLE|RL(?:GROUP(?:_POINTER)?|_(?:AUTHORITY|ENCODING|POINTER|TYPE))?|SP_HANDLE)|D(?:ATA|B_(?:HANDLE|LIST(?:_POINTER)?)|L_HANDLE)|F(?:IELD_POINTER|ORM_TYPE)|I(?:D(?:ENTIFIER(?:_POINTER)?)?|N(?:DEX|PUT_POINTER))|KEYCACHE_HANDLE|N(?:AME|ETWORK_ADDR|UMBER_OF_FIELDS)|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|O(?:INTER|LICY_IDENTIFIERS))|RE(?:ASON|QUEST_INPUTS|SPONSE_VECTOR)|S(?:IGNATURE|TOP_ON_POLICY)|T(?:IMESTRING|UPLE(?:GROUP(?:_POINTER)?)?))|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR)|NO(?:T_(?:SIGNER|TRUSTED)|_(?:DEFAULT_AUTHORITY|USER_INTERACTION))|OS_ACCESS_DENIED|RE(?:JECTED_FORM|QUEST_(?:LOST|REJECTED))|SE(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|U(?:N(?:KNOWN_(?:FORMAT|TAG)|SUPPORTED_(?:ADDR_TYPE|SERVICE))|SER_CANCELED)|VERIF(?:ICATION_FAILURE|Y_ACTION_FAILED)))|_(?:A(?:C(?:L_(?:AUTHORIZATION_(?:ANY|CHANGE_(?:ACL|OWNER)|D(?:B(?:S_(?:CREATE|DELETE)|_(?:DELETE|INSERT|MODIFY|READ))|E(?:CRYPT|LETE|RIVE))|E(?:NCRYPT|XPORT_(?:CLEAR|WRAPPED))|GENKEY|I(?:MPORT_(?:CLEAR|WRAPPED)|NTEGRITY)|LOGIN|MAC|P(?:ARTITION_ID|REAUTH_(?:BASE|END))|SIGN|TAG_VENDOR_DEFINED_START)|CODE_SIGNATURE_(?:INVALID|OSX)|EDIT_MODE_(?:ADD|DELETE|REPLACE)|KEYCHAIN_PROMPT_(?:CURRENT_VERSION|INVALID(?:_ACT)?|REQUIRE_PASSPHRASE|UNSIGNED(?:_ACT)?)|MATCH_(?:BITS|GID|HONOR_ROOT|UID)|PR(?:EAUTH_TRACKING_(?:AUTHORIZED|BLOCKED|COUNT_MASK|UNKNOWN)|OCESS_SELECTOR_CURRENT_VERSION)|SUBJECT_TYPE_(?:A(?:NY|SYMMETRIC_KEY)|BIOMETRIC|CO(?:DE_SIGNATURE|MMENT)|EXT_PAM_NAME|HASHED_SUBJECT|KEYCHAIN_PROMPT|LOGIN_NAME|P(?:A(?:RTITION|SSWORD)|R(?:EAUTH(?:_SOURCE)?|O(?:CESS|MPTED_(?:BIOMETRIC|PASSWORD)|TECTED_(?:BIOMETRIC|PASSWORD)))|UBLIC_KEY)|SYMMETRIC_KEY|THRESHOLD))|_(?:BASE_(?:AC_ERROR|ERROR)|PRIVATE_ERROR))|DDR_(?:CUSTOM|N(?:AME|ONE)|SOCKADDR|URL)|LG(?:CLASS_(?:ASYMMETRIC|CUSTOM|D(?:ERIVEKEY|IGEST)|KEYGEN|MAC|NONE|RANDOMGEN|S(?:IGNATURE|YMMETRIC)|UNIQUEGEN)|ID_(?:3DES(?:_(?:1KEY(?:_EEE)?|2KEY(?:_E(?:DE|EE))?|3KEY(?:_E(?:DE|EE))?))?|A(?:ES|PPLE_YARROW|SC)|B(?:ATON|LOWFISH)|C(?:AST(?:3|5)?|DMF|RAB|USTOM|oncat(?:BaseAnd(?:Data|Key)|DataAndBase|KeyAndBase))|D(?:ES(?:Random|X)?|H|SA(?:_BSAFE)?)|E(?:C(?:AES|C|D(?:H(?:_X963_KDF)?|SA(?:_SPECIFIED)?)|ES|MQV|NRA)|NTROPY_DEFAULT|lGamal|xtractFromKey)|F(?:ASTHASH|E(?:AL|E(?:D(?:EXP)?|_(?:MD5|SHA1))?)|IPS186Random|ortezzaTimestamp)|G(?:OST|enericSecret)|HAVAL(?:3|4|5)?|I(?:BCHASH|DEA|ntelPlatformRandom)|JUNIPER|K(?:E(?:A|YCHAIN_KEY)|H(?:AFRE|UFU))|L(?:AST|OKI|UCIFER)|M(?:A(?:DRYGA|YFLY)|D(?:2(?:Random|WithRSA)?|4|5(?:HMAC|Random|WithRSA)?)|MB|QV)|N(?:HASH|ONE|RA)|OPENSSH1|P(?:BE_OPENSSL_MD5|H|KCS(?:12_(?:PBE_(?:ENCR|MAC)|SHA1_PBE)|5_PBKDF(?:1_(?:MD(?:2|5)|SHA1)|2)))|R(?:C(?:2|4|5)|DES|EDOC(?:3)?|IPEM(?:AC|D)|SA|UNNING_COUNTER)|S(?:AFER|E(?:AL|CURE_PASSPHRASE)|HA(?:1(?:HMAC(?:_LEGACY)?|With(?:DSA|EC(?:DSA|NRA)|RSA))?|2(?:24(?:With(?:ECDSA|RSA))?|56(?:With(?:ECDSA|RSA))?)|384(?:With(?:ECDSA|RSA))?|512(?:With(?:ECDSA|RSA))?|Random)|KIPJACK|SL3(?:KeyAndMacDerive|M(?:D5(?:_MAC)?|asterDerive)|PreMasterGen|SHA1(?:_MAC)?))|TIGER|UTC|VENDOR_DEFINED|Wrap(?:Lynks|SET_OAEP)|XORBaseAndData|_FIRST_UNUSED)|MODE_(?:BC|C(?:BC(?:128|64|C|P(?:D|adIV8)|_IV8)?|FB(?:16|32|8|PadIV8|_IV8)?|OUNTER|USTOM)|ECB(?:128|64|96|Pad)?|ISO_9796|LAST|NONE|O(?:AEP_HASH|FB(?:64|NLF|PadIV8|_IV8)?)|P(?:BC|CBC|FB|KCS1_EM(?:E_(?:OAEP|V15)|SA_V15)|RIVATE_(?:KEY|WRAP)|UBLIC_KEY)|RELAYX|SHUFFLE|VENDOR_DEFINED|WRAP|X9_31))|PPLE(?:CSP(?:DL_DB_(?:CHANGE_PASSWORD|GET_(?:HANDLE|SETTINGS)|IS_LOCKED|LOCK|SET_SETTINGS|UNLOCK)|_KEYDIGEST)|DL_OPEN_PARAMETERS_VERSION|FILEDL_(?:COMMIT|DELETE_FILE|MAKE_(?:BACKUP|COPY)|ROLLBACK|T(?:AKE_FILE_LOCK|OGGLE_AUTOCOMMIT))|SCPDL_CSP_GET_KEYHANDLE|X509CL_(?:OBTAIN_CSR|VERIFY_CSR)|_(?:PRIVATE_CSPDL_CODE_(?:1(?:0|1|2|3|4|5|6|7|8|9)|2(?:0|1|2|3|4|5|6|7)|8|9)|UNLOCK_TYPE_(?:KEY(?:BAG|_DIRECT)|WRAPPED_PRIVATE)))|SC_OPTIMIZE_(?:ASCII|DEFAULT|S(?:ECURITY|IZE)|TIME(?:_SIZE)?)|TT(?:ACH_READ_ONLY|RIBUTE_(?:A(?:CCESS_CREDENTIALS|L(?:ERT_TITLE|G_(?:ID|PARAMS))|SC_OPTIMIZATION)|B(?:ASE|LOCK_SIZE)|C(?:SP_HANDLE|USTOM)|D(?:ATA_(?:ACCESS_CREDENTIALS|C(?:RYPTO_DATA|SSM_DATA)|D(?:ATE|L_DB_HANDLE)|K(?:EY|R_PROFILE)|NONE|RANGE|STRING|UINT32|VERSION)|ESCRIPTION|L_DB_HANDLE)|E(?:FFECTIVE_BITS|ND_DATE)|FEE_(?:CURVE_TYPE|PRIME_TYPE)|I(?:NIT_VECTOR|TERATION_COUNT|V_SIZE)|K(?:EY(?:ATTR|USAGE|_(?:LENGTH(?:_RANGE)?|TYPE))?|RPROFILE_(?:LOCAL|REMOTE))|LABEL|MODE|NONE|OUTPUT_SIZE|P(?:A(?:DDING|RAM_KEY|SSPHRASE)|R(?:I(?:ME|VATE_KEY_FORMAT)|OMPT)|UBLIC_KEY(?:_FORMAT)?)|R(?:ANDOM|OUNDS(?:_RANGE)?|SA_BLINDING)|S(?:ALT|EED|TART_DATE|UBPRIME|YMMETRIC_KEY_FORMAT)|TYPE_MASK|VE(?:NDOR_DEFINED|R(?:IFY_PASSPHRASE|SION))|WRAPPED_KEY_FORMAT)))|BASE_ERROR|C(?:ERT(?:GROUP_(?:CERT_PAIR|DATA|ENCODED_CERT|PARSED_CERT)|_(?:ACL_ENTRY|BUNDLE_(?:CUSTOM|ENCODING_(?:BER|CUSTOM|DER|PGP|SEXPR|UNKNOWN)|LAST|P(?:FX|GP_KEYRING|KCS(?:12|7_SIGNED_(?:DATA|ENVELOPED_DATA)))|SPKI_SEQUENCE|UNKNOWN)|ENCODING_(?:BER|CUSTOM|DER|LAST|MULTIPLE|NDR|PGP|SEXPR|UNKNOWN)|Intel|LAST|MULTIPLE|P(?:ARSE_FORMAT_(?:C(?:OMPLEX|USTOM)|LAST|MULTIPLE|NONE|OID_NAMED|SEXPR|TUPLE)|GP)|S(?:DSIv1|PKI|TATUS_(?:EXPIRED|IS_(?:FROM_NET|IN_(?:ANCHORS|INPUT_CERTS)|ROOT)|NOT_VALID_YET|TRUST_SETTINGS_(?:DENY|FOUND_(?:ADMIN|SYSTEM|USER)|IGNORED_ERROR|TRUST)))|TUPLE|UNKNOWN|X(?:9_ATTRIBUTE|_509(?:_ATTRIBUTE|v(?:1|2|3)))))|L_(?:BASE_(?:CL_ERROR|ERROR)|CUSTOM_C(?:ERT_(?:BUNDLE_TYPE|ENCODING|PARSE_FORMAT|TYPE)|RL_PARSE_FORMAT)|PRIVATE_ERROR|TEMPLATE_(?:INTERMEDIATE_CERT|PKIX_CERTTEMPLATE))|ONTEXT_EVENT_(?:CREATE|DELETE|UPDATE)|RL(?:GROUP_(?:CRL_PAIR|DATA|ENCODED_CRL|PARSED_CRL)|_(?:ENCODING_(?:B(?:ER|LOOM)|CUSTOM|DER|MULTIPLE|SEXPR|UNKNOWN)|PARSE_FORMAT_(?:C(?:OMPLEX|USTOM)|LAST|MULTIPLE|NONE|OID_NAMED|SEXPR|TUPLE)|TYPE_(?:MULTIPLE|SPKI|UNKNOWN|X_509v(?:1|2))))|S(?:P_(?:BASE_(?:CSP_ERROR|ERROR)|H(?:ARDWARE|YBRID)|PRIVATE_ERROR|RDR_(?:EXISTS|HW|TOKENPRESENT)|S(?:OFTWARE|TORES_(?:CERTIFICATES|GENERIC|P(?:RIVATE_KEYS|UBLIC_KEYS)|SESSION_KEYS))|TOK_(?:CLOCK_EXISTS|LOGIN_REQUIRED|PR(?:IVATE_KEY_PASSWORD|OT_AUTHENTICATION)|RNG|SESSION_KEY_PASSWORD|USER_PIN_(?:EXPIRED|INITIALIZED)|WRITE_PROTECTED))|SM_(?:BASE_(?:CSSM_ERROR|ERROR)|PRIVATE_ERROR))|USTOM_COMMON_ERROR_EXTENT)|D(?:B_(?:A(?:CCESS_(?:PRIVILEGED|RE(?:AD|SET)|WRITE)|ND|TTRIBUTE_(?:FORMAT_(?:B(?:IG_NUM|LOB)|COMPLEX|MULTI_UINT32|REAL|S(?:INT32|TRING)|TIME_DATE|UINT32)|NAME_AS_(?:INTEGER|OID|STRING)))|C(?:ERT_USE_(?:OWNER|PRIVACY|REVOKED|S(?:IGNING|YSTEM)|TRUSTED)|ONTAINS(?:_(?:FINAL_SUBSTRING|INITIAL_SUBSTRING))?)|DATASTORES_UNKNOWN|EQUAL|FILESYSTEMSCAN_MODE|GREATER_THAN|INDEX_(?:NONUNIQUE|ON_(?:ATTRIBUTE|RECORD|UNKNOWN)|UNIQUE)|LESS_THAN|MODIFY_ATTRIBUTE_(?:ADD|DELETE|NONE|REPLACE)|NO(?:NE|T_EQUAL)|OR|RECORDTYPE_(?:APP_DEFINED_(?:END|START)|OPEN_GROUP_(?:END|START)|SCHEMA_(?:END|START))|TRANSACTIONAL_MODE)|L_(?:BASE_(?:DL_ERROR|ERROR)|CUSTOM|DB_(?:RECORD_(?:A(?:LL_KEYS|NY|PPLESHARE_PASSWORD)|C(?:ERT|RL)|EXTENDED_ATTRIBUTE|GENERIC(?:_PASSWORD)?|INTERNET_PASSWORD|METADATA|P(?:OLICY|RIVATE_KEY|UBLIC_KEY)|SYMMETRIC_KEY|U(?:NLOCK_REFERRAL|SER_TRUST)|X509_C(?:ERTIFICATE|RL))|SCHEMA_(?:ATTRIBUTES|IN(?:DEXES|FO)|PARSING_MODULE))|FFS|LDAP|MEMORY|ODBC|P(?:KCS11|RIVATE_ERROR)|REMOTEDIR|UNKNOWN))|E(?:LAPSED_TIME_(?:COMPLETE|UNKNOWN)|RR(?:CODE_(?:ACL_(?:ADD_FAILED|BASE_CERTS_NOT_SUPPORTED|CHA(?:LLENGE_CALLBACK_FAILED|NGE_FAILED)|DELETE_FAILED|ENTRY_TAG_NOT_FOUND|REPLACE_FAILED|SUBJECT_TYPE_NOT_SUPPORTED)|CRL_ALREADY_SIGNED|DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:COMPATIBLE_VERSION|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:AC(?:CESS_CREDENTIALS|L_(?:BASE_CERTS|CHALLENGE_CALLBACK|E(?:DIT_MODE|NTRY_TAG)|SUBJECT_VALUE)|_HANDLE)|C(?:ERT(?:GROUP_POINTER|_POINTER)|L_HANDLE|ONTEXT_HANDLE|R(?:L_POINTER|YPTO_DATA)|SP_HANDLE)|D(?:ATA|B_(?:HANDLE|LIST(?:_POINTER)?)|L_HANDLE)|FIELD_POINTER|GUID|INPUT_POINTER|KR_HANDLE|N(?:E(?:TWORK_ADDR|W_ACL_(?:ENTRY|OWNER))|UMBER_OF_FIELDS)|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|OINTER)|SAMPLE_VALUE|TP_HANDLE)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR|ODULE_MANIFEST_VERIFY_FAILED)|NO_USER_INTERACTION|O(?:BJECT_(?:ACL_(?:NOT_SUPPORTED|REQUIRED)|MANIP_AUTH_DENIED|USE_AUTH_DENIED)|PERATION_AUTH_DENIED|S_ACCESS_DENIED)|PRIVILEGE_NOT_GRANTED|S(?:AMPLE_VALUE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE))|U(?:NKNOWN_(?:FORMAT|TAG)|SER_CANCELED)|VERIFICATION_FAILURE)|ORCODE_(?:C(?:OMMON_EXTENT|USTOM_OFFSET)|MODULE_EXTENT))|STIMATED_TIME_UNKNOWN|VIDENCE_FORM_(?:APPLE_(?:CERT(?:GROUP|_INFO)|HEADER)|C(?:ERT(?:_ID)?|RL(?:_(?:ID|NEXTTIME|THISTIME))?)|POLICYINFO|TUPLEGROUP|UNSPECIFIC|VERIFIER_TIME))|F(?:ALSE|EE_(?:CURVE_TYPE_(?:ANSI_X9_62|DEFAULT|MONTGOMERY|WEIERSTRASS)|PRIME_TYPE_(?:DEFAULT|FEE|GENERAL|MERSENNE))|IELDVALUE_COMPLEX_DATA_TYPE)|HINT_(?:ADDRESS_(?:APP|SP)|NONE)|INVALID_HANDLE|K(?:EY(?:ATTR_(?:ALWAYS_SENSITIVE|EXTRACTABLE|MODIFIABLE|NEVER_EXTRACTABLE|P(?:ARTIAL|ERMANENT|RIVATE|UBLIC_KEY_ENCRYPT)|RETURN_(?:D(?:ATA|EFAULT)|NONE|REF)|SENSITIVE)|BLOB_(?:OTHER|R(?:AW(?:_FORMAT_(?:BSAFE|CCA|FIPS186|MSCAPI|NONE|O(?:CTET_STRING|PENSS(?:H(?:2)?|L)|THER)|P(?:GP|KCS(?:1|3|8))|SPKI|VENDOR_DEFINED|X509))?|EF(?:ERENCE|_FORMAT_(?:INTEGER|OTHER|S(?:PKI|TRING))))|WRAPPED(?:_FORMAT_(?:APPLE_CUSTOM|MSCAPI|NONE|O(?:PENSS(?:H1|L)|THER)|PKCS(?:7|8)))?)|CLASS_(?:OTHER|P(?:RIVATE_KEY|UBLIC_KEY)|SE(?:CRET_PART|SSION_KEY))|HEADER_VERSION|USE_(?:ANY|DE(?:CRYPT|RIVE)|ENCRYPT|SIGN(?:_RECOVER)?|UNWRAP|VERIFY(?:_RECOVER)?|WRAP)|_HIERARCHY_(?:EXPORT|INTEG|NONE))|R_(?:BASE_ERROR|PRIVATE_ERROR))|LIST_(?:ELEMENT_(?:DATUM|SUBLIST|WORDID)|TYPE_(?:CUSTOM|SEXPR|UNKNOWN))|M(?:DS_(?:BASE_ERROR|PRIVATE_ERROR)|ODULE_STRING_SIZE)|N(?:ET_PROTO_(?:C(?:MP(?:S)?|USTOM)|FTP(?:S)?|LDAP(?:NS|S)?|NONE|OCSP|UNSPECIFIED|X500DAP)|OTIFY_(?:FAULT|INSERT|REMOVE))|OK|P(?:ADDING_(?:A(?:LTERNATE|PPLE_SSLv2)|C(?:IPHERSTEALING|USTOM)|FF|NONE|ONE|PKCS(?:1|5|7)|RANDOM|SIGRAW|VENDOR_DEFINED|ZERO)|KCS(?:5_PBKDF2_PRF_HMAC_SHA1|_OAEP_(?:MGF(?:1_(?:MD5|SHA1)|_NONE)|PSOURCE_(?:NONE|Pspecified)))|RIVILEGE_SCOPE_(?:NONE|PROCESS|THREAD)|VC_(?:APP|NONE|SP))|QUERY_(?:RETURN_DATA|SIZELIMIT_NONE|TIMELIMIT_NONE)|S(?:AMPLE_TYPE_(?:ASYMMETRIC_KEY|BIOMETRIC|COMMENT|HASHED_PASSWORD|KEY(?:BAG_KEY|CHAIN_(?:CHANGE_LOCK|LOCK|PROMPT))|P(?:ASSWORD|R(?:EAUTH|O(?:CESS|MPTED_(?:BIOMETRIC|PASSWORD)|TECTED_(?:BIOMETRIC|PASSWORD))))|RETRY_ID|S(?:IGNED_(?:NONCE|SECRET)|YMMETRIC_KEY)|THRESHOLD)|ERVICE_(?:AC|C(?:L|S(?:P|SM))|DL|KR|TP))|T(?:P_(?:A(?:CTION_(?:ALLOW_EXPIRED(?:_ROOT)?|CRL_SUFFICIENT|DEFAULT|FETCH_C(?:ERT_FROM_NET|RL_FROM_NET)|IMPLICIT_ANCHORS|LEAF_IS_CA|REQUIRE_(?:CRL_(?:IF_PRESENT|PER_CERT)|REV_PER_CERT)|TRUST_SETTINGS)|UTHORITY_REQUEST_C(?:ERT(?:ISSUE|NOTARIZE|RE(?:SUME|VOKE)|SUSPEND|USERECOVER|VERIFY)|RLISSUE))|BASE_(?:ERROR|TP_ERROR)|C(?:ERT(?:CHANGE_(?:HOLD|NO(?:NE|T_AUTHORIZED)|OK(?:WITHNEWTIME)?|RE(?:ASON_(?:AFFILIATIONCHANGE|C(?:ACOMPROMISE|EASEOPERATION)|HOLDRELEASE|KEYCOMPROMISE|SU(?:PERCEDED|SPECTEDCOMPROMISE)|UNKNOWN)|JECTED|LEASE|VOKE)|STATUS_UNKNOWN|WRONGCA)|ISSUE_(?:NOT_AUTHORIZED|OK(?:WITH(?:CERTMODS|SERVICEMODS))?|REJECTED|STATUS_UNKNOWN|WILL_BE_REVOKED)|NOTARIZE_(?:NOT_AUTHORIZED|OK(?:WITH(?:OUTFIELDS|SERVICEMODS))?|REJECTED|STATUS_UNKNOWN)|RECLAIM_(?:NO(?:MATCH|T_AUTHORIZED)|OK|REJECTED|STATUS_UNKNOWN)|VERIFY_(?:EXPIRED|INVALID(?:_(?:AUTHORITY|BASIC_CONSTRAINTS|C(?:ERT(?:GROUP|_VALUE)|RL_DIST_PT)|NAME_TREE|POLICY(?:_IDS)?|SIGNATURE))?|NOT_VALID_YET|REVOKED|SUSPENDED|UNKNOWN(?:_CRITICAL_EXT)?|VALID)|_(?:DIR_UPDATE|NOTIFY_RENEW|PUBLISH))|ONFIRM_(?:ACCEPT|REJECT|STATUS_UNKNOWN)|RL(?:ISSUE_(?:INVALID_DOMAIN|NOT_(?:AUTHORIZED|CURRENT)|OK|REJECTED|STATUS_UNKNOWN|UNKNOWN_IDENTIFIER)|_DISTRIBUTE))|FORM_TYPE_(?:GENERIC|REGISTRATION)|KEY_ARCHIVE|PRIVATE_ERROR|STOP_ON_(?:FIRST_(?:FAIL|PASS)|NONE|POLICY))|RUE)|USEE_(?:AUTHENTICATION|DOMESTIC|FINANCIAL|INSURANCE|K(?:EYEXCH|R(?:ENT|LE))|LAST|MEDICAL|NONE|SSL|WEAK)|VALUE_NOT_AVAILABLE|WORDID_(?:A(?:CL|LPHA|SYMMETRIC_KEY)?|B(?:ER|I(?:NARY|OMETRIC))?|C(?:ANCELED|ERT|OMMENT|RL|USTOM)?|D(?:ATE|B(?:S_(?:CREATE|DELETE)|_(?:DELETE|EXEC_STORED_QUERY|INSERT|MODIFY|READ))|E(?:CRYPT|L(?:ETE|TA_CRL)|R(?:IVE)?)|ISPLAY|O|SA(?:_SHA1)?)?|E(?:LGAMAL|N(?:CRYPT|TRY)|XPORT_(?:CLEAR|WRAPPED))?|G(?:E(?:NKEY)?)?|HA(?:SH(?:ED_(?:PASSWORD|SUBJECT))?|VAL)|I(?:BCHASH|MPORT_(?:CLEAR|WRAPPED)|NTEL|SSUER(?:_INFO)?)|K(?:E(?:A|Y(?:BAG_KEY|CHAIN_(?:CHANGE_LOCK|LOCK|PROMPT)|HOLDER)?)|_OF_N)|L(?:E|OGIN(?:_NAME)?)?|M(?:AC|D(?:2(?:WITHRSA)?|4|5(?:WITHRSA)?))|N(?:AME|DR|HASH|OT_(?:AFTER|BEFORE)|U(?:LL|MERIC))?|O(?:BJECT_HASH|N(?:E_TIME|LINE)|WNER)|P(?:A(?:M_NAME|RTITION|SSWORD)|GP|IN|R(?:E(?:AUTH(?:_SOURCE)?|FIX)|IVATE_KEY|O(?:CESS|MPTED_(?:BIOMETRIC|PASSWORD)|PAGATE|TECTED_(?:BIOMETRIC|P(?:ASSWORD|IN))))|UBLIC_KEY(?:_FROM_CERT)?)?|Q|R(?:ANGE|EVAL|IPEM(?:AC|D(?:160)?)|SA(?:_(?:ISO9796|PKCS(?:1(?:_(?:MD5|S(?:HA1|IG)))?|_(?:MD5|SHA1))?|RAW))?)|S(?:DSIV1|E(?:QUENCE|T|XPR)|HA1(?:WITH(?:DSA|ECDSA|RSA))?|IGN(?:ATURE|ED_(?:NONCE|SECRET))?|PKI|UBJECT(?:_INFO)?|Y(?:MMETRIC_KEY|STEM))|T(?:AG|HRESHOLD|IME)|URI|VE(?:NDOR_(?:END|START)|RSION)|X(?:509(?:V(?:1|2|3)|_ATTRIBUTE)|9_ATTRIBUTE)|_(?:FIRST_UNUSED|NLU_|RESERVED_1|STAR_|UNK_))|X509_DATAFORMAT_(?:ENCODED|PA(?:IR|RSED))))|_MAX_PATH)|antDecompress|o(?:mm(?:entID|onID)|pyrightID))|D(?:RAWHook|T_(?:Authority(?:InfoAccess|KeyID)|BasicConstraints|C(?:ertPolicies|rl(?:DistributionPoints|Number|Reason))|DeltaCrl|ExtendedKeyUsage|I(?:nhibitAnyPolicy|ssu(?:erAltName|ingDistributionPoint))|KeyUsage|N(?:ameConstraints|etscapeCertType)|Other|Policy(?:Constraints|Mappings)|QC_Statements|Subject(?:AltName|KeyID)))|E(?:OLHook|QUALTO|V(?:HIDE|LEVEL|MOVE|NOP|SHOW))|F(?:ORMID|or(?:matVersionID|ward(?:BackwardLooping|Looping)))|G(?:NT_(?:D(?:NSName|irectoryName)|EdiPartyName|IPAddress|OtherName|R(?:FC822Name|egisteredID)|URI|X400Address)|REATERTHAN)|H(?:DActivity|ITTESTHook)|I(?:dleActivity|nstrumentID)|KAEISHandleCGI|L(?:A(?:TENCY_QOS_TIER_(?:0|1|2|3|4|5|UNSPECIFIED)|UNCH_DATA_(?:ARRAY|BOOL|DICTIONARY|ERRNO|FD|INTEGER|MACHPORT|OPAQUE|REAL|STRING))|ESSTHAN)|M(?:ACE(?:3Type|6Type)|IDIDataID|PLibrary_(?:DevelopmentRevision|M(?:ajorVersion|inorVersion)|Release)|arkerID)|N(?:X_(?:BigEndian|L(?:eftButton|ittleEndian)|OneButton|RightButton|UnknownByteOrder)|ameID|etActivity|o(?:Looping|neType))|O(?:S(?:A(?:ControlFlowError|Duplicate(?:Handler|P(?:arameter|roperty))|I(?:llegal(?:A(?:ccess|ssign)|Index|Range)|nconsistentDeclarations)|M(?:essageNotUnderstood|issingParameter)|ParameterMismatch|Syntax(?:Error|TypeError)|TokenTooLong|Undefined(?:Handler|Variable))|_LOG_TYPE_(?:DE(?:BUG|FAULT)|ERROR|FAULT|INFO))|verallAct)|S(?:SL_(?:DH(?:E_(?:DSS_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA))|RSA_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA)))|_(?:DSS_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA))|RSA_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA))|anon_(?:EXPORT_WITH_(?:DES40_CBC_SHA|RC4_40_MD5)|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA|RC4_128_MD5))))|FORTEZZA_DMS_WITH_(?:FORTEZZA_CBC_SHA|NULL_SHA)|N(?:O_SUCH_CIPHERSUITE|ULL_WITH_NULL_NULL)|RSA_(?:EXPORT_WITH_(?:DES40_CBC_SHA|RC(?:2_CBC_40_MD5|4_40_MD5))|WITH_(?:3DES_EDE_CBC_(?:MD5|SHA)|DES_CBC_(?:MD5|SHA)|IDEA_CBC_(?:MD5|SHA)|NULL_(?:MD5|SHA)|RC(?:2_CBC_MD5|4_128_(?:MD5|SHA)))))|lpTypeErr|oundDataID)|T(?:ASK_(?:BACKGROUND_APPLICATION|CONTROL_APPLICATION|D(?:ARWINBG_APPLICATION|EFAULT_APPLICATION)|FOREGROUND_APPLICATION|GRAPHICS_SERVER|INSPECT_BASIC_COUNTS|NONUI_APPLICATION|RENICED|THROTTLE_APPLICATION|UNSPECIFIED)|HROUGHPUT_QOS_TIER_(?:0|1|2|3|4|5|UNSPECIFIED)|LS_(?:AES_(?:128_(?:CCM_(?:8_SHA256|SHA256)|GCM_SHA256)|256_GCM_SHA384)|CHACHA20_POLY1305_SHA256|DH(?:E_(?:DSS_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|PSK_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA(?:256|384)?|RC4_128_SHA)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384))))|_(?:DSS_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|anon_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384))|RC4_128_MD5)))|E(?:CDH(?:E_(?:ECDSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256|NULL_SHA|RC4_128_SHA)|PSK_WITH_AES_(?:128_CBC_SHA|256_CBC_SHA)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256|NULL_SHA|RC4_128_SHA))|_(?:ECDSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA|RC4_128_SHA)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA|RC4_128_SHA)|anon_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_CBC_SHA|256_CBC_SHA)|NULL_SHA|RC4_128_SHA)))|MPTY_RENEGOTIATION_INFO_SCSV)|NULL_WITH_NULL_NULL|PSK_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256|NULL_SHA(?:256|384)?|RC4_128_SHA)|RSA_(?:PSK_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA(?:256|384)?|RC4_128_SHA)|WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384))|NULL_(?:MD5|SHA(?:256)?)|RC4_128_(?:MD5|SHA))))|extWidthHook)|U(?:NORDERED|srActivity)|W(?:DEFNFnd|IDTHHook)|XPC_ACTIVITY_STATE_(?:C(?:HECK_IN|ONTINUE)|D(?:EFER|ONE)|RUN|WAIT)|a(?:b(?:brevDate|ortErr)|c(?:tiv(?:Dev|Mask|ateEvt|eFlag(?:Bit)?)|uteUpr(?:A|I|O|U))|d(?:bAddrMask|d(?:Re(?:fFailed|sFailed)|Size(?:Bit)?))|eBuildSyntax(?:Bad(?:D(?:ata|esc)|EOF|Hex|Negative|Token)|CoercedList|MissingQuote|No(?:C(?:lose(?:Brac(?:e|ket)|Hex|Paren|String)|olon)|E(?:OF|rr)|Key)|OddHex|Uncoerced(?:DoubleAt|Hex))|fp(?:A(?:ccessDenied|lready(?:LoggedInErr|Mounted)|uthContinue)|B(?:ad(?:DirIDType|IDErr|UAM|VersNum)|itmapErr)|C(?:a(?:llNot(?:Allowed|Supported)|nt(?:Mo(?:untMoreSrvre|ve)|Rename)|talogChanged)|ontainsSharedErr)|D(?:enyConflict|i(?:ffVolErr|rNot(?:Empty|Found)|skFull))|EofError|F(?:ileBusy|latVol)|I(?:D(?:Exists|NotFound)|conTypeError|nside(?:SharedErr|TrashErr)|temNotFound)|LockErr|MiscErr|No(?:MoreLocks|Server)|Object(?:Exists|Locked|NotFound|TypeErr)|P(?:armErr|wd(?:ExpiredErr|NeedsChangeErr|PolicyErr|SameErr|TooShortErr))|Range(?:NotLocked|Overlap)|S(?:ame(?:NodeErr|ObjectErr)|e(?:rverGoingDown|ssClosed))|TooManyFilesOpen|UserNotAuth|VolLocked)|l(?:phaLock(?:Bit)?|tDBoxProc)|pp(?:1(?:Evt|Mask)|2(?:Evt|Mask)|3(?:Evt|Mask)|4(?:Evt|Mask)|IsDaemon|M(?:emFullErr|odeErr)|VersionTooOld|e(?:arance(?:Bad(?:BrushIndexErr|CursorIndexErr|TextColorIndexErr)|Process(?:NotRegisteredErr|RegisteredErr)|ThemeHasNoAccents)|ndDITL(?:Bottom|Right))|le(?:Logo|MenuFolderIconResource))|s(?:i(?:AliasName|ParentName|ServerName|VolumeName|ZoneName)|p(?:B(?:adVersNum|ufTooSmall)|No(?:Ack|MoreSess|Servers)|ParamErr|S(?:e(?:rverBusy|ssClosed)|izeErr)|TooMany))|t(?:AbsoluteCenter|Bottom(?:Left|Right)?|Center(?:Bottom|Left|Right|Top)|HorizontalCenter|Left|None|Right|Top(?:Left|Right)?|VerticalCenter|om(?:IndexInvalidErr|sNotOfSameTypeErr)|p(?:BadRsp|LenErr))|u(?:t(?:hFailErr|oKey(?:Mask)?)|xiliaryExportDataUnavailable))|b(?:A(?:ccessCntl|llowCDiDataHandler|ncestorModDateChanges)|DoNotDisplay|Ha(?:ndleAERecording|s(?:B(?:TreeMgr|lankAccessPrivileges)|C(?:atSearch|opyFile)|D(?:esktopMgr|irectIO)|ExtFSVol|F(?:ileIDs|olderLock)|MoveRename|OpenDeny|PersonalAccessPrivileges|ShortName|UserGroupList))|Is(?:AutoMounted|Case(?:Preserving|Sensitive)|Ejectable|On(?:ExternalBus|InternalBus)|Removable)|L(?:2PCanMapFileBlocks|anguageMask|imitFCBs|ocalWList)|No(?:BootBlks|DeskItems|LclSync|MiniFndr|RootTimes|S(?:witchTo|ysDir)|V(?:NEdit|olumeSizes))|ParentModDateChanges|S(?:cript(?:LanguageMask|Mask)|upports(?:2TBFiles|AsyncRequests|Ex(?:clusiveLocks|tendedFileSecurity)|FS(?:CatalogSearch|ExchangeObjects)|HFSPlusAPIs|Journaling|LongNames|MultiScriptNames|NamedForks|S(?:ubtreeIterators|ymbolicLinks)|TrashVolumeCache))|T(?:akeActiveEvent|rshOffLine)|a(?:d(?:ATPSkt|B(?:tSlpErr|uffNum)|C(?:allOrderErr|hannel|ksmErr|o(?:decCharacterizationErr|mponent(?:Instance|Selector|Type)|ntrollerHeight))|D(?:BtSlp|Cksum|ataRefIndex|e(?:lim|pthErr)|ictFormat|rag(?:FlavorErr|ItemErr|RefErr))|E(?:dit(?:Index|List|ionFileErr)|nding|xtResource)|F(?:CBErr|i(?:dErr|leFormat)|o(?:lderDescErr|rmat))|I(?:mage(?:Description|Err|RgnErr)|nputText)|LocNameErr|M(?:DBErr|ovErr)|P(?:asteboard(?:FlavorErr|I(?:ndexErr|temErr)|SyncErr)|ortNameErr|rofileError|ublicMovieAtom)|R(?:eqErr|outingSizeErr)|S(?:GChannel|crapRefErr|e(?:ctionErr|rviceMethodErr)|ubPartErr)|Tra(?:ckIndex|nslation(?:RefErr|SpecErr))|UnitErr)|se(?:DblQuote|SingQuote))|dNamErr|re(?:akRecd|veMark)|t(?:DupRecErr|Key(?:AttrErr|LenErr)|NoSpace|RecNotFnd|n(?:Ctrl|State(?:Bit)?))|uf(?:2SmallErr|TooSmall|fer(?:IsSmall|sTooSmall)))|c(?:A(?:DBAddress|EList|ccessory(?:Process|Suitcase)|ddress(?:Spec)?|lias(?:File|List|OrString)|p(?:pl(?:eTalkAddress|ication(?:File|Process)?)|ril)|rc|ugust)|B(?:o(?:dyColor|olean)|usAddress)|C(?:ell|har|l(?:assIdentifier|ipping(?:File|Window)|osure)|o(?:erc(?:e(?:KataHiragana|LowerCase|OneByteToTwoByte|Remove(?:Diacriticals|Hyphens|Punctuation|WhiteSpace)|SmallKana|UpperCase|Zenkakuhankaku)|ion)|l(?:orTable|umn)|n(?:stant|t(?:ainer(?:Window)?|entSpace|rolPanelFile))))|D(?:TPWindow|e(?:cember|pthErr|sk(?:AccessoryFile|top(?:Printer)?)|v(?:Err|Spec))|isk|ocument(?:File)?|rawingArea|ynamicLibrary)|E(?:n(?:tireContents|umeration)|thernetAddress|ventIdentifier)|F(?:TPItem|ebruary|i(?:le|reWireAddress|xed(?:Point|Rectangle)?)|o(?:lder|nt(?:File|Suitcase))|r(?:ame(?:Color|work)|iday))|Gr(?:aphic(?:Line|Object|Shape|Text)|oup(?:edGraphic)?)|H(?:TML|andle(?:Breakpoint|r))|I(?:PAddress|conFamily|n(?:foWindow|sertion(?:Loc|Point)|t(?:ern(?:alFinderObject|etAddress)|l(?:Text|WritingCode)))|tem)|J(?:anuary|u(?:ly|ne))|Key(?:Form|Identifier|stroke)|L(?:abel|i(?:n(?:e|kedList)|st(?:Element|Or(?:Record|String)|RecordOrString)?)|o(?:calTalkAddress|ng(?:DateTime|Fixed(?:Point|Rectangle)?|Integer|Point|Rectangle)))|M(?:a(?:chine(?:Loc)?|rch|tchErr|y)|enu(?:Item)?|issingValue|on(?:day|th))|N(?:o(?:MemErr|vember)|umber(?:DateTimeOrString|Or(?:DateTime|String))?)|O(?:bject(?:BeingExamined|Specifier)?|ctober|nline(?:Disk|LocalDisk|RemoteDisk)|penableObject|val)|P(?:ICT|a(?:ckage|ragraph)|ixel(?:Map)?|olygon|r(?:e(?:ferences(?:Window)?|position)|o(?:ce(?:dure|ss)|perty|tectErr)))|QD(?:Point|Rectangle)|R(?:GBColor|a(?:ngeErr|wData)|e(?:al|c(?:ord|tangle)|ference|sErr)|o(?:tation|undedRectangle|w)|unningAddress)|S(?:CSIAddress|aturday|cript(?:ingAddition)?|e(?:conds|lection|ptember)|h(?:ar(?:ableContainer|ing(?:Privileges|Window))|ortInteger)|mallReal|ound(?:File)?|pecialFolders|t(?:atusWindow|orage|ring(?:Class)?)|u(?:itcase|nday)|ymbol)|T(?:able|e(?:mpMemErr|xt(?:Color|Flow|Styles)?)|hu(?:mbColor|rsday)|okenRingAddress|rash|uesday|ype)|U(?:RL|SBAddress|ndefined|ser(?:Identifier)?)|Ve(?:ctor|rsion)|W(?:e(?:dnesday|ekday)|indow|ord|ritingCodeInfo)|Zone|a(?:l(?:Arabic(?:Civil|Lunar)|Coptic|Gregorian|J(?:apanese|ewish)|Persian|l(?:NotSupportedByNodeErr|erSecuritySession))|n(?:cel|not(?:BeLeafAtomErr|DeferErr|FindAtomErr|M(?:akeContiguousErr|oveAttachedController)|SetWidthOfAttachedController)|t(?:Create(?:PickerWindow|SingleForkFile)|DoThatInCurrentMode|EnableTrack|FindHandler|GetFlavorErr|LoadP(?:ackage|ick(?:MethodErr|er))|OpenHandler|PutPublicMovieAtom|Re(?:adUtilities|ceiveFromSynthesizerOSErr)|S(?:endToSynthesizerOSErr|tepErr)))|tChangedErr|utionIcon)|bNotFound|dev(?:GenErr|MemErr|ResErr|Unset)|e(?:dilla|nt(?:eredDot|ury))|frag(?:A(?:bortClosureErr|rchitectureErr)|C(?:F(?:M(?:InternalErr|StartupErr)|ragRsrcErr)|losureIDErr|on(?:nectionIDErr|t(?:ainerIDErr|extIDErr)))|DupRegistrationErr|ExecFileRefErr|F(?:i(?:leSizeErr|rst(?:ErrCode|ReservedCode))|ragment(?:CorruptErr|FormatErr|UsageErr))|I(?:mportToo(?:NewErr|OldErr)|nit(?:AtBootErr|FunctionErr|LoopErr|OrderErr))|L(?:astErrCode|ibConnErr)|MapFileErr|No(?:ApplicationErr|ClientMemErr|IDsErr|LibraryErr|P(?:ositionErr|rivateMemErr)|RegistrationErr|S(?:ectionErr|ymbolErr)|tClosureErr)|OutputLengthErr|R(?:eservedCode_(?:1|2|3)|srcForkErr)|StdFolderErr|UnresolvedErr)|h(?:a(?:nnel(?:Busy|NotBusy)|rCodeMask)|eckBoxProc|kCtrl)|ircumflex(?:Upr(?:A|E|I|O|U))?|kSumErr|l(?:earDev|k(?:RdErr|WrErr)|os(?:Err|eDev)|rBit)|m(?:1(?:0CLRData|1CLRData|2CLRData|3CLRData|4CLRData|5CLRData|6_8ColorPacking)|24_8ColorPacking|3(?:2_(?:16ColorPacking|32ColorPacking|8ColorPacking)|CLRData)|4(?:0_8ColorPacking|8_(?:16ColorPacking|8ColorPacking)|CLRData)|5(?:6_8ColorPacking|CLRData)|6(?:4_(?:16ColorPacking|8ColorPacking)|CLRData)|7CLRData|8(?:CLRData|_8ColorPacking)|9CLRData|A(?:RGB(?:32(?:PmulSpace|Space)|64(?:L(?:PmulSpace|Space)|PmulSpace|Space))|ToB(?:0Tag|1Tag|2Tag)|b(?:ortWriteAccess|s(?:oluteColorimetric|tractClass))|lpha(?:FirstPacking|LastPacking|PmulSpace|Space)|sciiData)|B(?:ToA(?:0Tag|1Tag|2Tag)|e(?:ginAccess|stMode)|inaryData|l(?:ackPointCompensation(?:Mask)?|ue(?:ColorantTag|TRCTag))|radfordChromaticAdaptation|ufferBasedProfile)|C(?:M(?:SReservedFlagsMask|Y(?:Data|K(?:32Space|64(?:LSpace|Space)|Data|Space)))|S(?:1(?:C(?:hromTag|ustTag)|NameTag|ProfileVersion|TRCTag)|2ProfileVersion)|a(?:librationDateTimeTag|meraDeviceClass|nt(?:Co(?:ncatenateError|pyModifiedV1Profile)|Delete(?:Element|Profile)|GamutCheckError|XYZ))|h(?:arTargetTag|romaticAdaptationTag)|lose(?:Access|Spool)|o(?:lorSpace(?:AlphaMask|Class|EncodingMask|P(?:ackingMask|remulAlphaMask)|ReservedMask|Space(?:AndAlphaMask|Mask))|pyrightTag)|reateNewAccess|urrent(?:DeviceInfoVersion|Profile(?:InfoVersion|LocationSize|MajorVersion)))|D(?:e(?:fault(?:DeviceID|ProfileID)|vice(?:AlreadyRegistered|DBNotFoundErr|InfoVersion1|M(?:fgDescTag|odelDescTag)|NotRegistered|Profile(?:InfoVersion(?:1|2)|sNotFound)|State(?:AppleRsvdBits|Busy|De(?:fault|viceRsvdBits)|ForceNotify|Offline)))|isplay(?:Class|DeviceClass|Use)|raftMode)|E(?:lementTagNotFound|mbedded(?:Mask|Profile|Use(?:Mask)?)|ndAccess|rrIncompatibleProfile)|F(?:atalProfileErr|lare(?:0|100))|G(?:amut(?:CheckingMask|Result(?:1Space|Space)|Tag)|eometry(?:0(?:45or450|dord0)|Unknown)|lossy(?:MatteMask)?|r(?:ay(?:16(?:LSpace|Space)|8Space|A(?:16(?:PmulSpace|Space)|32(?:L(?:PmulSpace|Space)|PmulSpace|Space)|PmulSpace|Space)|Data|Space|TRCTag)|een(?:ColorantTag|TRCTag)))|H(?:LS(?:32Space|Data|Space)|SV(?:32Space|Data|Space))|I(?:CC(?:ProfileVersion(?:2(?:1)?|4)|ReservedFlagsMask)|lluminant(?:A|D(?:5(?:0|5)|65|93)|EquiPower|F(?:2|8)|Unknown)|n(?:dexRangeErr|put(?:Class|Use)|ter(?:nalCFErr|polationMask)|valid(?:ColorSpace|DstMap|Profile(?:Comment|Location)?|S(?:earch|rcMap)))|terate(?:AllDeviceProfiles|Cu(?:rrentDeviceProfiles|stomDeviceProfiles)|DeviceProfilesMask|FactoryDeviceProfiles))|L(?:AB(?:24Space|32Space|48(?:LSpace|Space)|Space)|UV(?:32Space|Space)|abData|i(?:n(?:e(?:arChromaticAdaptation|sPer)|kClass)|ttleEndianPacking)|ong(?:10ColorPacking|8ColorPacking)|u(?:minanceTag|vData))|M(?:C(?:Eight(?:8Space|Space)|Five(?:8Space|Space)|H(?:5Data|6Data|7Data|8Data)|S(?:even(?:8Space|Space)|ix(?:8Space|Space)))|a(?:cintosh|gicNumber|keAndModelTag)|e(?:asurementTag|dia(?:BlackPointTag|WhitePointTag)|thod(?:Error|NotFound))|icrosoft)|N(?:a(?:med(?:Color(?:2Tag|Class|NotFound|Tag)|Data|Indexed(?:32(?:LSpace|Space)|Space))|tiveDisplayInfoTag)|o(?:C(?:olorPacking|urrentProfile)|GDevicesError|ProfileBase|Space|rmalMode)|umHeaderElements)|O(?:neBitDirectPacking|pen(?:Read(?:Access|Spool)|Write(?:Access|Spool))|riginalProfileLocationSize|utput(?:Class|Use))|P(?:S(?:2(?:C(?:RD(?:0Tag|1Tag|2Tag|3Tag|VMSizeTag)|SATag)|RenderingIntentTag)|7bit|8bit)|a(?:rametricType(?:0|1|2|3|4)|thBasedProfile)|erceptual|r(?:e(?:fsSynchError|view(?:0Tag|1Tag|2Tag))|interDeviceClass|o(?:file(?:Description(?:MLTag|Tag)|Error|IterateDataVersion(?:1|2|3|4)|MajorVersionMask|NotFound|SequenceDescTag|sIdentical)|of(?:DeviceClass|Use))|trDefaultScreens))|QualityMask|R(?:GB(?:16(?:LSpace|Space)|24Space|32Space|48(?:LSpace|Space)|565(?:LSpace|Space)|A(?:32(?:PmulSpace|Space)|64(?:L(?:PmulSpace|Space)|PmulSpace|Space)|PmulSpace|Space)|Data|Space)|angeOverFlow|e(?:ad(?:Access|Spool)|d(?:ColorantTag|TRCTag)|flective(?:TransparentMask)?|lativeColorimetric|servedSpace(?:1|2)|verseChannelPacking))|S(?:RGB(?:16ChannelEncoding|Data)|aturation|c(?:annerDeviceClass|reening(?:DescTag|Tag))|earchError|i(?:g(?:C(?:rdInfoType|urveType)|Dat(?:aType|eTimeType)|Lut(?:16Type|8Type)|M(?:akeAndModelType|easurementType|ulti(?:Funct(?:A2BType|B2AType)|LocalizedUniCodeType))|Na(?:medColor(?:2Type|Type)|tiveDisplayInfoType)|P(?:S2CRDVMSizeType|arametricCurveType|rofile(?:DescriptionType|SequenceDescType))|S(?:15Fixed16Type|creeningType|ignatureType)|TextType|U(?:1(?:6Fixed16Type|Fixed15Type)|Int(?:16Type|32Type|64Type|8Type)|crBgType|nicodeTextType)|Vi(?:deoCardGammaType|ewingConditionsType)|XYZType)|liconGraphics)|olaris|potFunction(?:Cross|D(?:efault|iamond)|Ellipse|Line|Round|Square|Unknown)|tdobs(?:19(?:31TwoDegrees|64TenDegrees)|Unknown))|T(?:aligent|echnology(?:AMDisplay|CRTDisplay|D(?:igitalCamera|yeSublimationPrinter)|Electro(?:photographicPrinter|staticPrinter)|F(?:ilm(?:Scanner|Writer)|lexography)|Gravure|InkJetPrinter|OffsetLithography|P(?:MDisplay|hoto(?:CD|ImageSetter|graphicPaperPrinter)|rojectionTelevision)|ReflectiveScanner|Silkscreen|T(?:ag|hermalWaxPrinter)|Video(?:Camera|Monitor)))|U(?:crBgTag|nsupportedDataType|seDefaultChromaticAdaptation)|V(?:i(?:deoCardGamma(?:FormulaType|Ta(?:bleType|g))|ewingConditions(?:DescTag|Tag))|onKriesChromaticAdaptation)|W(?:ord5(?:65ColorPacking|ColorPacking)|rite(?:Access|Spool))|XYZ(?:24Space|32Space|48(?:LSpace|Space)|Data|Space)|Y(?:CbCrData|XY(?:32Space|Space)|xyData)|apFontTableTag|dKey(?:Bit)?|p(?:Alias(?:NoFlags|OnlyThisFile)|IsMissing|ThreadSafe|WantsRegisterMessage))|o(?:dec(?:AbortErr|BadDataErr|C(?:ant(?:QueueErr|WhenErr)|onditionErr)|D(?:ataVersErr|isabledErr|roppedFrameErr)|E(?:rr|xtensionNotFoundErr)|ImageBufErr|N(?:eed(?:AccessKeyErr|ToFlushChainErr)|o(?:MemoryPleaseWaitErr|thingToBlitErr))|O(?:ffscreenFailed(?:Err|PleaseRetryErr)|penErr)|ParameterDialogConfirm|S(?:creenBufErr|izeErr|poolErr)|UnimpErr|WouldOffscreenErr)|l(?:lection(?:I(?:ndexRangeErr|tem(?:LockedErr|NotFoundErr))|VersionErr)|or(?:SyncNotInstalled|sRequestedErr))|mponent(?:AutoVersionIncludeFlags|D(?:ll(?:EntryNotFoundErr|LoadErr)|o(?:AutoVersion|ntRegister))|HasMultiplePlatforms|LoadResident|Not(?:Captured|ThreadSafeErr)|WantsUnregister)|n(?:nectionInvalid|straintReachedErr|t(?:ainer(?:AlreadyOpenWrn|NotFoundWrn)|rol(?:Err|HandleInvalidErr|InvalidDataVersionErr|Key(?:Bit)?|P(?:anelFolderIconResource|roperty(?:Invalid|NotFoundErr))|ler(?:BoundsNotExact|HasFixedHeight))))|pyDev|r(?:Err|eFoundationUnknownErr)|uld(?:Not(?:ParseSourceFileErr|ResolveDataRef|UseAnExistingSample)|ntGetRequiredComponent))|rash|trlItem|u(?:r(?:NumberPartsVersion|r(?:LeadingZ|NegSym|SymLead|TrailingZ|ent(?:CurLang|DefLang))|sorDev)|tDev))|d(?:BoxProc|InstErr|RemovErr|a(?:t(?:a(?:Already(?:Closed|OpenForWrite)|No(?:DataRef|tOpenFor(?:Read|Write))|VerErr)|e(?:StdMask|Time(?:Invalid|NotFound)))|y(?:Field|LdingZ|Mask|Of(?:Week(?:Field|Mask)|Year(?:Field|Mask))))|blDagger|c(?:eExtErr|m(?:B(?:ad(?:D(?:ataSizeErr|ictionaryErr)|F(?:eatureErr|i(?:eld(?:InfoErr|TypeErr)|ndMethodErr))|KeyErr|PropertyErr)|lockFullErr|ufferOverflowErr)|D(?:ictionary(?:BusyErr|NotOpenErr)|upRecordErr)|IterationCompleteErr|N(?:ecessaryFieldErr|o(?:AccessMethodErr|FieldErr|RecordErr|tDictionaryErr))|P(?:aramErr|ermissionErr|rotectedErr)|TooManyKeyErr))|dp(?:LenErr|SktErr)|e(?:activDev|bugging(?:Duplicate(?:OptionErr|SignatureErr)|ExecutionContextErr|Invalid(?:NameErr|OptionErr|SignatureErr)|No(?:CallbackErr|MatchErr))|fault(?:Component(?:Any(?:Flags(?:AnyManufacturer(?:AnySubType)?)?|Manufacturer|SubType)|Identical)|PhysicalEntryCount)|limPad|s(?:criptorFontTableTag|k(?:PatID|top(?:DamagedErr|IconResource))|tPortErr)|viceCantMeetRequest)|i(?:a(?:eresisUpr(?:E|I|Y)|log(?:Kind|NoTimeoutErr))|ffVolErr|giUnimpErr|r(?:FulErr|NFErr|ectXObjectAlreadyExists)|sk(?:Evt|Mask))|my|o(?:All|Color|F(?:ace|ont)|Size|Toggle|cumentProc|m(?:Cannot|Native|TranslateFirst|Wildcard)|tlessLwrI|ubleAcute)|r(?:agNotAcceptedErr|iver(?:Evt|HardwareGoneErr|Mask)|opFolderIconResource|vQType)|s(?:32BitMode|AddressErr|B(?:ad(?:L(?:aunch|ibrary)|Patch(?:Header)?|S(?:ANEOpcode|lotInt|tartupDisk))|u(?:fPtrTooLow|sError))|C(?:DEFNotFound|antHoldSystemHeap|hkErr|oreErr)|Di(?:rtyDisk|sassemblerInstalled)|ExtensionsDisabled|F(?:PErr|SErr|inderErr|orcedQuit)|G(?:ibblyMovedToDisabledFolder|reeting)|H(?:D20Installed|MenuFindErr)|I(?:OCoreErr|llInstErr|rqErr)|L(?:ine(?:AErr|FErr)|o(?:adErr|stConnectionToNetworkDisk))|M(?:B(?:ATA(?:PISysError|SysError)|ExternFlpySysError|FlpySysError|SysError|arNFnd)|DEFNotFound|ac(?:OSROMVersionTooOld|sBugInstalled)|emFullErr|i(?:scErr|xedModeFailure)|ustUseFCBAccessors)|N(?:eedToWriteBootBlocks|o(?:Exts(?:Disassembler|MacsBug)|FPU|P(?:a(?:ckErr|tch)|k(?:1|2|3|4|5|6|7))|t(?:EnoughRAMToBoot|The1)))|O(?:ldSystem|vflowErr)|P(?:CCardATASysError|arityErr|rivErr)|R(?:AMDiskTooBig|e(?:insert|moveDisk))|S(?:CSIWarn|hutDownOrRes(?:tart|ume)|tknHeap|witchOffOrRestart|ys(?:Err|tem(?:FileErr|RequiresPowerPC)))|TraceErr|UnBootableSystem|VM(?:BadBackingStore|DeferredFuncTableFull)|W(?:DEFNotFound|riteToSupervisorStackGuardPage)|ZeroDivErr|kFulErr)|tQType|u(?:mmyType|p(?:FNErr|licate(?:AtomTypeAndIDErr|F(?:lavorErr|olderDescErr)|HandlerErr|PasteboardFlavorErr|RoutingErr|ScrapFlavorErr))|ration(?:Day|Forever|Hour|Mi(?:crosecond|llisecond|nute)|NoWait|Second))|ym)|e(?:A(?:DB|ddressSpec|nalogAudio|ppleTalk|udio(?:Line(?:In|Out)|Out))|Bus|C(?:DROM|apsLockDown|learKey|o(?:mm(?:Slot|andDown)|n(?:duit|trolDown)))|D(?:VD|e(?:leteKey|viceType)|i(?:gitalAudio|splay)|ownArrowKey)|E(?:n(?:dKey|terKey)|scapeKey|thernet)|F(?:1(?:0Key|1Key|2Key|3Key|4Key|5Key|Key)|2Key|3Key|4Key|5Key|6Key|7Key|8Key|9Key|ireWire|loppy|orwardDelKey)|H(?:D|elpKey|omeKey)|I(?:P|RTalk|nfrared|rDA)|Key(?:Kind|board)|L(?:CD|e(?:ftArrowKey|nErr)|ocalTalk)|M(?:ac(?:IP|Video)|icrophone|o(?:d(?:em(?:P(?:ort|rinterPort))?|ifiers)|nitorOut|use)|ultiErr)|NuBus(?:Card)?|OptionDown|P(?:C(?:I(?:bus|card)|card)|DS(?:card|slot)|PP|age(?:DownKey|UpKey)|o(?:intingDevice|stScript)|r(?:inter(?:Port)?|otocol))|R(?:eturnKey|ightArrowKey)|S(?:CSI|VGA|cheme|erial|hiftDown|peakers|torageDevice|video)|T(?:abKey|okenRing|rack(?:ball|pad))|U(?:SB|pArrowKey)|Video(?:In|Monitor|Out)|WS(?:ArrayType|BooleanType|D(?:at(?:aType|eType)|ictionaryType|oubleType)|IntegerType|NullType|StringType|UnknownType)|dit(?:Text|i(?:ngNotAllowed|onMgrInitErr))|mptyPathErr|n(?:dOfDataReached|um(?:A(?:fterDate|l(?:iases|l(?:D(?:isks|ocuments)|LocalDisks|OpenFolders|RemoteDisks))|nyDate|rr(?:angement|ows))|B(?:e(?:foreDate|tweenDate)|ooleanValues)|Con(?:flicts|sid(?:erations|sAndIgnores))|Date|ExistingItems|Fo(?:lders|ntsPanel)|Ge(?:neralPanel|stalt)|I(?:conSize|nfoWindowPanel)|Justification|KeyForm|La(?:rgeIconSize|st(?:Month|Week|Year))|M(?:emoryPanel|i(?:niIconSize|scValues))|O(?:lderItems|nDate)|P(?:osition|r(?:efs(?:ButtonViewPanel|GeneralPanel|IconViewPanel|L(?:abelPanel|istViewPanel)|WindowPanel)|otection))|Quality|S(?:aveOptions|haringPanel|mallIconSize|ortDirection(?:Normal|Reverse)?|t(?:at(?:ionery|usNConfigPanel)|yle))|T(?:his(?:Month|Week|Year)|oday|ransferMode)|ViewBy|Where|Yesterday)|v(?:BadVers|NotPresent|VersTooBig))|ofErr|r(?:a(?:Field|Mask)|r(?:A(?:E(?:AccessorNotFound|B(?:ad(?:KeyForm|ListItem|TestKey)|u(?:fferTooSmall|ildSyntaxError))|C(?:ant(?:HandleClass|PutThatThere|SupplyType|Undo)|o(?:ercionFail|rruptData))|D(?:esc(?:IsNull|NotFound)|uplicateHandler)|E(?:mptyListContainer|vent(?:F(?:ailed|iltered)|Not(?:Handled|Permitted)|WouldRequireUserConsent))|HandlerNotFound|I(?:llegalIndex|mpossibleRange|n(?:Transaction|dexTooLarge))|LocalOnly|N(?:e(?:gativeCount|werVersion)|o(?:Such(?:Logical|Object|Transaction)|User(?:Interaction|Selection)|t(?:A(?:EDesc|S(?:ingleObject|pecialFunction)|n(?:E(?:lement|numMember)|ObjSpec)|ppleEvent)|Modifiable)))|P(?:aramMissed|r(?:ivilegeError|opertiesClash))|Re(?:adDenied|c(?:eive(?:EscapeCurrent|Terminate)|ordingIsAlreadyOn)|plyNot(?:Arrived|Valid))|Stream(?:AlreadyConverted|BadNesting)|T(?:argetAddressNotPermitted|imeout|ypeError)|Unknown(?:AddressType|ObjectType|SendMode)|ValueOutOfRange|W(?:aitCanceled|r(?:iteDenied|ong(?:DataType|NumberArgs))))|S(?:CantCo(?:mpareMoreThan32k|nsiderAndIgnore)|I(?:llegalFormalParameter|nconsistentNames)|NoResultReturned|ParameterNotForEvent|TerminologyNestingTooDeep)|borted|lreadyInImagingMode|ttention|uthorization(?:BadAddress|Canceled|Denied|ExternalizeNotAllowed|In(?:ter(?:actionNotAllowed|nal(?:izeNotAllowed)?)|valid(?:Flags|Pointer|Ref|Set|Tag))|Success|ToolE(?:nvironmentError|xecuteFailure)))|C(?:an(?:NotInsertWhileWalkProcInProgress|notUndo|tEmbed(?:IntoSelf|Root))|o(?:ntrol(?:DoesntSupportFocus|HiddenOrDisabled|IsNotEmbedder|sAlreadyExist)|r(?:eEndianData(?:DoesNotMatchFormat|Too(?:LongForFormat|ShortForFormat))|ruptWindowDescription)|uldntSetFocus)|pp(?:General|Last(?:SystemDefinedError|UserDefinedError)|bad_(?:alloc|cast|exception|typeid)|domain_error|i(?:nvalid_argument|os_base_failure)|l(?:ength_error|ogic_error)|o(?:ut_of_range|verflow_error)|r(?:ange_error|untime_error)|underflow_error))|D(?:SPQueueSize|ata(?:Browser(?:I(?:nvalidProperty(?:Data|Part)|temNot(?:Added|Found))|NotConfigured|PropertyNot(?:Found|Supported))|NotSupported|SizeMismatch))|E(?:mptyScrap|n(?:dOf(?:Body|Document)|gineNotFound))|F(?:S(?:AttributeNotFound|Bad(?:AllocFlags|Buffer|F(?:SRef|ork(?:Name|Ref))|I(?:nfoBitmap|te(?:mCount|ratorFlags))|PosMode|SearchParams)|Fork(?:Exists|NotFound)|IteratorNot(?:Found|Supported)|Missing(?:CatInfo|Name)|N(?:ameTooLong|o(?:MoreItems|t(?:AFolder|EnoughSpaceForOperation)))|OperationNotSupported|PropertyNotValid|QuotaExceeded|RefsDifferent|UnknownCall)|inder(?:AppFolderProtected|B(?:adPackageContents|oundsWrong)|C(?:an(?:notPutAway|t(?:DeleteImmediately|Move(?:Source|To(?:Ancestor|Destination))|Overwrite|UseTrashedItems))|orruptOpenFolderList)|FileSharingMustBeOn|I(?:ncestuousMove|sBusy|temAlreadyInDest)|L(?:astReserved|ockedItemsInTrash)|MustBeActive|NoInvisibleFiles|OnlyLockedItemsInTrash|Pro(?:gramLinkingMustBeOn|perty(?:DoesNotApply|NowWindowBased))|S(?:harePointsCantInherit|ysFolderProtected)|Un(?:knownUser|supportedInsidePackages)|VolumeNotFound|Window(?:MustBe(?:ButtonView|IconView|ListView)|NotOpen|WrongType))|loatingWindowsNotInitialized|wdReset)|HMIllegalContentForM(?:aximumState|inimumState)|I(?:A(?:AllocationErr|BufferTooSmall|Canceled|EndOfTextRun|InvalidDocument|No(?:Err|MoreItems)|ParamErr|TextExtractionErr|UnknownErr)|nvalid(?:PartCode|Range|Window(?:P(?:roperty|tr)|Ref))|te(?:m(?:AlreadyInTree|Not(?:Control|FoundInTree))|ratorReachedEnd))|K(?:B(?:Fail(?:Setting(?:ID|TranslationTable)|WritePreference)|IlligalParameters|PS2KeyboardNotAvailable)|C(?:AuthFailed|BufferTooSmall|CreateChainFailed|D(?:ata(?:Not(?:Available|Modifiable)|TooLarge)|uplicate(?:Callback|Item|Keychain))|I(?:n(?:teraction(?:NotAllowed|Required)|valid(?:Callback|ItemRef|Keychain|SearchRef))|temNotFound)|KeySizeNotAllowed|No(?:CertificateModule|DefaultKeychain|PolicyModule|S(?:torageModule|uch(?:Attr|Class|Keychain))|tAvailable)|ReadOnly(?:Attr)?|WrongKCVersion))|M(?:arginWilllNotFit|essageNotSupported)|N(?:eedsCompositedWindow|o(?:HiliteText|RootControl|nContiuousAttribute|t(?:InImagingMode|ValidTree)))|O(?:SA(?:AppNotHighLevelEventAware|BadS(?:elector|torageType)|C(?:ant(?:A(?:ccess|ssign)|C(?:oerce|reate)|GetTerminology|Launch|OpenComponent|StorePointers)|o(?:mponentMismatch|rrupt(?:Data|Terminology)))|D(?:ata(?:BlockTooLarge|Format(?:Obsolete|TooNew))|ivideByZero)|GeneralError|In(?:ternalTableOverflow|validID)|N(?:oSuchDialect|umericOverflow)|RecordingIsAlreadyOn|S(?:criptError|ourceNotAvailable|tackOverflow|ystemError)|TypeError)|ffset(?:I(?:nvalid|sOutsideOfView)|NotOnElementBounday)|pen(?:Denied|ing))|R(?:e(?:adOnlyText|fNum)|ootAlreadyExists)|S(?:SL(?:ATS(?:C(?:ertificate(?:HashAlgorithmViolation|TrustViolation)|iphersuiteViolation)|LeafCertificateHashAlgorithmViolation|Minimum(?:KeySizeViolation|VersionViolation)|Violation)|B(?:ad(?:C(?:ert(?:ificateStatusResponse)?|ipherSuite|onfiguration)|RecordMac)|ufferOverflow)|C(?:ert(?:Expired|NotYetValid|ificateRequired)|l(?:ient(?:CertRequested|HelloReceived)|osed(?:Abort|Graceful|NoNotify))|on(?:figurationFailed|nectionRefused)|rypto)|Dec(?:o(?:deError|mpressFail)|ryptionFail)|FatalAlert|H(?:andshakeFail|ostNameMismatch)|I(?:llegalParam|n(?:appropriateFallback|ternal))|M(?:issingExtension|oduleAttach)|N(?:e(?:gotiation|tworkTimeout)|oRootCert)|P(?:eer(?:A(?:ccessDenied|uthCompleted)|Bad(?:Cert|RecordMac)|Cert(?:Expired|Revoked|Unknown)|Dec(?:o(?:deError|mpressFail)|rypt(?:Error|ionFail))|ExportRestriction|HandshakeFail|In(?:sufficientSecurity|ternalError)|NoRenegotiation|ProtocolVersion|RecordOverflow|U(?:n(?:expectedMsg|knownCA|supportedCert)|serCancelled))|rotocol)|RecordOverflow|SessionNotFound|TransportReset|Un(?:expected(?:Message|Record)|known(?:PSKIdentity|RootCert)|recognizedName|supportedExtension)|W(?:eakPeerEphemeralDHKey|ouldBlock)|XCertChainInvalid)|e(?:c(?:A(?:CL(?:AddFailed|ChangeFailed|DeleteFailed|NotSimple|ReplaceFailed)|ddin(?:LoadFailed|UnloadFailed)|l(?:gorithmMismatch|locate|readyLoggedIn)|pple(?:AddAppACLSubject|InvalidKey(?:EndDate|StartDate)|PublicKeyIncomplete|S(?:SLv2Rollback|ignatureMismatch))|tt(?:achHandleBusy|ributeNotInContext)|uthFailed)|B(?:adReq|lockSizeMismatch|ufferTooSmall)|C(?:RL(?:AlreadySigned|BadURI|Expired|Not(?:Found|Trusted|ValidYet)|PolicyFailed|ServerDown)|S(?:AmbiguousBundleFormat|Bad(?:BundleFormat|CallbackValue|Di(?:ctionaryFormat|skImageFormat)|FrameworkVersion|LVArch|MainExecutable|NestedCode|ObjectFormat|Resource|TeamIdentifier)|C(?:MSTooLarge|ancelled)|D(?:B(?:Access|Denied)|SStoreSymlink|bCorrupt)|FileHardQuarantined|GuestInvalid|H(?:elperFailed|ost(?:Protocol(?:Contradiction|DedicationError|Invalid(?:Attribute|Hash)|NotProxy|RelativePath|StateError|Unrelated)|Reject))|In(?:foPlistFailed|ternalError|valid(?:A(?:ssociatedFileData|ttributeValues)|Entitlements|Flags|ObjectRef|Platform|RuntimeVersion|Symlink|TeamIdentifier))|MultipleGuests|No(?:Ma(?:inExecutable|tches)|SuchCode|t(?:A(?:Host|ppLike)|Supported))|O(?:bjectRequired|utdated)|Re(?:gularFile|q(?:Failed|Invalid|Unsupported)|source(?:DirectoryFailed|NotSupported|RulesInvalid|s(?:Invalid|Not(?:Found|Sealed)))|vokedNotarization)|S(?:ig(?:DB(?:Access|Denied)|nature(?:Failed|Invalid|NotVerifiable|Un(?:supported|trusted)))|taticCode(?:Changed|NotFound))|TooBig|Un(?:implemented|s(?:ealed(?:AppRoot|FrameworkRoot)|igned(?:NestedCode)?|upported(?:DigestAlgorithm|GuestAttributes)))|Vetoed|WeakResource(?:Envelope|Rules))|allbackFailed|ertificate(?:CannotOperate|Expired|N(?:ameNotAllowed|otValidYet)|PolicyNotAllowed|Revoked|Suspended|ValidityPeriodTooLong)|o(?:deSigning(?:Bad(?:CertChainLength|PathLengthConstraint)|Development|No(?:BasicConstraints|ExtendedKeyUsage))|nversionError|reFoundationUnknown)|reateChainFailed)|D(?:ata(?:Not(?:Available|Modifiable)|TooLarge|baseLocked|storeIsOpen)|e(?:code|vice(?:Error|Failed|Reset|VerifyFailed))|iskFull|skFull|uplicate(?:Callback|Item|Keychain))|E(?:MM(?:LoadFailed|UnloadFailed)|ndOfData|ventNotificationCallbackNotFound|xtendedKeyUsageNotCritical)|F(?:i(?:eldSpecifiedMultiple|leTooBig)|unction(?:Failed|IntegrityFail))|HostNameMismatch|I(?:DPFailure|O|n(?:DarkWake|comp(?:atible(?:DatabaseBlob|FieldFormat|KeyBlob|Version)|leteCertRevocationCheck)|putLengthError|sufficientC(?:lientID|redentials)|ter(?:action(?:NotAllowed|Required)|nal(?:Component|Error))|val(?:dCRLAuthority|id(?:A(?:CL|c(?:cess(?:Credentials|Request)|tion)|ddinFunctionTable|lgorithm(?:Parms)?|ttribute(?:AccessCredentials|B(?:ase|lockSize)|DLDBHandle|E(?:ffectiveBits|ndDate)|I(?:nitVector|terationCount)|Key(?:Length|Type)?|Label|Mode|OutputSize|P(?:a(?:dding|ssphrase)|ri(?:me|vateKeyFormat)|ublicKeyFormat)|R(?:andom|ounds)|S(?:alt|eed|tartDate|ubprime|ymmetricKeyFormat)|Version|WrappedKeyFormat)|uthority(?:KeyID)?)|B(?:aseACLs|undleInfo)|C(?:RL(?:Encoding|Group|Index|Type)?|allback|ert(?:Authority|ificate(?:Group|Ref))|ontext)|D(?:BL(?:ist|ocation)|ata(?:baseBlob)?|igestAlgorithm)|E(?:ncoding|xtendedKeyUsage)|FormType|GUID|Handle(?:Usage)?|I(?:D(?:Linkage)?|dentifier|n(?:dex(?:Info)?|putVector)|temRef)|Key(?:AttributeMask|Blob|Format|Hierarchy|Label|Ref|Usage(?:ForPolicy|Mask)|chain)|LoginName|ModifyMode|N(?:ame|e(?:tworkAddress|wOwner)|umberOfFields)|O(?:utputVector|wnerEdit)|P(?:VC|a(?:rsingModule|ss(?:throughID|wordRef))|o(?:inter|licyIdentifiers)|refsDomain)|Query|R(?:e(?:ason|cord|quest(?:Inputs|or)|sponseVector)|oot)|S(?:ampleValue|cope|e(?:archRef|rviceMask)|ignature|topOnPolicy|ub(?:ServiceID|ject(?:KeyID|Name)))|T(?:imeString|rustSetting(?:s)?|uple(?:Credendtials|Group)?)|Val(?:idityPeriod|ue))))|temNotFound)|Key(?:BlobTypeIncorrect|HeaderInconsistent|IsSensitive|SizeNotAllowed|UsageIncorrect)|LibraryReferenceNotFound|M(?:DSError|emoryError|issing(?:A(?:lgorithmParms|ttribute(?:AccessCredentials|B(?:ase|lockSize)|DLDBHandle|E(?:ffectiveBits|ndDate)|I(?:nitVector|terationCount)|Key(?:Length|Type)?|Label|Mode|OutputSize|P(?:a(?:dding|ssphrase)|ri(?:me|vateKeyFormat)|ublicKeyFormat)|R(?:andom|ounds)|S(?:alt|eed|tartDate|ubprime|ymmetricKeyFormat)|Version|WrappedKeyFormat))|Entitlement|RequiredExtension|Value)|o(?:bileMe(?:CSRVerifyFailure|FailedConsistencyCheck|NoRequestPending|Request(?:AlreadyPending|Queued|Redirected)|Server(?:AlreadyExists|Error|NotAvailable|ServiceErr))|dule(?:Man(?:ager(?:InitializeFailed|NotFound)|ifestVerifyFailed)|NotLoaded))|ultiple(?:ExecSegments|PrivKeys|ValuesUnsupported))|N(?:etworkFailure|o(?:AccessForItem|BasicConstraints(?:CA)?|CertificateModule|Default(?:Authority|Keychain)|FieldValues|PolicyModule|S(?:torageModule|uch(?:Attr|Class|Keychain))|TrustSettings|t(?:Available|Initialized|LoggedIn|Signer|Trusted)))|O(?:CSP(?:BadRe(?:quest|sponse)|No(?:Signer|tTrustedToAnchor)|Respon(?:der(?:InternalError|MalformedReq|SignatureRequired|TryLater|Unauthorized)|seNonceMismatch)|S(?:ignatureError|tatusUnrecognized)|Unavailable)|pWr|utputLengthError)|P(?:VC(?:AlreadyConfigured|ReferentNotFound)|a(?:ram|ssphraseRequired|thLengthConstraintExceeded)|kcs12VerifyFailure|olicyNotFound|rivilegeNot(?:Granted|Supported)|ublicKeyInconsistent)|Qu(?:erySizeUnknown|otaExceeded)|Re(?:adOnly(?:Attr)?|cordModified|jectedForm|quest(?:Descriptor|Lost|Rejected)|sourceSignBad(?:CertChainLength|ExtKeyUsage))|S(?:MIME(?:Bad(?:ExtendedKeyUsage|KeyUsage)|EmailAddressesNotFound|KeyUsageNotCritical|NoEmailAddress|SubjAltNameNotCritical)|SLBadExtendedKeyUsage|e(?:lfCheckFailed|rviceNotAvailable)|igningTimeMissing|tagedOperation(?:InProgress|NotStarted)|uccess)|T(?:agNotFound|imestamp(?:AddInfoNotAvailable|Bad(?:Alg|DataFormat|Request)|Invalid|Missing|NotTrusted|Re(?:jection|vocation(?:Notification|Warning))|S(?:erviceNotAvailable|ystemFailure)|TimeNotAvailable|Unaccepted(?:Extension|Policy)|Waiting)|rust(?:NotAvailable|SettingDeny))|U(?:n(?:implemented|known(?:C(?:RLExtension|ertExtension|riticalExtensionFlag)|Format|QualifiedCertStatement|Tag)|supported(?:AddressType|F(?:ieldFormat|ormat)|IndexInfo|Key(?:AttributeMask|Format|Label|Size|UsageMask)|Locality|Num(?:Attributes|Indexes|RecordTypes|SelectionPreds)|Operator|QueryLimits|Service|VectorOfBuffers))|serCanceled)|Verif(?:icationFailure|y(?:ActionFailed|Failed))|Wr(?:Perm|ongSecVersion))|ssion(?:AuthorizationDenied|In(?:ternal|valid(?:Attributes|Flags|Id))|Success|ValueNotSet))|tate)|T(?:askNotFound|opOf(?:Body|Document)|reeIsLocked)|U(?:n(?:known(?:AttributeTag|Control|Element)|recognizedWindowClass|supportedWindowAttributesForClass)|serWantsToDragWindow)|W(?:S(?:InternalError|ParseError|T(?:imeoutError|ransportError))|indow(?:Does(?:Not(?:FitOnscreen|HaveProxy)|ntSupportFocus)|NotFound|PropertyNotFound|RegionCodeInvalid|sAlreadyInitialized))))|url(?:A(?:FP|T)|EPPC|F(?:TP|ile)|Gopher|HTTP(?:S)?|IMAP|L(?:DAP|aunch)|M(?:ail(?:box)?|essage|ulti)|N(?:FS|NTP|ews)|POP|RTSP|SNews|Telnet|Unknown)|v(?:Type|e(?:nt(?:AlreadyPostedErr|ClassIn(?:correctErr|validErr)|DeferAccessibilityEventErr|H(?:andlerAlreadyInstalledErr|otKey(?:ExistsErr|InvalidErr))|InternalErr|KindIncorrectErr|Loop(?:QuitErr|TimedOutErr)|Not(?:HandledErr|InQueueErr)|Pa(?:rameterNotFoundErr|ssToNextTargetErr)|TargetBusyErr)|ryEvent)|tNotEnb)|x(?:UserBreak|cessCollsns|t(?:FSErr|en(?:dedBlock(?:Len)?|sionsFolderIconResource)|ra(?:ctErr|neousStrings))))|f(?:B(?:adPartsTable|estGuess|syErr)|D(?:esktop|isk)|E(?:mptyFormatString|xtra(?:Decimal|Exp|Percent|Separator))|Form(?:StrIsNAN|atO(?:K|verflow))|HasBundle|Invisible|LckdErr|Missing(?:Delimiter|Literal)|Negative|O(?:nDesk|utOfSynch)|Positive|SpuriousChars|Trash|VNumber|Zero|a(?:ceBit|talDateTime)|e(?:ature(?:FontTableTag|Unsupported)|tchReference)|i(?:Ligature|d(?:Exists|NotFound)|eldOrderNotIntl|le(?:BoundsErr|OffsetTooBigErr)|rst(?:DskErr|PickerError))|l(?:Ligature|avor(?:DataPromised|NotSaved|S(?:ender(?:Only|Translated)|ystemTranslated)|Type(?:HFS|PromiseHFS))|o(?:at(?:GrowProc|Proc|Side(?:GrowProc|Proc|Zoom(?:GrowProc|Proc))|Zoom(?:GrowProc|Proc))|ppyIconResource))|mt(?:1Err|2Err)|n(?:OpnErr|fErr)|o(?:nt(?:Bit|DecError|Not(?:Declared|OutlineErr)|Panel(?:FontSelectionQDStyleVersionErr|S(?:electionStyleErr|howErr))|SubErr|sFolderIconResource)|r(?:ceRead(?:Bit|Mask)|m(?:A(?:bsolutePosition|lias)|Creator|Name|PropertyID|R(?:ange|elativePosition)|Test|U(?:niqueID|serPropertyID)|Whose)))|raction|s(?:AtMark|CurPerm|D(?:SIntErr|ataTooBigErr)|From(?:LEOF|Mark|Start)|QType|R(?:d(?:AccessPerm|DenyPerm|Perm|Wr(?:Perm|ShPerm))|nErr|t(?:DirID|ParID))|SB(?:A(?:ccessDate(?:Bit)?|ttributeModDate(?:Bit)?)|Dr(?:BkDat(?:Bit)?|CrDat(?:Bit)?|FndrInfo(?:Bit)?|MdDat(?:Bit)?|NmFls(?:Bit)?|ParID(?:Bit)?|UsrWds(?:Bit)?)|F(?:l(?:Attrib(?:Bit)?|BkDat(?:Bit)?|CrDat(?:Bit)?|FndrInfo(?:Bit)?|LgLen(?:Bit)?|MdDat(?:Bit)?|P(?:arID(?:Bit)?|yLen(?:Bit)?)|R(?:LgLen(?:Bit)?|PyLen(?:Bit)?)|XFndrInfo(?:Bit)?)|ullName(?:Bit)?)|GroupID(?:Bit)?|N(?:egate(?:Bit)?|odeID(?:Bit)?)|P(?:artialName(?:Bit)?|ermissions(?:Bit)?)|Skip(?:HiddenItems(?:Bit)?|PackageContents(?:Bit)?)|UserID(?:Bit)?)|UnixPriv|Wr(?:AccessPerm|DenyPerm|Perm)|m(?:B(?:adF(?:FSNameErr|SD(?:LenErr|VersionErr))|usyFFSErr)|DuplicateFSIDErr|FFSNotFoundErr|NoAlternateStackErr|UnknownFSMMessageErr))|ullTrashIconResource)|g(?:WorldsNotSameDepthAndSizeErr|crOnMFMErr|e(?:n(?:CdevRangeBit|eric(?:ApplicationIconResource|CDROMIconResource|D(?:eskAccessoryIconResource|ocumentIconResource)|E(?:ditionFileIconResource|xtensionIconResource)|F(?:ileServerIconResource|olderIconResource)|HardDiskIconResource|MoverObjectIconResource|PreferencesIconResource|QueryDocumentIconResource|RAMDiskIconResource|S(?:tationeryIconResource|uitcaseIconResource)))|stalt(?:16Bit(?:AudioSupport|SoundIO)|20thAnniversary|32Bit(?:Addressing|Capable|QD(?:1(?:1|2|3))?|SysZone)|68(?:0(?:00|10|20|30(?:MMU)?|40(?:FPU|MMU)?)|8(?:51|8(?:1|2))|k)|8BitQD|A(?:DB(?:ISOKbdII|KbdII)|FPClient(?:3_(?:5|6(?:_(?:1|2|3))?|7(?:_2)?|8(?:_(?:1|3|4))?)|AttributeMask|CfgRsrc|MultiReq|SupportsIP|V(?:MUI|ersionMask))?|LM(?:Attr|Has(?:CFMSupport|RescanNotifiers|SF(?:Group|Location))|Present|Vers)|MU|T(?:A(?:Attr|Present)|SU(?:AscentDescentControlsFeature|B(?:atchBreakLinesFeature|iDiCursorPositionFeature|yCharacterClusterFeature)|D(?:ecimalTabFeature|irectAccess|ropShadowStyleFeature)|F(?:allbacks(?:Feature|ObjFeatures)|eatures)|GlyphBoundsFeature|Highlight(?:ColorControlFeature|InactiveTextFeature)|IgnoreLeadingFeature|L(?:ayoutC(?:acheClearFeature|reateAndCopyFeature)|ineControlFeature|owLevelOrigFeatures)|MemoryFeature|NearestCharLineBreakFeature|PositionToCursorFeature|StrikeThroughStyleFeature|T(?:abSupportFeature|extLocatorUsageFeature|rackingFeature)|U(?:nderlineOptionsStyleFeature|pdate(?:1|2|3|4|5|6|7))|Version)|alkVersion)|UXVersion|VLTree(?:Attr|PresentBit|Supports(?:HandleBasedTreeBit|TreeLockingBit))|WS(?:6150_6(?:0|6)|8(?:150_(?:110|80)|550)|9150_(?:120|80))|d(?:dressingModeAttr|minFeaturesFlagsAttr)|l(?:iasMgr(?:Attr|FollowsAliasesWhenResolving|Pre(?:fersPath|sent)|Re(?:quiresAccessors|solveAliasFileWithMountOptions)|Supports(?:AOCEKeychain|ExtendedCalls|FSCalls|RemoteAppletalk))|legroQD(?:Text)?|tivecRegistersSwappedCorrectlyBit)|ntiAliasedTextAvailable|pp(?:earance(?:Attr|CompatMode|Exists|Version)|le(?:Adjust(?:ADBKbd|ISOKbd|Keypad)|Events(?:Attr|Present)|Guide(?:IsDebug|Present)|Script(?:Attr|P(?:owerPCSupport|resent)|Version)|TalkVersion))|rbitorAttr|syncSCSI(?:INROM)?)|Bu(?:iltInSoundInput|sClkSpeed(?:MHz)?)|C(?:FM(?:99Present(?:Mask)?|Attr|Present(?:Mask)?)|PU(?:486|6(?:0(?:1|3(?:e(?:v)?)?|4(?:e(?:v)?)?)|80(?:00|10|20|30|40))|750(?:FX)?|970(?:FX|MP)?|Apollo|G4(?:74(?:47|50))?|Pentium(?:4|II|Pro)?|X86)|RM(?:Attr|P(?:ersistentFix|resent)|ToolRsrcCalls)|TBVersion|a(?:n(?:StartDragInFloatWindow|UseCGTextRendering)|r(?:bonVersion|dServicesPresent))|l(?:assic(?:II)?|oseView(?:Attr|DisplayMgrFriendly|Enabled))|o(?:l(?:lectionMgrVersion|or(?:Matching(?:Attr|LibLoaded|Version)|Picker(?:Version)?|Sync(?:1(?:0(?:4|5)?|1)|2(?:0|1(?:1|2|3)?|5|6(?:1)?)|30)))|mp(?:onent(?:Mgr|Platform)|ressionMgr)|n(?:nMgr(?:Attr|CMSearchFix|ErrorString|MultiAsyncIO|Present)|t(?:extualMenu(?:Attr|Has(?:AttributeAndModifierKeys|UnicodeSupport)|TrapAvailable|UnusedBit)|rol(?:M(?:gr(?:Attr|Present(?:Bit)?|Version)|sgPresentMask)|Strip(?:Attr|Exists|User(?:Font|HotKey)|Version(?:Fixed)?))))|untOfCPUs)|reatesAliasFontRsrc|urrentGraphicsVersion)|D(?:BAccessMgr(?:Attr|Present)|ITLExt(?:Attr|Present|SupportsIctb)|T(?:MgrSupportsFSM|P(?:Features|Info))|esktop(?:Pictures(?:Attr|Displayed|Installed)|SpeechRecognition)|i(?:alogM(?:gr(?:Attr|HasAquaAlert(?:Bit|Mask)|Present(?:Bit|Mask)?)|sgPresentMask)|ctionaryMgr(?:Attr|Present)|gitalSignatureVersion|s(?:kCacheSize|playMgr(?:Attr|C(?:an(?:Confirm|SwitchMirrored)|olorSyncAware)|GeneratesProfiles|Present|S(?:etDepthNotifies|leepNotifies)|Vers)))|ra(?:gMgr(?:Attr|FloatingWind|HasImageSupport|Present)|wSprocketVersion)|upSelectorErr)|E(?:MMU1|asyAccess(?:Attr|Locked|O(?:ff|n)|Sticky)|ditionMgr(?:Attr|Present|TranslationAware)|xt(?:ADBKbd|ISOADBKbd|ToolboxTable|en(?:ded(?:TimeMgr|WindowAttributes(?:Bit|Mask)?)|sionTableVersion)))|F(?:BC(?:CurrentVersion|IndexingState|Version|indexing(?:Critical|Safe))|PUType|S(?:A(?:llowsConcurrentAsyncIO|ttr)|IncompatibleDFA82|M(?:DoesDynamicLoad|Version)|NoMFSVols|Supports(?:2TBVols|4GBVols|DirectIO|ExclusiveLocks|H(?:FSPlusVols|ardLinkDetection))|UsesPOSIXPathsForConversion)|XfrMgr(?:A(?:sync|ttr)|ErrorString|MultiFile|Present)|i(?:le(?:AllocationZeroedBlocksBit|Mapping(?:Attr|MultipleFilesFix|Present))|nd(?:Folder(?:Attr|Present|RedirectionAttr)|er(?:Attr|CallsAEProcess|DropEvent|F(?:loppyRootComments|ullDragManagerSupport)|HasClippings|LargeAndNotSavedFlavorsOK|MagicPlacement|Supports4GBVolumes|U(?:nderstandsRedirectedDesktopFolder|ses(?:ExtensibleFolderManager|SpecialOpenFoldersFile))))|rstSlotNumber)|loppy(?:Attr|IsM(?:FMOnly|anualEject)|UsesDiskInPlace)|o(?:lder(?:DescSupport|Mgr(?:FollowsAliasesWhenResolving|Supports(?:Domains|ExtendedCalls|FSCalls)))|ntMgrAttr)|rontWindowMayBeHidden(?:Bit|Mask)|ullExtFSDispatching)|G(?:X(?:PrintingMgrVersion|Version)|raphics(?:Attr|Is(?:Debugging|Loaded|PowerPC)|Version))|H(?:a(?:rdware(?:Attr|Vendor(?:Apple|Code))|s(?:ASC|Color|D(?:eepGWorlds|irectPixMaps)|E(?:nhancedLtalk|xtendedDiskInit)|F(?:MTuner|SSpecCalls|ileSystemManager|loatingWindows(?:Bit|Mask)?)|G(?:PI(?:aTo(?:DCDa|RTxCa)|bToDCDb)|rayishTextOr)|H(?:FSPlusAPIs|WClosedCaptioning)|IRRemote|ParityCapability|ResourceOverrides|S(?:C(?:C|SI(?:96(?:1|2))?)|erialFader|ingleWindowMode(?:Bit|Mask)|o(?:ftPowerOff|und(?:Fader|InputDevice))|tereoDecoder|ystemIRFunction)|TVTuner|UniversalROM|V(?:IA(?:1|2)|idDecoderScaler)|Window(?:Buffering(?:Bit|Mask)?|Shadows(?:Bit|Mask))|ZoomedVideo))|elpMgr(?:Attr|Extensions|Present)|i(?:dePort(?:A|B)|ghLevelMatching))|I(?:NeedIRPowerOffConfirm|PCSupport|RDisabled|conUtilities(?:Attr|Has(?:32BitIcons|48PixelIcons|8BitDeepMasks|IconServices)|Present)|n(?:itHeapZerosOutHeapsBit|te(?:l|rnalDisplay)))|JapanAdjustADBKbd|K(?:BPS2(?:Keyboards|Set(?:IDToAny|TranslationTable))|eyboard(?:Type|sAttr))|L(?:aunch(?:C(?:anReturn|ontrol)|FullFileSpec)|ineLevelInput|o(?:cationErr|gical(?:PageSize|RAMSize)|wMemorySize))|M(?:B(?:Legacy|MultipleBays|SingleBay)|MUType|P(?:CallableAPIsAttr|DeviceManager|FileManager|TrapCalls)|ac(?:512KE|AndPad|C(?:entris6(?:10|50|60AV)|lassic|olorClassic)|II(?:c(?:i|x)|fx|si|v(?:i|m|x)|x)?|Kbd|LC(?:475|5(?:20|75|80)|II(?:I)?)?|OS(?:Compatibility(?:Box(?:Attr|HasSerial|Present|less))?|XQD(?:Text)?)|Plus(?:Kbd)?|Quadra(?:6(?:05|10|30|50|60AV)|700|8(?:00|40AV)|9(?:00|50))|SE(?:030)?|TV|XL|hine(?:Icon|Type))|e(?:diaBay|moryMap(?:Attr|Sparse)|nuMgr(?:A(?:quaLayout(?:Bit|Mask)|ttr)|CGImageMenuTitle(?:Bit|Mask)|M(?:oreThanFiveMenusDeep(?:Bit|Mask)|ultipleItemsWithCommandID(?:Bit|Mask))|Present(?:Bit|Mask)?|RetainsIconRef(?:Bit|Mask)|SendsMenuBoundsToDefProc(?:Bit|Mask))|ssageMgrVersion)|i(?:scAttr|xedMode(?:Attr|CFM68K(?:Has(?:State|Trap))?|PowerPC|Version))|u(?:lti(?:Channels|pleUsersState)|stUseFCBAccessors))|N(?:a(?:meRegistryVersion|tive(?:CPU(?:family|type)|ProcessMgrBit|T(?:imeMgr|ype1FontSupport)))|ew(?:HandleReturnsZeroedMemoryBit|PtrReturnsZeroedMemoryBit)|o(?:FPU|MMU|tification(?:MgrAttr|Present))|uBus(?:Connectors|Present|SlotCount))|O(?:CE(?:SFServerAvailable|T(?:B(?:Available|NativeGlueAvailable|Present)?|oolbox(?:Attr|Version)))|FA2available|S(?:Attr|L(?:CompliantFinder|InSystem)|Table|XFBCCurrentVersion)|pen(?:FirmwareInfo|Tpt(?:A(?:RAPPresent|ppleTalk(?:Loaded(?:Bit|Mask)|Present(?:Bit|Mask)))|IPXSPX(?:Loaded(?:Bit|Mask)|Present(?:Bit|Mask))|Loaded(?:Bit|Mask)|NetworkSetup(?:Legacy(?:Export|Import)|SupportsMultihoming|Version)?|P(?:PPPresent|resent(?:Bit|Mask))|RemoteAccess(?:ClientOnly|Loaded|MPServer|P(?:Server|resent)|Version)?|TCP(?:Loaded(?:Bit|Mask)|Present(?:Bit|Mask))|Versions)?)|riginal(?:ATSUVersion|QD(?:Text)?)|utlineFonts)|P(?:C(?:Card(?:FamilyPresent|HasPowerControl|SupportsCardBus)?|X(?:Attr|Has(?:8and16BitFAT|ProDOS)|NewUI|UseICMapping))|Mgr(?:CPUIdle|DispatchExists|Exists|S(?:CC|ound|upportsAVPowerStateAtSleepWake))|PC(?:DragLibPresent|QuickTimeLibPresent|Supports(?:Incoming(?:AppleTalk|TCP_IP)?|Out(?:Going|going(?:AppleTalk|TCP_IP))|RealTime|TCP_IP)|Toolbox(?:Attr|Present))|S2Keyboard|ar(?:ity(?:Attr|Enabled)|tialRsrcs)|erforma(?:250|4(?:50|6x|7x)|5(?:300|50|80)|6(?:00|3(?:00|60)|400))|hysicalRAMSize(?:InMegabytes)?|layAndRecord|o(?:pup(?:Attr|Present)|rt(?:ADisabled|BDisabled|able(?:2001(?:ANSIKbd|ISOKbd|JISKbd)|SlotPresent|USB(?:ANSIKbd|ISOKbd|JISKbd))?)|wer(?:Book(?:1(?:00|4(?:0(?:0)?|5)|50|6(?:0|5(?:c)?)|70|80(?:c)?|90)|2400|3400|5(?:00PPCUpgrade|20(?:c)?|300|40(?:c)?)|Duo2(?:10|30(?:0)?|50|70c|80(?:c)?)|G3(?:Series(?:2)?)?)|M(?:ac(?:4400(?:_160)?|5(?:2(?:00|60)|400|500)|6(?:100_6(?:0|6)|200|400|500)|7(?:100_(?:66|80)|200|300|500|600)|8(?:100_(?:1(?:00|10|20)|80)|500|600)|9(?:500|600)|Centris6(?:10|50)|G3|LC(?:475|575|630)|NewWorld|Performa(?:47x|57x|63x)|Quadra(?:6(?:10|30|50)|700|800|9(?:00|50)))|gr(?:Attr|Vers))|PC(?:A(?:SArchitecture|ware)|Has(?:64BitSupport|D(?:CB(?:AInstruction|TStreams)|ataStreams)|GraphicsInstructions|S(?:TFIWXInstruction|quareRootInstructions)|VectorInstructions)|IgnoresDCBST|ProcessorFeatures)?))|r(?:o(?:F16(?:ANSIKbd|ISOKbd|JISKbd)|c(?:ClkSpeed(?:MHz)?|essor(?:CacheLineSize|Type)))|tbl(?:ADBKbd|ISOKbd))|wrB(?:k(?:99JISKbd|E(?:K(?:DomKbd|ISOKbd|JISKbd)|xt(?:ADBKbd|ISOKbd|JISKbd))|Sub(?:DomKbd|ISOKbd|JISKbd))|ook(?:ADBKbd|ISOADBKbd)))|Q(?:D(?:3D(?:Present|V(?:ersion|iewer(?:Present)?))?|HasLongRowBytes|Text(?:Features|Version))|TVR(?:C(?:ubicPanosPresent|ylinderPanosPresent)|Mgr(?:Attr|Present|Vers)|ObjMoviesPresent)|u(?:adra(?:6(?:05|10|30|50|60AV)|700|8(?:00|40AV)|9(?:00|50))|ick(?:Time(?:Conferencing(?:Info)?|Features|Streaming(?:Features|Version)|ThreadSafe(?:FeaturesAttr|Graphics(?:Export|Import)|ICM|Movie(?:Export|Import|Playback|Toolbox))|Version)?|draw(?:Features|Version))))|R(?:BVAddr|M(?:F(?:akeAppleMenuItemsRolledIn|orceSysHeapRolledIn)|SupportsFSCalls|TypeIndexOrderingReverse)|OM(?:Size|Version)|e(?:al(?:TempMemory|timeMgr(?:Attr|Present))|sourceMgr(?:Attr|BugFixesAttrs)|visedTimeMgr))|S(?:C(?:C(?:ReadAddr|WriteAddr)|SI(?:PollSIH|SlotBoot)?)|DP(?:FindVersion|PromptVersion|StandardDirectoryVersion)|E(?:30SlotPresent|SlotPresent)|FServer|MP(?:MailerVersion|SPSendLetterVersion)|a(?:feOFAttr|nityCheckResourceFiles)|bitFontSupport|cr(?:apMgr(?:Attr|TranslationAware)|eenCapture(?:Dir|Main)|ipt(?:Count|MgrVersion|ingSupport)|ollingThrottle)|e(?:rialA(?:rbitrationExists|ttr)|tDragImageUpdates)|h(?:eetsAreWindowModal(?:Bit|Mask)|utdown(?:Attributes|HassdOnBootVolUnmount))|lot(?:Attr|MgrExists)|ndPlayDoubleBuffer|o(?:ftwareVendor(?:Apple|Code|Licensee)|und(?:Attr|IOMgrPresent))|p(?:e(?:cificMatchSupport|ech(?:Attr|HasPPCGlue|MgrPresent|Recognition(?:Attr|Version)))|litOS(?:A(?:ttr|ware)|BootDriveIsNetworkVolume|EnablerVolumeIsDifferentFromBootVolume|MachineNameS(?:etToNetworkNameTemp|tartupDiskIsNonPersistent)))|quareMenuBar|t(?:andard(?:File(?:58|Attr|Has(?:ColorIcons|DynamicVolumeAllocation)|TranslationAware|UseGenericIcons)|TimeMgr)|d(?:ADBKbd|ISOADBKbd|NBP(?:Attr|Present|SupportsAutoPosition))|ereo(?:Capability|Input|Mixing))|upports(?:ApplicationURL|FSpResourceFileAlreadyOpenBit|Mirroring)|ys(?:Architecture|DebuggerSupport|ZoneGrowable|temUpdateVersion))|T(?:E(?:1|2|3|4|5|6|Attr|Has(?:GetHiliteRgn|WhiteBackground)|Supports(?:InlineInput|TextObjects))|SM(?:DisplayMgrAwareBit|TE(?:1(?:5(?:2)?)?|Attr|Present|Version)?|doesTSMTEBit|gr(?:15|2(?:0|2|3)|Attr|Version))|VAttr|e(?:le(?:Mgr(?:A(?:ttr|utoAnswer)|IndHandset|NewTELNewSupport|P(?:owerPCSupport|resent)|S(?:ilenceDetect|oundStreams))|phoneSpeechRecognition)|mpMem(?:Support|Tracked)|rmMgr(?:Attr|ErrorString|Present)|xtEditVersion)|h(?:irdParty(?:ANSIKbd|ISOKbd|JISKbd)|read(?:Mgr(?:Attr|Present)|sLibraryPresent))|imeMgrVersion|oolboxTable|ranslation(?:Attr|GetPathAPIAvail|Mgr(?:Exists|HintOrder)|PPCAvail))|U(?:DFSupport|SB(?:A(?:ndy(?:ANSIKbd|ISOKbd|JISKbd)|ttr)|Cosmo(?:ANSIKbd|ISOKbd|JISKbd)|HasIsoch|Pr(?:esent|interSharing(?:Attr(?:Booted|Mask|Running)?|Version(?:Mask)?)|oF16(?:ANSIKbd|ISOKbd|JISKbd))|Version)|n(?:defSelectorErr|known(?:Err|ThirdPartyKbd))|serVisibleMachineName)|V(?:IA(?:1Addr|2Addr)|M(?:Attr|BackingStoreFileRefNum|FilemappingOn|Has(?:LockMemoryForOutput|PagingControl)|Info(?:NoneType|Si(?:mpleType|ze(?:StorageType|Type))|Type)|Present|ZerosPagesBit)|alueImplementedVers|ersion)|W(?:SII(?:CanPrintWithoutPrGeneralBit|Support)|indow(?:LiveResize(?:Bit|Mask)|M(?:gr(?:Attr|Present(?:Bit|Mask)?)|inimizeToDock(?:Bit|Mask)))|orldScriptII(?:Attr|Version))|X86(?:AdditionalFeatures|Features|Has(?:APIC|C(?:ID|LFSH|MOV|X(?:16|8))|D(?:E|S(?:CPL)?)|EST|F(?:PU|XSR)|HTT|M(?:C(?:A|E)|MX|ONITOR|SR|TRR)|P(?:A(?:E|T)|GE|S(?:E(?:36)?|N))|S(?:EP|MX|S(?:E(?:2|3)?)?|upplementalSSE3)|T(?:M(?:2)?|SC)|VM(?:E|X)|xTPR)|ResACPI|Serviced20)))|fpErr|ra(?:bTimeComplete|veUpr(?:E|I|O|U))|uestNotAllowedErr)|h(?:AxisOnly|Menu(?:Cmd|FindErr)|a(?:chek|ndlerNotFoundErr|rdwareConfigErr)|i(?:Archive(?:EncodingCompleteErr|HIObjectIgnoresArchivingErr|KeyNotAvailableErr|TypeMismatchErr)|Object(?:C(?:annotSubclassSingletonErr|lass(?:ExistsErr|Has(?:InstancesErr|SubclassesErr)|IsAbstractErr))|Delegate(?:AlreadyExistsErr|NotFoundErr))|erMenu|ghLevelEventMask|tDev)|m(?:BalloonAborted|CloseViewActive|Help(?:Disabled|ManagerNotInited)|NoBalloonUp|OperationUnsupported|S(?:ameAsLastBalloon|kippedBalloon)|UnknownHelpType|WrongVersion)|our(?:Field|Mask)|r(?:HTMLRenderingLibNotInstalledErr|LeadingZ|MiscellaneousExceptionErr|U(?:RLNotHandledErr|nableToResizeHandleErr))|wParamErr)|i(?:IOAbort(?:Err)?|MemFullErr|c(?:Config(?:InappropriateErr|NotFoundErr)|InternalErr|No(?:MoreWritersErr|Perm|URLErr|thingToOverrideErr)|P(?:ermErr|r(?:ef(?:DataErr|NotFoundErr)|ofileNotFoundErr))|Read(?:OnlyPerm|WritePerm)|T(?:ooManyProfilesErr|runcatedErr)|onItem)|llegal(?:C(?:hannelOSErr|ontrollerOSErr)|InstrumentOSErr|Knob(?:OSErr|ValueOSErr)|NoteChannelOSErr|PartOSErr|ScrapFlavor(?:FlagsErr|SizeErr|TypeErr)|VoiceAllocationOSErr)|n(?:Co(?:llapseBox|ntent)|D(?:esk|rag)|G(?:oAway|row)|MenuBar|NoWindow|ProxyIcon|S(?:tructure|ysWindow)|ToolbarButton|Zoom(?:In|Out)|compatibleVoice|it(?:Dev|IWMErr)|putOutOfBounds|sufficientStackErr|t(?:Arabic|DrawHook|E(?:OLHook|uropean)|HitTestHook|InlineInputTSMTEP(?:ostUpdateHook|reUpdateHook)|Japanese|NWidthHook|OutputMask|Roman|TextWidthHook|W(?:estern|idthHook)|er(?:nal(?:ComponentErr|QuickTimeError|ScrapErr)|ruptsMaskedErr)|lCurrency)|valid(?:Atom(?:ContainerErr|Err|TypeErr)|C(?:hunk(?:Cache|Num)|omponentID)|D(?:ataRef(?:Container)?|uration)|EditState|FolderTypeErr|H(?:andler|otSpotIDErr)|I(?:conRefErr|mageIndexErr|ndexErr)|M(?:edia|ovie)|Node(?:FormatErr|IDErr)|PickerType|Rect|S(?:ample(?:Desc(?:Index|ription)|Num|Table)|prite(?:I(?:DErr|ndexErr)|PropertyErr|WorldPropertyErr))|T(?:ime|ra(?:ck|nslationPathErr))|ViewStateErr))|o(?:Dir(?:Flg|Mask)|Err|QType)|t(?:emDisable|lc(?:D(?:isableKeyScriptSync(?:Mask)?|ualCaret)|S(?:howIcon|ysDirection)))|u(?:Current(?:CurLang|DefLang|Script)|NumberPartsTable|S(?:cript(?:CurLang|DefLang)|ystem(?:CurLang|DefLang|Script))|UnTokenTable|W(?:hiteSpaceList|ord(?:SelectTable|WrapTable))))|k(?:1(?:6(?:B(?:E5(?:55PixelFormat|65PixelFormat)|itCardErr)|LE5(?:55(?:1PixelFormat|PixelFormat)|65PixelFormat))|IndexedGrayPixelFormat|MonochromePixelFormat)|2(?:4(?:BGRPixelFormat|RGBPixelFormat)|Indexed(?:GrayPixelFormat|PixelFormat)|vuyPixelFormat)|3(?:2(?:A(?:BGRPixelFormat|RGBPixelFormat)|B(?:GRAPixelFormat|itHeap)|RGBAPixelFormat)|DMixer(?:AttenuationCurve_(?:Exponential|Inverse|Linear|Power)|Param_(?:Azimuth|Distance|Elevation|Gain|P(?:laybackRate|ost(?:AveragePower|PeakHoldLevel)|re(?:AveragePower|PeakHoldLevel)))|RenderingFlags_(?:ConstantReverbBlend|D(?:istance(?:Attenuation|Diffusion|Filter)|opplerShift)|InterAuralDelay|LinearDistanceAttenuation)))|4Indexed(?:GrayPixelFormat|PixelFormat)|68kInterruptLevelMask|8Indexed(?:GrayPixelFormat|PixelFormat)|A(?:E(?:A(?:ND|bout|ctivate|fter|l(?:iasSelection|l(?:Caps)?|waysInteract)|n(?:swer|y)|pp(?:earanceChanged|lication(?:Class|Died))|rrow(?:At(?:End|Start)|BothEnds)|sk|utoDown)|B(?:e(?:fore|gin(?:Transaction|ning|sWith))|old)|C(?:a(?:n(?:Interact|SwitchLayer)|se(?:ConsiderMask|IgnoreMask|SensEquals)?)|entered|hangeView|l(?:eanUp|o(?:ne|se))|o(?:mmandClass|n(?:densed|tains)|py|reSuite|untElements)|reate(?:Element|Publisher)|ut)|D(?:ata(?:Array|baseSuite)|e(?:activate|bug(?:POSTHeader|ReplyHeader|XML(?:DebugAll|Re(?:quest|sponse)))|faultTimeout|lete|sc(?:Array|ListFactor(?:None|Type(?:AndSize)?)))|i(?:acritic(?:ConsiderMask|IgnoreMask)?|rectCall|skEvent)|o(?:Not(?:AutomaticallyAddAnnotationsToEvent|IgnoreHandler|PromptForUserConsent)|ObjectsExist|Script|nt(?:DisposeOnResume|Execute|Reco(?:nnect|rd))|wn)|rag|uplicateSelection)|E(?:ditGraphic|ject|mpty(?:Trash)?|nd(?:Transaction|sWith)?|quals|rase|xpan(?:ded|sion(?:ConsiderMask|IgnoreMask)?))|F(?:a(?:lse|st)|etchURL|i(?:nder(?:Events|Suite)|rst)|ormulaProtect|ullyJustified)|G(?:e(?:stalt|t(?:ClassInfo|Data(?:Size)?|EventInfo|InfoSelection|PrivilegeSelection|SuiteInfo|URL))|r(?:eaterThan(?:Equals)?|ow))|H(?:TTPProxy(?:HostAttr|PortAttr)|andle(?:Array|SimpleRanges)|i(?:Quality|dden|gh(?:Level|Priority))|yphens(?:ConsiderMask|IgnoreMask)?)|I(?:Do(?:M(?:arking|inimum)|Whose)|S(?:Action(?:Path)?|C(?:lient(?:Address|IP)|ontentType)|F(?:romUser|ullRequest)|GetURL|HTTPSearchArgs|Method|P(?:assword|ostArgs)|Referrer|S(?:criptName|erver(?:Name|Port))|User(?:Agent|Name)|WebStarSuite)|gnore(?:App(?:EventHandler|PhacHandler)|Sys(?:EventHandler|PhacHandler))|mageGraphic|n(?:fo|goreBuiltInEventHandler|ter(?:actWith(?:All|Local|Self)|ceptOpen|netSuite))|sUniform|talic)|K(?:ataHiragana|ey(?:Class|D(?:escArray|own)))|L(?:ast|e(?:ftJustified|ssThan(?:Equals)?)|o(?:calProcess|gOut|wercase))|M(?:a(?:in|keObjectsVisible)|enu(?:Class|Select)|i(?:ddle|scStandards)|o(?:difiable|use(?:Class|Down(?:InBack)?)|ve(?:d)?))|N(?:OT|avigationKey|e(?:verInteract|xt)|o(?:Arrow|Dispatch|Reply|nmodifiable|rmalPriority|tify(?:Recording|St(?:artRecording|opRecording)))?|ullEvent)|O(?:R|SAXSizeResource|pen(?:Application|Contents|Documents|Selection)?|utline)|P(?:a(?:ckedArray|geSetup|s(?:sSubDescs|te))|lain|r(?:evious|int(?:Documents|Selection|Window)?|o(?:cessNonReplyEvents|mise))|u(?:nctuation(?:ConsiderMask|IgnoreMask)?|tAway(?:Selection)?))|Q(?:D(?:Ad(?:M(?:ax|in)|d(?:Over|Pin))|B(?:ic|lend)|Copy|Not(?:Bic|Copy|Or|Xor)|Or|Su(?:b(?:Over|Pin)|pplementalSuite)|Xor)|u(?:eueReply|i(?:ckdrawSuite|t(?:A(?:ll|pplication)|PreserveState|Reason))))|R(?:PCClass|awKey|e(?:allyLogOut|buildDesktopDB|do|gular|moteProcess|openApplication|place|quiredSuite|s(?:ized|olveNestedLists|tart|ume)|ve(?:alSelection|rt))|ightJustified)|S(?:OAPScheme|a(?:meProcess|ve)|cr(?:apEvent|iptingSizeResource)|e(?:lect|t(?:Data|Position))|h(?:a(?:dow|r(?:edScriptHandler|ing))|ow(?:Clipboard|Preferences|RestartDialog|ShutdownDialog)|utDown)|leep|mall(?:Caps|Kana|SystemFontChanged)|o(?:cks(?:4Protocol|5Protocol|HostAttr|P(?:asswordAttr|ortAttr|roxyAttr)|UserAttr)|rt)|pe(?:cialClassProperties|ech(?:D(?:etected|one)|Suite))|t(?:artRecording|op(?:Recording|pedMoving)|rikethrough)|u(?:bscript|perscript|spend)|y(?:nc|stemFontChanged))|T(?:ableSuite|e(?:rminologyExtension|xtSuite)|hemeSwitch|r(?:ansactionTerminated|ue))|U(?:T(?:Apostrophe|ChangesState|DirectParamIsReference|Enum(?:ListIsExclusive|erated|sAreTypes)|Feminine|HasReturningParam|Masculine|NotDirectParamIsTarget|Optional|P(?:aramIs(?:Reference|Target)|lural|ropertyIsReference)|Re(?:adWrite|plyIsReference)|TightBindingFunction|listOfItems)|n(?:d(?:erline|o)|knownSource)|p(?:date)?|se(?:HTTPProxyAttr|RelativeIterators|S(?:ocksAttr|tandardDispatch)|rTerminology))|Vi(?:ewsFontChanged|rtualKey)|W(?:a(?:itReply|keUpEvent|ntReceipt)|h(?:iteSpace(?:ConsiderMask|IgnoreMask)?|oleWordEquals)|indowClass)|XMLRPCScheme|Yes|Z(?:enkakuHankaku|oom(?:In|Out)?))|FP(?:ExtendedFlagsAlternateAddressMask|ServerIcon|Tag(?:Length(?:DDP|IP(?:Port)?)|Type(?:D(?:DP|NS)|IP(?:Port)?)))|H(?:Intern(?:alErr|etConfigPrefErr)|TOCType(?:Developer|User))|LM(?:D(?:eferSwitchErr|uplicateModuleErr)|GroupNotFoundErr|In(?:stallationErr|ternalErr)|Location(?:NotFoundErr|sFolderType)|Module(?:CommunicationErr|sFolderType)|NoSuchModuleErr|PreferencesFolderType|RebootFlagsLevelErr)|NKRCurrentVersion|RM(?:M(?:ountVol|ultVols)|NoUI|Search(?:More|RelFirst)?|TryFileIDFirst)|S(?:A(?:dd|nd|ppleScriptSuite)|C(?:o(?:m(?:es(?:After|Before)|ment(?:Event)?)|n(?:catenate|siderReplies(?:ConsiderMask|IgnoreMask)?|tains))|urrentApplication)|D(?:efault(?:M(?:ax(?:HeapSize|StackSize)|in(?:HeapSize|StackSize))|Preferred(?:HeapSize|StackSize))|ivide)|E(?:ndsWith|qual|rrorEventCode|xcluding)|GreaterThan(?:OrEqual)?|HasOpenHandler|I(?:mporting|nitializeEventCode)|L(?:aunchEvent|essThan(?:OrEqual)?)|M(?:agic(?:EndTellEvent|TellEvent)|inimumVersion|ultiply)|N(?:egate|ot(?:Equal)?|um(?:berOfSourceStyles|ericStrings(?:ConsiderMask|IgnoreMask)?))|Or|P(?:ower|repositionalSubroutine)|Quotient|Remainder|S(?:criptEditorSuite|elect(?:CopySourceAttributes|Get(?:AppTerminology(?:Obsolete)?|Handler(?:Names|Obsolete)?|Property(?:Names|Obsolete)?|S(?:ourceStyle(?:Names|s)|ysTerminology))|Init|Set(?:Handler(?:Obsolete)?|Property(?:Obsolete)?|Source(?:Attributes|Styles)))|ourceStyle(?:ApplicationKeyword|C(?:lass|omment)|Dynamic(?:Class|E(?:numValue|ventName)|P(?:arameterName|roperty))|E(?:numValue|ventName)|L(?:anguageKeyword|iteral)|NormalText|ObjectSpecifier|P(?:arameterName|roperty)|String|U(?:ncompiledText|serSymbol))|t(?:art(?:LogEvent|sWith)|opLogEvent)|ub(?:routineEvent|tract))|TypeNamesSuite|UseEventCode)|TS(?:BoldQDStretch|CubicCurveType|DeletedGlyphcode|F(?:ileReferenceFilterSelector|lat(?:DataUstl(?:CurrentVersion|Version(?:0|1|2))|tenedFontSpecifierRawNameData)|ont(?:AutoActivation(?:Ask|D(?:efault|isabled)|Enabled)|Cont(?:ainerRefUnspecified|ext(?:Global|Local|Unspecified))|F(?:amilyRefUnspecified|ilter(?:CurrentVersion|Selector(?:Font(?:ApplierFunction|Family(?:ApplierFunction)?)|Generation|Unspecified))|ormatUnspecified)|Notify(?:Action(?:DirectoriesChanged|FontsChanged)|Option(?:Default|ReceiveWhileSuspended))|RefUnspecified))|G(?:enerationUnspecified|lyphInfo(?:AppleReserved|ByteSizeMask|HasImposedWidth|Is(?:Attachment|LTHanger|RBHanger|WhiteSpace)|TerminatorGlyph))|I(?:nvalid(?:Font(?:Access|ContainerAccess|FamilyAccess|TableAccess)|GlyphAccess)|t(?:alicQDSkew|eration(?:Completed|ScopeModified)))|Line(?:Appl(?:eReserved|yAntiAliasing)|BreakToNearestCharacter|Disable(?:A(?:ll(?:BaselineAdjustments|GlyphMorphing|Justification|KerningAdjustments|LayoutOperations|TrackingAdjustments)|utoAdjustDisplayPos)|NegativeJustification)|F(?:illOutToWidth|ractDisable)|HasNo(?:Hangers|OpticalAlignment)|I(?:gnoreFontLeading|mposeNoAngleForEnds|sDisplayOnly)|KeepSpacesOutOfMargin|LastNoJustification|No(?:AntiAliasing|LayoutOptions|SpecialJustification)|TabAdjustEnabled|Use(?:DeviceMetrics|QDRendering))|NoTracking|O(?:ptionFlags(?:ActivateDisabled|ComposeFontPostScriptName|D(?:efault(?:Scope)?|oNotNotify)|I(?:ncludeDisabledMask|terat(?:eByPrecedenceMask|ionScopeMask))|ProcessSubdirectories|Re(?:cordPersistently|strictedScope)|U(?:nRestrictedScope|se(?:DataFork(?:AsResourceFork)?|ResourceFork)))|therCurveType)|Qu(?:adCurveType|eryActivateFontMessage)|RadiansFactor|Style(?:Appl(?:eReserved|y(?:AntiAliasing|Hints))|No(?:AntiAliasing|Hinting|Options))|U(?:A(?:fterWithStreamShiftTag|scentTag)|B(?:a(?:ckgroundC(?:allback|olor)|dStreamErr|selineClassTag)|eforeWithStreamShiftTag|usyObjectErr|y(?:C(?:haracter(?:Cluster)?|luster)|TypographicCluster|Word))|C(?:GContextTag|enterTab|learAll|o(?:lorTag|ordinateOverflowErr)|rossStreamShiftTag)|D(?:ataStreamUnicodeStyledText|e(?:c(?:imalTab|ompositionFactorTag)|faultFontFallbacks|scentTag)|irectData(?:AdvanceDeltaFixedArray|BaselineDeltaFixedArray|DeviceDeltaSInt16Array|LayoutRecordATSLayoutRecord(?:Current|Version1)|Style(?:IndexUInt16Array|SettingATSUStyleSettingRefArray)))|F(?:lattenOptionNoOptionsMask|o(?:nt(?:MatrixTag|Tag|s(?:Matched|NotMatched))|rceHangingTag)|rom(?:FollowingLayout|PreviousLayout|TextBeginning))|GlyphSelectorTag|HangingInhibitFactorTag|I(?:mposeWidthTag|nvalid(?:Attribute(?:SizeErr|TagErr|ValueErr)|Ca(?:cheErr|llInsideCallbackErr)|Font(?:Err|FallbacksErr|ID)|StyleErr|Text(?:LayoutErr|RangeErr)))|KerningInhibitFactorTag|L(?:a(?:ng(?:RegionTag|uageTag)|st(?:Err|ResortOnlyFallback)|youtOperation(?:AppleReserved|BaselineAdjustment|CallbackStatus(?:Continue|Handled)|Justification|KerningAdjustment|Morph|None|OverrideTag|PostLayoutAdjustment|TrackingAdjustment))|e(?:adingTag|ftT(?:ab|oRightBaseDirection))|ine(?:AscentTag|B(?:aselineValuesTag|reakInWord)|D(?:e(?:cimalTabCharacterTag|scentTag)|irectionTag)|F(?:lushFactorTag|ontFallbacksTag)|HighlightCGColorTag|JustificationFactorTag|La(?:ng(?:RegionTag|uageTag)|youtOptionsTag)|RotationTag|T(?:extLocatorTag|runcationTag)|WidthTag)|owLevelErr)|Max(?:ATSUITagValue|LineTag|StyleTag)|N(?:o(?:C(?:aretAngleTag|orrespondingFontErr)|Font(?:CmapAvailableErr|NameErr|ScalerAvailableErr)|LigatureSplitTag|OpticalAlignmentTag|S(?:elector|pecialJustificationTag|tyleRunsAssignedErr)|tSetErr)|umberTabTypes)|OutputBufferTooSmallErr|PriorityJustOverrideTag|Q(?:D(?:BoldfaceTag|CondensedTag|ExtendedTag|ItalicTag|UnderlineTag)|uickDrawTextErr)|R(?:GBAlphaColorTag|ightT(?:ab|oLeftBaseDirection))|S(?:equentialFallbacks(?:Exclusive|Preferred)|izeTag|t(?:rongly(?:Horizontal|Vertical)|yle(?:Contain(?:edBy|s)|D(?:oubleLineCount|ropShadow(?:BlurOptionTag|ColorOptionTag|OffsetOptionTag|Tag))|Equals|RenderingOptionsTag|S(?:ingleLineCount|trikeThrough(?:Co(?:lorOptionTag|untOptionTag)|Tag))|TextLocatorTag|Un(?:derlineCo(?:lorOptionTag|untOptionTag)|equal)))|uppressCrossKerningTag)|T(?:oTextEnd|r(?:ackingTag|unc(?:FeatNoSquishing|ate(?:End|Middle|None|S(?:pecificationMask|tart)))))|U(?:n(?:FlattenOptionNoOptionsMask|supportedStreamFormatErr)|se(?:GrafPortPenLoc|LineControlWidth))|VerticalCharacterTag|se(?:CaretOrigins|DeviceOrigins|FractionalOrigins|GlyphAdvance|LineHeight|OriginFlags)))|U(?:Gr(?:aphErr_(?:CannotDoInCurrentContext|Invalid(?:AudioUnit|Connection)|NodeNotFound|OutputNodeErr)|oupParameterID_(?:All(?:NotesOff|SoundOff)|ChannelPressure|DataEntry(?:_LSB)?|Expression(?:_LSB)?|Foot(?:_LSB)?|KeyPressure(?:_(?:FirstKey|LastKey))?|ModWheel(?:_LSB)?|P(?:an(?:_LSB)?|itchBend)|ResetAllControllers|S(?:ostenuto|ustain)|Volume(?:_LSB)?))|LowShelfParam_(?:CutoffFrequency|Gain)|MIDISynthProperty_EnablePreload|N(?:BandEQ(?:FilterType_(?:2ndOrderButterworth(?:HighPass|LowPass)|Band(?:Pass|Stop)|HighShelf|LowShelf|Parametric|Resonant(?:High(?:Pass|Shelf)|Low(?:Pass|Shelf)))|P(?:aram_(?:B(?:andwidth|ypassBand)|F(?:ilterType|requency)|G(?:ain|lobalGain))|roperty_(?:BiquadCoefficients|MaxNumberOfBands|NumberOfBands)))|et(?:ReceiveP(?:aram_(?:NumParameters|Status)|roperty_(?:Hostname|Password))|S(?:end(?:NumPresetFormats|P(?:aram_(?:NumParameters|Status)|r(?:esetFormat_(?:AAC_(?:128kbpspc|32kbpspc|4(?:0kbpspc|8kbpspc)|64kbpspc|80kbpspc|96kbpspc|LD_(?:32kbpspc|4(?:0kbpspc|8kbpspc)|64kbpspc))|IMA4|Lossless(?:16|24)|PCM(?:Float32|Int(?:16|24))|ULaw)|operty_(?:Disconnect|P(?:assword|ortNum)|ServiceName|TransmissionFormat(?:Index)?))))|tatus_(?:Connect(?:ed|ing)|Listening|NotConnected|Overflow|Underflow)))|odeInteraction_(?:Connection|InputCallback))|Parameter(?:Listener_AnyParameter|MIDIMapping_(?:Any(?:ChannelFlag|NoteFlag)|Bipolar(?:_On)?|SubRange|Toggle))|Sampler(?:P(?:aram_(?:CoarseTuning|FineTuning|Gain|Pan)|roperty_(?:BankAndPreset|Load(?:AudioFiles|Instrument|PresetFromBank)))|_Default(?:BankLSB|MelodicBankMSB|PercussionBankMSB))|VoiceIO(?:Err_UnexpectedNumberOfInputChannels|Property_(?:BypassVoiceProcessing|MuteOutput|VoiceProcessingEnableAGC)))|VL(?:I(?:nOrder|s(?:Le(?:af|ftBranch)|RightBranch|Tree))|NullNode|P(?:ostOrder|reOrder))|X(?:CopyMultipleAttributeOptionStopOnError|Error(?:A(?:PIDisabled|ctionUnsupported|ttributeUnsupported)|CannotComplete|Failure|I(?:llegalArgument|nvalidUIElement(?:Observer)?)|No(?:Value|t(?:EnoughPrecision|Implemented|ification(?:AlreadyRegistered|NotRegistered|Unsupported)))|ParameterizedAttributeUnsupported|Success)|MenuItemModifier(?:Control|No(?:Command|ne)|Option|Shift)|UnderlineStyle(?:Double|None|Single|Thick))|bbrevSquaredLigaturesO(?:ffSelector|nSelector)|c(?:c(?:essException|ountKCItemAttr)|tivateAnd(?:HandleClick|IgnoreClick))|dd(?:KCEvent(?:Mask)?|ressKCItemAttr)|l(?:ert(?:Caution(?:Alert|BadgeIcon|Icon)|Default(?:CancelText|O(?:KText|therText))|Flags(?:AlertIsMovable|Use(?:Co(?:mpositing|ntrolHierarchy)|Theme(?:Background|Controls)))|Note(?:Alert|Icon)|PlainAlert|St(?:dAlert(?:CancelButton|HelpButton|O(?:KButton|therButton))|op(?:Alert|Icon))|VariantCode|WindowClass)|i(?:asBadgeIcon|gn(?:AbsoluteCenter|Bottom(?:Left|Right)?|Center(?:Bottom|Left|Right|Top)|HorizontalCenter|Left|None|Right|Top(?:Left|Right)?|VerticalCenter))|l(?:CapsSelector|LowerCaseSelector|PPDDomains|Typ(?:eFeaturesO(?:ffSelector|nSelector)|ographicFeaturesType)|WindowClasses)|readySavedStateErr|t(?:HalfWidthTextSelector|P(?:lainWindowClass|roportionalTextSelector)|ernate(?:HorizKanaO(?:ffSelector|nSelector)|KanaType|VertKanaO(?:ffSelector|nSelector)))|ways(?:Authenticate|SendSubject))|n(?:dConnections|notationType|y(?:AuthType|Component(?:FlagsMask|Manufacturer|SubType|Type)|P(?:ort|rotocol)|TransactionID))|pp(?:PackageAliasType|earance(?:EventClass|Folder(?:Icon|Type)|Part(?:DownButton|Indicator|LeftButton|Meta(?:Disabled|Inactive|None)|Page(?:DownArea|LeftArea|RightArea|UpArea)|RightButton|UpButton)|Region(?:C(?:loseBox|o(?:llapseBox|ntent))|Drag|Grow|Structure|T(?:itle(?:Bar|ProxyIcon|Text)|oolbarButton)|ZoomBox))|l(?:e(?:ExtrasFolder(?:Icon|Type)|JapaneseDictionarySignature|Lo(?:go(?:CharCode|Icon|Unicode)|sslessFormatFlag_(?:16BitSourceData|2(?:0BitSourceData|4BitSourceData)|32BitSourceData))|M(?:anufacturer|enu(?:Folder(?:AliasType|Icon(?:Resource)?|Type)|Icon))|S(?:cript(?:BadgeIcon|Subtype)|hare(?:AuthenticationFolderType|PasswordKCItemClass|SupportFolderType))|Talk(?:Icon|ZoneIcon)|shareAutomountServerAliasesFolderType)|ication(?:AliasType|CPAliasType|DAAliasType|SupportFolder(?:Icon|Type)|ThreadID|WindowKind|sFolder(?:Icon|Type))))|s(?:sistantsFolder(?:Icon|Type)|teriskToMultiplyO(?:ffSelector|nSelector)|ync(?:Eject(?:Complete|InProgress)|Mount(?:Complete|InProgress)|Unmount(?:Complete|InProgress)))|t(?:SpecifiedOrigin|temptDupCardEntryErr)|u(?:dio(?:A(?:ggregateDevice(?:ClassID|Property(?:ActiveSubDeviceList|C(?:lockDevice|omposition)|FullSubDeviceList|MasterSubDevice))|lertSoundsFolderType)|B(?:alanceFadeType_(?:EqualPower|MaxUnityGain)|o(?:o(?:leanControl(?:ClassID|PropertyValue)|tChimeVolumeControlClassID)|x(?:ClassID|Property(?:Acqui(?:red|sitionFailed)|BoxUID|ClockDeviceList|DeviceList|Has(?:Audio|MIDI|Video)|IsProtected|TransportType))))|C(?:hannel(?:Bit_(?:Center(?:Surround|Top(?:Front|Middle|Rear))?|L(?:FEScreen|eft(?:Center|Surround(?:Direct)?|Top(?:Front|Middle|Rear))?)|Right(?:Center|Surround(?:Direct)?|Top(?:Front|Middle|Rear))?|Top(?:Back(?:Center|Left|Right)|CenterSurround)|VerticalHeight(?:Center|Left|Right))|Coordinates_(?:Azimuth|BackFront|D(?:istance|ownUp)|Elevation|LeftRight)|Flags_(?:AllOff|Meters|RectangularCoordinates|SphericalCoordinates)|La(?:bel_(?:Ambisonic_(?:W|X|Y|Z)|B(?:eginReserved|inaural(?:Left|Right))|C(?:enter(?:Surround(?:Direct)?|Top(?:Front|Middle|Rear))?|lickTrack)|Di(?:alogCentricMix|screte(?:_(?:0|1(?:0|1|2|3|4|5)?|2|3|4|5|6(?:5535)?|7|8|9))?)|EndReserved|ForeignLanguage|H(?:OA_ACN(?:_(?:0|1(?:0|1|2|3|4|5)?|2|3|4|5|6(?:5024)?|7|8|9))?|aptic|ea(?:dphones(?:Left|Right)|ringImpaired))|L(?:FE(?:2|Screen)|eft(?:Center|Surround(?:Direct)?|To(?:p(?:Front|Middle|Rear)|tal)|Wide)?)|M(?:S_(?:Mid|Side)|ono)|Narration|R(?:earSurround(?:Left|Right)|ight(?:Center|Surround(?:Direct)?|To(?:p(?:Front|Middle|Rear)|tal)|Wide)?)|Top(?:Back(?:Center|Left|Right)|CenterSurround)|U(?:n(?:known|used)|seCoordinates)|VerticalHeight(?:Center|Left|Right)|XY_(?:X|Y))|youtTag_(?:A(?:AC_(?:3_0|4_0|5_(?:0|1)|6_(?:0|1)|7_(?:0|1(?:_(?:B|C))?)|Octagonal|Quadraphonic)|C3_(?:1_0_1|2_1_1|3_(?:0(?:_1)?|1(?:_1)?))|mbisonic_B_Format|tmos_(?:5_1_2|7_1_4|9_1_6)|udioUnit_(?:4|5(?:_(?:0|1))?|6(?:_(?:0|1))?|7_(?:0(?:_Front)?|1(?:_Front)?)|8))|B(?:eginReserved|inaural)|Cube|D(?:TS_(?:3_1|4_1|6_(?:0_(?:A|B|C)|1_(?:A|B|C|D))|7_(?:0|1)|8_(?:0_(?:A|B)|1_(?:A|B)))|VD_(?:0|1(?:0|1|2|3|4|5|6|7|8|9)?|2(?:0)?|3|4|5|6|7|8|9)|iscreteInOrder)|E(?:AC(?:3_(?:6_1_(?:A|B|C)|7_1_(?:A|B|C|D|E|F|G|H))|_(?:6_0_A|7_0_A))|magic_Default_7_1|ndReserved)|H(?:OA_ACN_(?:N3D|SN3D)|exagonal)|ITU_(?:1_0|2_(?:0|1|2)|3_(?:0|1|2(?:_1)?|4_1))|M(?:PEG_(?:1_0|2_0|3_0_(?:A|B)|4_0_(?:A|B)|5_(?:0_(?:A|B|C|D)|1_(?:A|B|C|D))|6_1_A|7_1_(?:A|B|C))|atrixStereo|idSide|ono)|Octagonal|Pentagonal|Quadraphonic|S(?:MPTE_DTV|tereo(?:Headphones)?)|TMH_10_2_(?:full|std)|U(?:nknown|seChannel(?:Bitmap|Descriptions))|WAVE_(?:2_1|3_0|4_0_(?:A|B)|5_(?:0_(?:A|B)|1_(?:A|B))|6_1|7_1)|XY)))|l(?:ipLightControlClassID|ock(?:Device(?:ClassID|Property(?:AvailableNominalSampleRates|C(?:lockDomain|ontrolList)|Device(?:Is(?:Alive|Running)|UID)|Latency|NominalSampleRate|TransportType))|Source(?:Control(?:ClassID|PropertyItemKind)|ItemKindInternal)))|o(?:dec(?:AppendInput(?:BufferListSelect|DataSelect)|B(?:ad(?:DataError|PropertySizeError)|itRate(?:ControlMode_(?:Constant|LongTermAverage|Variable(?:Constrained)?)|Format(?:_(?:ABR|CBR|VBR))?))|D(?:elayMode_(?:Compatibility|Minimum|Optimal)|oesSampleRateConversion)|ExtendFrequencies|GetProperty(?:InfoSelect|Select)|I(?:llegalOperationError|n(?:itializeSelect|putFormatsForOutputFormat))|No(?:Error|tEnoughBufferSpaceError)|Output(?:FormatsForInputFormat|Precedence(?:BitRate|None|SampleRate)?)|Pr(?:imeMethod_(?:No(?:ne|rmal)|Pre)|o(?:duceOutput(?:BufferListSelect|DataSelect|Packet(?:AtEOF|Failure|NeedsMoreInputData|Success(?:HasMore)?))|perty(?:A(?:djustLocalQuality|pplicable(?:BitRateRange|InputSampleRates|OutputSampleRates)|vailable(?:BitRate(?:Range|s)|Input(?:ChannelLayout(?:Tags|s)|SampleRates)|NumberChannels|Output(?:ChannelLayout(?:Tags|s)|SampleRates)))|BitRate(?:ControlMode|ForVBR)|Current(?:Input(?:ChannelLayout|Format|SampleRate)|Output(?:ChannelLayout|Format|SampleRate)|TargetBitRate)|D(?:elayMode|oesSampleRateConversion|ynamicRangeControlMode)|EmploysDependentPackets|Format(?:CFString|Info|List)|HasVariablePacketByteSizes|I(?:nput(?:BufferSize|ChannelLayout|FormatsForOutputFormat)|sInitialized)|M(?:a(?:gicCookie|nufacturerCFString|ximumPacketByteSize)|inimum(?:DelayMode|Number(?:InputPackets|OutputPackets)))|NameCFString|Output(?:ChannelLayout|FormatsForInputFormat)|P(?:a(?:cket(?:FrameSize|SizeLimitForVBR)|ddedZeros)|r(?:ime(?:Info|Method)|ogramTargetLevel(?:Constant)?))|QualitySetting|Re(?:commendedBitRateRange|quiresPacketDescription)|S(?:ettings|oundQualityForVBR|upported(?:InputFormats|OutputFormats))|UsedInputBufferSize|ZeroFramesPadded)))|Quality_(?:High|Low|M(?:ax|edium|in))|ResetSelect|S(?:etPropertySelect|tateError)|U(?:n(?:initializeSelect|knownPropertyError|s(?:pecifiedError|upportedFormatError))|seRecommendedSampleRate))|mponent(?:Err_Instance(?:Invalidated|TimedOut)|Flag_Unsearchable|ValidationResult_(?:Failed|Passed|TimedOut|Un(?:authorizedError_(?:Init|Open)|known))|sFolderType)|n(?:trol(?:ClassID|Property(?:Element|Scope|Variant))|verter(?:A(?:pplicableEncode(?:BitRates|SampleRates)|vailableEncode(?:BitRates|ChannelLayoutTags|SampleRates))|C(?:hannelMap|o(?:decQuality|mpressionMagicCookie)|urrent(?:InputStreamDescription|OutputStreamDescription))|DecompressionMagicCookie|E(?:ncode(?:AdjustableSampleRate|BitRate)|rr_(?:BadPropertySizeError|FormatNotSupported|In(?:putSampleRateOutOfRange|valid(?:InputSize|OutputSize))|O(?:perationNotSupported|utputSampleRateOutOfRange)|PropertyNotSupported|RequiresPacketDescriptionsError|UnspecifiedError))|InputChannelLayout|OutputChannelLayout|Pr(?:ime(?:Info|Method)|operty(?:BitDepthHint|Calculate(?:InputBufferSize|OutputBufferSize)|Dither(?:BitDepth|ing)|FormatList|InputCodecParameters|M(?:aximum(?:Input(?:BufferSize|PacketSize)|OutputPacketSize)|inimum(?:InputBufferSize|OutputBufferSize))|OutputCodecParameters|Settings))|Quality_(?:High|Low|M(?:ax|edium|in))|SampleRateConverter(?:Algorithm|Complexity(?:_(?:Linear|M(?:astering|inimumPhase)|Normal))?|InitialPhase|Quality)))))|D(?:ata(?:DestinationControlClassID|SourceControlClassID)|e(?:coderComponentType|vice(?:ClassID|P(?:ermissionsError|ro(?:cessorOverload|perty(?:A(?:ctualSampleRate|vailableNominalSampleRates)|Buffer(?:FrameSize(?:Range)?|Size(?:Range)?)|C(?:hannel(?:CategoryName(?:CFString)?|N(?:ame(?:CFString)?|ominalLineLevel(?:NameForID(?:CFString)?|s)?|umberName(?:CFString)?))|l(?:ipLight|ock(?:D(?:evice|omain)|Source(?:KindForID|NameForID(?:CFString)?|s)?))|onfigurationApplication)|D(?:ataSource(?:KindForID|NameForID(?:CFString)?|s)?|evice(?:CanBeDefault(?:Device|SystemDevice)|HasChanged|Is(?:Alive|Running(?:Somewhere)?)|Manufacturer(?:CFString)?|Name(?:CFString)?|UID)|riverShouldOwniSub)|H(?:ighPassFilterSetting(?:NameForID(?:CFString)?|s)?|ogMode)|I(?:O(?:CycleUsage|ProcStreamUsage|StoppedAbnormally)|con|sHidden)|JackIsConnected|L(?:atency|istenback)|M(?:odelUID|ute)|NominalSampleRate|P(?:ha(?:ntomPower|seInvert)|l(?:ayThru(?:Destination(?:NameForID(?:CFString)?|s)?|S(?:olo|tereoPan(?:Channels)?)|Volume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?))?|ugIn)|referredChannel(?:Layout|sForStereo))|Re(?:gisterBufferList|latedDevices)|S(?:afetyOffset|cope(?:Input|Output|PlayThrough)|olo|t(?:ereoPan(?:Channels)?|ream(?:Configuration|Format(?:Match|Supported|s)?|s))|u(?:b(?:Mute|Volume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?))|pportsMixing))|T(?:alkback|ransportType)|UsesVariableBufferFrameSizes|Volume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?))))|StartTime(?:DontConsult(?:DeviceFlag|HALFlag)|IsInputFlag)|TransportType(?:A(?:VB|ggregate|irPlay|utoAggregate)|B(?:luetooth(?:LE)?|uiltIn)|DisplayPort|FireWire|HDMI|PCI|Thunderbolt|U(?:SB|nknown)|Virtual)|Un(?:known|supportedFormatError)))|igidesignFolderType)|En(?:coderComponentType|dPoint(?:ClassID|Device(?:ClassID|Property(?:Composition|EndPointList|IsPrivate))))|F(?:ile(?:3GP(?:2Type|Type)|A(?:AC_ADTSType|C3Type|IF(?:CType|FType)|MRType)|BadPropertySizeError|C(?:AFType|loseSelect|o(?:mponent_(?:Available(?:FormatIDs|StreamDescriptionsForFormat)|Can(?:Read|Write)|ExtensionsForType|F(?:astDispatchTable|ileTypeName)|HFSTypeCodesForType|MIMETypesForType|UTIsForType)|untUserDataSelect)|reate(?:Select|URLSelect))|D(?:ataIsThisFormatSelect|oesNotAllow64BitDataSizeError)|E(?:ndOfFileError|xtensionIsThisFormatSelect)|F(?:LACType|ile(?:DataIsThisFormatSelect|IsThisFormatSelect|NotFoundError)|lags_(?:DontPageAlignAudioData|EraseFile))|G(?:et(?:GlobalInfoS(?:elect|izeSelect)|Property(?:InfoSelect|Select)|UserDataS(?:elect|izeSelect))|lobalInfo_(?:A(?:ll(?:Extensions|HFSTypeCodes|MIMETypes|UTIs)|vailable(?:FormatIDs|StreamDescriptionsForFormat))|ExtensionsForType|FileTypeName|HFSTypeCodesForType|MIMETypesForType|ReadableTypes|TypesFor(?:Extension|HFSTypeCode|MIMEType|UTI)|UTIsForType|WritableTypes))|In(?:itialize(?:Select|WithCallbacksSelect)|valid(?:ChunkError|FileError|Packet(?:DependencyError|OffsetError)))|L(?:ATMInLOASType|oopDirection_(?:Backward|Forward(?:AndBackward)?|NoLooping))|M(?:4(?:AType|BType)|P(?:1Type|2Type|3Type|EG4Type)|arkerType_Generic)|N(?:extType|otOp(?:enError|timizedError))|Op(?:e(?:n(?:Select|URLSelect|WithCallbacksSelect)|rationNotSupportedError)|timizeSelect)|P(?:ermissionsError|ositionError|roperty(?:A(?:lbumArtwork|udio(?:Data(?:ByteCount|PacketCount)|TrackCount))|B(?:itRate|yteToPacket)|Ch(?:annelLayout|unkIDs)|D(?:ata(?:Format(?:Name)?|Offset)|eferSizeUpdates)|EstimatedDuration|F(?:ileFormat|ormatList|rameToPacket)|I(?:D3Tag|nfoDictionary|sOptimized)|Ma(?:gicCookieData|rkerList|ximumPacketSize)|NextIndependentPacket|P(?:acket(?:RangeByteCountUpperBound|SizeUpperBound|T(?:ableInfo|o(?:Byte|DependencyInfo|Frame|RollDistance)))|reviousIndependentPacket)|Re(?:gionList|s(?:erveDuration|trictsRandomAccess))|SourceBitDepth|UseAudioTrack))|R(?:F64Type|e(?:ad(?:BytesSelect|P(?:acket(?:DataSelect|sSelect)|ermission)|WritePermission)|gionFlag_(?:LoopEnable|Play(?:Backward|Forward))|moveUserDataSelect))|S(?:et(?:PropertySelect|UserDataSelect)|oundDesigner2Type|tream(?:Error_(?:BadPropertySize|D(?:ataUnavailable|iscontinuityCantRecover)|I(?:llegalOperation|nvalid(?:File|PacketOffset))|NotOptimized|Uns(?:pecifiedError|upported(?:DataFormat|FileType|Property))|ValueUnknown)|P(?:arseFlag_Discontinuity|roperty(?:Flag_(?:CacheProperty|PropertyIsCached)|_(?:A(?:udioData(?:ByteCount|PacketCount)|verageBytesPerPacket)|B(?:itRate|yteToPacket)|ChannelLayout|Data(?:Format|Offset)|F(?:ileFormat|ormatList|rameToPacket)|InfoDictionary|Ma(?:gicCookieData|ximumPacketSize)|NextIndependentPacket|P(?:acket(?:SizeUpperBound|T(?:ableInfo|o(?:Byte|DependencyInfo|Frame|RollDistance)))|reviousIndependentPacket)|Re(?:adyToProducePackets|strictsRandomAccess))))|SeekFlag_OffsetIsEstimated))|Uns(?:pecifiedError|upported(?:DataFormatError|FileTypeError|PropertyError))|W(?:AVEType|rite(?:BytesSelect|P(?:acketsSelect|ermission))))|ormat(?:60958AC3|A(?:C3|ES3|Law|MR(?:_WB)?|pple(?:IMA4|Lossless)|udible)|Bad(?:PropertySizeError|SpecifierSizeError)|DVIIntelIMA|EnhancedAC3|F(?:LAC|lag(?:Is(?:AlignedHigh|BigEndian|Float|Non(?:Interleaved|Mixable)|Packed|SignedInteger)|s(?:A(?:reAllClear|udioUnitCanonical)|Canonical|Native(?:Endian|FloatPacked))))|LinearPCM|M(?:ACE(?:3|6)|IDIStream|PEG(?:4(?:AAC(?:_(?:ELD(?:_(?:SBR|V2))?|HE(?:_V2)?|LD|Spatial))?|CELP|HVXC|TwinVQ)|D_USAC|Layer(?:1|2|3))|icrosoftGSM)|Opus|P(?:arameterValueStream|roperty_(?:A(?:SBDFrom(?:ESDS|MPEGPacket)|reChannelLayoutsEquivalent|vailableEncode(?:BitRates|ChannelLayoutTags|NumberChannels|SampleRates))|B(?:alanceFade|itmapForLayoutTag)|Channel(?:Layout(?:F(?:or(?:Bitmap|Tag)|romESDS)|Hash|Name|SimpleName)|Map|Name|ShortName)|Decode(?:FormatIDs|rs)|Encode(?:FormatIDs|rs)|F(?:irstPlayableFormatFromList|ormat(?:EmploysDependentPackets|I(?:nfo|s(?:E(?:ncrypted|xternallyFramed)|VBR))|List|Name))|ID3Tag(?:Size|ToDictionary)|MatrixMixMap|NumberOfChannelsForLayout|OutputFormatList|PanningMatrix|Tag(?:ForChannelLayout|sForNumberOfChannels)|ValidateChannelLayout))|Q(?:Design(?:2)?|UALCOMM)|TimeCode|U(?:Law|n(?:knownFormatError|s(?:pecifiedError|upported(?:DataFormatError|PropertyError))))|iLBC))|H(?:ardware(?:Bad(?:DeviceError|ObjectError|PropertySizeError|StreamError)|IllegalOperationError|No(?:Error|tRunningError)|P(?:owerHint(?:FavorSavingPower|None)|roperty(?:Bo(?:otChimeVolume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?)|xList)|ClockDeviceList|De(?:fault(?:InputDevice|OutputDevice|SystemOutputDevice)|vice(?:ForUID|s))|HogModeIsAllowed|IsInitingOrExiting|MixStereoToMono|P(?:lugIn(?:ForBundleID|List)|owerHint|rocessIs(?:Audible|Master))|RunLoop|S(?:erviceRestarted|leepingIsAllowed)|Trans(?:late(?:BundleIDTo(?:PlugIn|TransportManager)|UIDTo(?:Box|ClockDevice|Device))|portManagerList)|U(?:nloadingIsAllowed|ser(?:IDChanged|SessionIsActiveOrHeadless))))|Service(?:DeviceProperty_VirtualMaster(?:Balance|Volume)|Property_ServiceRestarted)|Un(?:knownPropertyError|s(?:pecifiedError|upportedOperationError)))|ighPassFilterControlClassID)|ISubOwnerControlClassID|JackControlClassID|L(?:FE(?:MuteControlClassID|VolumeControlClassID)|evelControl(?:ClassID|Property(?:Convert(?:DecibelsToScalar|ScalarToDecibels)|Decibel(?:Range|Value|sToScalarTransferFunction)|ScalarValue)|TranferFunction(?:1(?:0Over1|1Over1|2Over1|Over(?:2|3))|2Over1|3Over(?:1|2|4)|4Over1|5Over1|6Over1|7Over1|8Over1|9Over1|Linear))|i(?:neLevelControlClassID|stenbackControlClassID))|MuteControlClassID|O(?:bject(?:ClassID(?:Wildcard)?|Property(?:BaseClass|C(?:lass|ontrolList|reator)|Element(?:CategoryName|Master|N(?:ame|umberName)|Wildcard)|FirmwareVersion|Identify|Listener(?:Added|Removed)|M(?:anufacturer|odelName)|Name|Owne(?:dObjects|r)|S(?:cope(?:Global|Input|Output|PlayThrough|Wildcard)|e(?:lectorWildcard|rialNumber)))|SystemObject|Unknown)|fflineUnit(?:Property_(?:InputSize|OutputSize)|RenderAction_(?:Complete|Preflight|Render))|utputUnit(?:Property_(?:C(?:hannelMap|urrentDevice)|EnableIO|HasIO|IsRunning|S(?:etInputCallback|tartTime(?:stampsAtZero)?))|Range|St(?:artSelect|opSelect)))|P(?:ha(?:ntomPowerControlClassID|seInvertControlClassID)|lugIn(?:C(?:lassID|reateAggregateDevice)|DestroyAggregateDevice|Property(?:B(?:oxList|undleID)|ClockDeviceList|DeviceList|TranslateUIDTo(?:Box|ClockDevice|Device))|sFolderType)|r(?:esetsFolderType|opertyWildcard(?:Channel|PropertyID|Section)))|Queue(?:DeviceProperty_(?:NumberChannels|SampleRate)|Err_(?:Buffer(?:E(?:mpty|nqueuedTwice)|InQueue)|C(?:annotStart(?:Yet)?|odecNotFound)|DisposalPending|EnqueueDuringReset|Invalid(?:Buffer|CodecAccess|Device|OfflineMode|P(?:arameter|roperty(?:Size|Value)?)|QueueType|RunState|Tap(?:Context|Type))|P(?:ermissions|rimeTimedOut)|QueueInvalidated|RecordUnderrun|TooManyTaps)|P(?:aram_(?:P(?:an|itch|layRate)|Volume(?:RampTime)?)|ro(?:cessingTap_(?:EndOfStream|P(?:ostEffects|reEffects)|S(?:iphon|tartOfStream))|perty_(?:C(?:hannelLayout|onverterError|urrent(?:Device|LevelMeter(?:DB)?))|DecodeBufferSizeFrames|Enable(?:LevelMetering|TimePitch)|IsRunning|Ma(?:gicCookie|ximumOutputPacketSize)|StreamDescription|TimePitch(?:Algorithm|Bypass))))|TimePitchAlgorithm_(?:Spectral|TimeDomain|Varispeed))|S(?:e(?:lectorControl(?:ClassID|ItemKindSpacer|Property(?:AvailableItems|CurrentItem|Item(?:Kind|Name)))|rvices(?:Bad(?:PropertySizeError|SpecifierSizeError)|NoError|Property(?:CompletePlaybackIfAppDies|IsUISound)|SystemSound(?:ClientTimedOutError|ExceededMaximumDurationError|UnspecifiedError)|UnsupportedPropertyError)|ttingsFlags_(?:ExpertParameter|InvisibleParameter|MetaParameter|UserInterfaceParameter))|liderControl(?:ClassID|Property(?:Range|Value))|o(?:loControlClassID|und(?:BanksFolderType|sFolderType))|t(?:ereoPanControl(?:ClassID|Property(?:PanningChannels|Value))|ream(?:ClassID|Property(?:Available(?:PhysicalFormats|VirtualFormats)|Direction|IsActive|Latency|OwningDevice|PhysicalFormat(?:Match|Supported|s)?|StartingChannel|TerminalType|VirtualFormat)|TerminalType(?:Di(?:gitalAudioInterface|splayPort)|H(?:DMI|ead(?:phones|setMicrophone))|L(?:FESpeaker|ine)|Microphone|Receiver(?:Microphone|Speaker)|Speaker|TTY|Unknown)|Unknown))|u(?:bDevice(?:ClassID|DriftCompensation(?:HighQuality|LowQuality|M(?:axQuality|ediumQuality|inQuality))|Property(?:DriftCompensation(?:Quality)?|ExtraLatency))|pportFolderType)|ystemObjectClassID)|T(?:alkbackControlClassID|imeStamp(?:HostTimeValid|NothingValid|RateScalarValid|S(?:MPTETimeValid|ample(?:HostTimeValid|TimeValid))|WordClockTimeValid)|oolboxErr(?:_(?:CannotDoInCurrentContext|EndOfTrack|I(?:llegalTrackDestination|nvalid(?:EventType|PlayerState|SequenceType))|NoSequence|StartOfTrack|Track(?:IndexError|NotFound))|or_NoTrackDestination)|ransportManager(?:C(?:lassID|reateEndPointDevice)|DestroyEndPointDevice|Property(?:EndPointList|Trans(?:lateUIDToEndPoint|portType))))|Unit(?:Add(?:PropertyListenerSelect|RenderNotifySelect)|C(?:lumpID_System|omplexRenderSelect)|E(?:rr_(?:CannotDoInCurrentContext|ExtensionNotFound|F(?:ailedInitialization|ileNotSpecified|ormatNotSupported)|I(?:llegalInstrument|n(?:itialized|strumentTypeNotFound|valid(?:Element|File(?:Path)?|OfflineRender|P(?:arameter(?:Value)?|roperty(?:Value)?)|Scope)))|M(?:IDIOutputBufferFull|issingKey)|NoConnection|PropertyNot(?:InUse|Writable)|RenderTimeout|TooManyFramesToProcess|Un(?:authorized|initialized|knownFileType))|vent_(?:BeginParameterChangeGesture|EndParameterChangeGesture|P(?:arameterValueChange|ropertyChange)))|GetP(?:arameterSelect|roperty(?:InfoSelect|Select))|InitializeSelect|M(?:anufacturer_Apple|igrateProperty_(?:FromPlugin|OldAutomation))|OfflineProperty_(?:InputSize|OutputSize|Preflight(?:Name|Requirements)|StartOffset)|P(?:arameter(?:Flag_(?:C(?:FNameRelease|anRamp)|Display(?:Cube(?:Root|d)|Exponential|Logarithmic|Mask|Square(?:Root|d))|ExpertMode|G(?:lobal|roup)|Has(?:C(?:FNameString|lump)|Name)|I(?:nput|s(?:ElementMeta|GlobalMeta|HighResolution|Readable|Writable))|MeterReadOnly|NonRealTime|O(?:mitFromPresets|utput)|PlotHistory|ValuesHaveStrings)|Name_Full|Unit_(?:AbsoluteCents|B(?:PM|eats|oolean)|C(?:ents|ustomUnit)|De(?:cibels|grees)|EqualPowerCrossfade|Generic|Hertz|Indexed|LinearGain|M(?:IDI(?:Controller|NoteNumber)|eters|i(?:lliseconds|xerFaderCurve1))|Octaves|P(?:an|ercent|hase)|R(?:at(?:e|io)|elativeSemiTones)|S(?:ampleFrames|econds)))|ro(?:cess(?:MultipleSelect|Select)|perty_(?:A(?:UHostIdentifier|ddParameterMIDIMapping|llParameterMIDIMappings|udioChannelLayout)|B(?:usCount|ypassEffect)|C(?:PULoad|lassInfo(?:FromDocument)?|o(?:coaUI|ntextName)|urrentP(?:layTime|reset))|De(?:ferredRenderer(?:ExtraLatency|PullSize|WaitFrames)|pendentParameters)|Element(?:Count|Name)|F(?:a(?:ctoryPresets|stDispatch)|requencyResponse)|GetUIComponentList|Ho(?:stCallbacks|tMapParameterMIDIMapping)|I(?:conLocation|n(?:PlaceProcessing|put(?:AnchorTimeStamp|SamplesInOutput)))|La(?:stRenderError|tency)|M(?:IDI(?:ControlMapping|OutputCallback(?:Info)?)|a(?:keConnection|trix(?:Dimensions|Levels)|ximumFramesPerSlice)|eter(?:Clipping|ingMode))|NickName|OfflineRender|P(?:a(?:nnerMode|rameter(?:ClumpName|HistoryInfo|I(?:DName|nfo)|List|StringFromValue|Value(?:FromString|Name|Strings)|sForOverview))|resent(?:Preset|ationLatency))|Re(?:moveParameterMIDIMapping|nderQuality|questViewController|verbRoomType)|S(?:RCAlgorithm|ampleRate(?:ConverterComplexity)?|chedule(?:AudioSlice|StartTimeStamp|dFile(?:BufferSizeFrames|IDs|NumberBuffers|Prime|Region))|et(?:ExternalBuffer|RenderCallback)|houldAllocateBuffer|p(?:atial(?:Mixer(?:AttenuationCurve|DistanceParams|RenderingFlags)|izationAlgorithm)|e(?:akerConfiguration|echChannel))|treamFormat|upport(?:ed(?:ChannelLayoutTags|NumChannels)|sMPE))|TailTime|UsesInternalReverb|Voice)))|R(?:ange|e(?:move(?:PropertyListener(?:Select|WithUserDataSelect)|RenderNotifySelect)|nder(?:Action_(?:DoNotCheckRenderArgs|OutputIsSilence|P(?:ostRender(?:Error)?|reRender))|Select)|setSelect))|S(?:RCAlgorithm_(?:MediumQuality|Polyphase)|ampleRateConverterComplexity_(?:Linear|Mastering|Normal)|c(?:heduleParametersSelect|ope_(?:G(?:lobal|roup)|Input|Layer(?:Item)?|Note|Output|Part))|etP(?:arameterSelect|ropertySelect)|ubType_(?:A(?:U(?:Converter|Filter|iPodTimeOther)|udioFilePlayer)|BandPassFilter|D(?:LSSynth|e(?:f(?:aultOutput|erredRenderer)|lay)|istortion|ynamicsProcessor)|G(?:enericOutput|raphicEQ)|H(?:ALOutput|RTFPanner|igh(?:PassFilter|ShelfFilter))|Low(?:PassFilter|ShelfFilter)|M(?:IDISynth|atrix(?:Mixer|Reverb)|erger|ulti(?:BandCompressor|ChannelMixer|Splitter))|N(?:BandEQ|e(?:t(?:Receive|Send)|wTimePitch))|P(?:arametricEQ|eakLimiter|itch)|R(?:everb2|o(?:gerBeep|undTripAAC))|S(?:ample(?:Delay|r)|cheduledSoundPlayer|oundFieldPanner|p(?:atialMixer|eechSynthesis|hericalHeadPanner|litter)|tereoMixer|ystemOutput)|TimePitch|V(?:arispeed|ectorPanner|oiceProcessingIO)))|Type_(?:Effect|FormatConverter|Generator|M(?:IDIProcessor|ixer|usic(?:Device|Effect))|O(?:fflineEffect|utput)|Panner)|UninitializeSelect|yCodecComponentType)|V(?:STFolderType|olumeControlClassID)|_(?:BadFilePathError|File(?:NotFoundError|PermissionError)|MemFullError|ParamError|TooManyFilesOpenError|UnimplementedError))|t(?:h(?:TypeKCItemAttr|orizationFlag(?:CanNotPreAuthorize|De(?:faults|stroyRights)|ExtendRights|InteractionAllowed|NoData|P(?:artialRights|reAuthorize)))|o(?:GenerateReturnID|matorWorkflowsFolderType|saveInformationFolderType)))|vailBoundsChangedFor(?:D(?:isplay|ock)|MenuBar))|B(?:LibTag2|SLN(?:C(?:ontrolPointFormat(?:NoMap|WithMap)|urrentVersion)|DistanceFormat(?:NoMap|WithMap)|HangingBaseline|Ideographic(?:CenterBaseline|HighBaseline|LowBaseline)|LastBaseline|MathBaseline|N(?:oBaseline(?:Override)?|umBaselineClasses)|RomanBaseline|Tag)|T(?:B(?:adCloseMask|igKeysMask)|HeaderNode|IndexNode|LeafNode|MapNode|VariableIndexKeysMask)|a(?:ck(?:spaceCharCode|wardArrowIcon)|d(?:A(?:dapterErr|rg(?:LengthErr|sErr)|ttributeErr)|BaseErr|C(?:ISErr|ustomIFIDErr)|DeviceErr|EDCErr|HandleErr|IRQErr|LinkErr|OffsetErr|PageErr|S(?:izeErr|ocketErr|peedErr)|T(?:upleDataErr|ypeErr)|V(?:ccErr|ppErr)|WindowErr)|ndpassParam_(?:Bandwidth|CenterFrequency))|ellCharCode|ig5_(?:BasicVariant|DOSVariant|ETenVariant|StandardVariant)|lessed(?:BusErrorBait|Folder)|o(?:otTimeStartupItemsFolderType|xAnnotationSelector)|ridgeSoftwareRunningCantSleep|u(?:llet(?:CharCode|Unicode)|rningIcon|syErr|ttonDialogItem)|y(?:CommentView|DateView|IconView|KindView|LabelView|NameView|S(?:izeView|mallIcon)|VersionView|tePacketTranslationFlag_IsEstimate))|C(?:A(?:Clock(?:Message_(?:Armed|Disarmed|PropertyChanged|St(?:art(?:TimeSet|ed)|opped)|WrongSMPTEFormat)|Property_(?:InternalTimebase|M(?:IDIClockDestinations|TC(?:Destinations|FreewheelTime)|eterTrack)|Name|S(?:MPTE(?:Format|Offset)|endMIDISPP|ync(?:Mode|Source))|T(?:empoMap|imebaseSource))|SyncMode_(?:Internal|M(?:IDIClockTransport|TCTransport))|Time(?:Format_(?:AbsoluteSeconds|Beats|HostTime|S(?:MPTE(?:Seconds|Time)|amples|econds))|base_(?:Audio(?:Device|OutputUnit)|HostTime))|_(?:CannotSetTimeError|Invalid(?:P(?:layRateError|ropertySizeError)|S(?:MPTE(?:FormatError|OffsetError)|ync(?:ModeError|SourceError))|Time(?:FormatError|base(?:Error|SourceError))|UnitError)|UnknownPropertyError))|F(?:LinearPCMFormatFlagIs(?:Float|LittleEndian)|MarkerType_(?:Edit(?:Destination(?:Begin|End)|Source(?:Begin|End))|Generic|Index|KeySignature|Program(?:End|Start)|Re(?:gion(?:End|S(?:tart|yncPoint))|leaseLoop(?:End|Start))|S(?:avedPlayPosition|election(?:End|Start)|ustainLoop(?:End|Start))|T(?:empo|imeSignature|rack(?:End|Start)))|RegionFlag_(?:LoopEnable|Play(?:Backward|Forward))|_(?:AudioDataChunkID|ChannelLayoutChunkID|EditCommentsChunkID|F(?:il(?:e(?:Type|Version_Initial)|lerChunkID)|ormatListID)|In(?:foStringsChunkID|strumentChunkID)|M(?:IDIChunkID|a(?:gicCookieID|rkerChunkID))|OverviewChunkID|P(?:acketTableChunkID|eakChunkID)|RegionChunkID|S(?:MPTE_TimeType(?:2(?:398|4|5|997(?:Drop)?)|30(?:Drop)?|5(?:0|994(?:Drop)?)|60(?:Drop)?|None)|tr(?:eamDescriptionChunkID|ingsChunkID))|U(?:MIDChunkID|UIDChunkID)|iXMLChunkID)))|CRegister(?:CBit|NBit|VBit|XBit|ZBit)|F(?:Error(?:HTTP(?:AuthenticationTypeUnsupported|Bad(?:Credentials|ProxyCredentials|URL)|ConnectionLost|P(?:arseFailure|roxyConnectionFailure)|RedirectionLoopDetected|SProxyConnectionFailure)|PACFile(?:Auth|Error))|FTPErrorUnexpectedStatusCode|H(?:TTPCookieCannotParseCookieFile|ost(?:Addresses|Error(?:HostNotFound|Unknown)|Names|Reachability))|M68kRTA|NetService(?:Error(?:BadArgument|C(?:ancel|ollision)|DNSServiceFailure|In(?:Progress|valid)|NotFound|Timeout|Unknown)|Flag(?:IsD(?:efault|omain)|MoreComing|NoAutoRename|Remove)|MonitorTXT|sError(?:BadArgument|C(?:ancel|ollision)|In(?:Progress|valid)|NotFound|Timeout|Unknown))|S(?:OCKS(?:4Error(?:Id(?:Conflict|entdFailed)|RequestFailed|UnknownStatusCode)|5Error(?:Bad(?:Credentials|ResponseAddr|State)|NoAcceptableMethod|UnsupportedNegotiationMethod)|ErrorUn(?:knownClientVersion|supportedServerVersion))|treamError(?:HTTP(?:Authentication(?:Bad(?:Password|UserName)|TypeUnsupported)|BadURL|ParseFailure|RedirectionLoop|SProxyFailureUnexpectedResponseToCONNECTMethod)|SOCKS(?:4(?:Id(?:Conflict|entdFailed)|RequestFailed|SubDomainResponse)|5(?:Bad(?:ResponseAddr|State)|SubDomain(?:Method|Response|UserPass))|SubDomain(?:None|VersionCode)|UnknownClientVersion)))|URLError(?:AppTransportSecurityRequiresSecureConnection|Ba(?:ckgroundSession(?:InUseByAnotherProcess|WasDisconnected)|d(?:ServerResponse|URL))|C(?:a(?:llIsActive|n(?:celled|not(?:C(?:loseFile|onnectToHost|reateFile)|Decode(?:ContentData|RawData)|FindHost|LoadFromNetwork|MoveFile|OpenFile|ParseResponse|RemoveFile|WriteToFile)))|lientCertificateRe(?:jected|quired))|D(?:NSLookupFailed|ata(?:LengthExceedsMaximum|NotAllowed)|ownloadDecodingFailed(?:MidStream|ToComplete))|File(?:DoesNotExist|IsDirectory|OutsideSafeArea)|HTTPTooManyRedirects|InternationalRoamingOff|N(?:etworkConnectionLost|o(?:PermissionsToReadFile|tConnectedToInternet))|Re(?:directToNonExistentLocation|questBodyStreamExhausted|sourceUnavailable)|Se(?:cureConnectionFailed|rverCertificate(?:Has(?:BadDate|UnknownRoot)|NotYetValid|Untrusted))|TimedOut|U(?:n(?:known|supportedURL)|ser(?:AuthenticationRequired|CancelledAuthentication))|ZeroByteResource))|G(?:Image(?:AnimationStatus_(?:AllocationFailure|CorruptInputImage|IncompleteInputImage|ParameterError|UnsupportedFormat)|Metadata(?:Error(?:BadArgument|ConflictingArguments|PrefixConflict|Un(?:known|supportedFormat))|Type(?:A(?:lternate(?:Array|Text)|rray(?:Ordered|Unordered))|Default|Invalid|Str(?:ing|ucture)))|PropertyOrientation(?:Down(?:Mirrored)?|Left(?:Mirrored)?|Right(?:Mirrored)?|Up(?:Mirrored)?)|Status(?:Complete|In(?:complete|validData)|ReadingHeader|Un(?:expectedEOF|knownType)))|L(?:Bad(?:A(?:ddress|lloc|ttribute)|Co(?:deModule|n(?:nection|text))|D(?:isplay|rawable)|Enumeration|FullScreen|Match|OffScreen|P(?:ixelFormat|roperty)|RendererInfo|State|Value|Window)|C(?:E(?:CrashOnRemovedFunctions|DisplayListOptimization|MPEngine|Rasterization|S(?:tateValidation|urfaceBackingSize|wap(?:Limit|Rectangle)))|P(?:C(?:lientStorage|ontextPriorityRequest(?:High|Low|Normal)|urrentRendererID)|DispatchTableSize|GPU(?:FragmentProcessing|RestartStatus(?:Blacklisted|Caused|None)|VertexProcessing)|HasDrawable|MPSwapsInFlight|ReclaimResources|S(?:urface(?:BackingSize|O(?:pacity|rder)|SurfaceVolatile|Texture)|wap(?:Interval|Rectangle))))|GO(?:ClearFormatCache|FormatCacheSize|Re(?:setLibrary|tainRenderers)|Use(?:BuildCache|ErrorHandler))|NoError|OGLPVersion_(?:3_2_Core|GL3_Core|Legacy)|PFA(?:A(?:cc(?:elerated(?:Compute)?|umSize)|l(?:l(?:Renderers|owOfflineRenderers)|phaSize)|ux(?:Buffers|DepthStencil))|Backing(?:Store|Volatile)|C(?:losestPolicy|o(?:lor(?:Float|Size)|mpliant))|D(?:epthSize|isplayMask|oubleBuffer)|FullScreen|M(?:PSafe|aximumPolicy|inimumPolicy|ulti(?:Screen|sample))|NoRecovery|O(?:ffScreen|penGLProfile)|PBuffer|R(?:e(?:motePBuffer|ndererID)|obust)|S(?:ample(?:Alpha|Buffers|s)|ingleRenderer|te(?:ncilSize|reo)|upersample)|TripleBuffer|VirtualScreenCount|Window)|R(?:P(?:Acc(?:elerated(?:Compute)?|umModes)|B(?:ackingStore|ufferModes)|Co(?:lorModes|mpliant)|D(?:epthModes|isplayMask)|FullScreen|GPU(?:FragProcCapable|VertProcCapable)|M(?:PSafe|ax(?:AuxBuffers|Sample(?:Buffers|s))|ultiScreen)|O(?:ffScreen|nline)|R(?:enderer(?:Count|ID)|obust)|S(?:ample(?:Alpha|Modes)|tencilModes)|TextureMemory(?:Megabytes)?|VideoMemory(?:Megabytes)?|Window)|enderer(?:A(?:TIRa(?:deon(?:8500ID|9700ID|ID|X(?:1000ID|2000ID|3000ID))|ge(?:128ID|ProID))|ppleSWID)|Ge(?:Force(?:2MXID|3ID|8xxxID|FXID)|neric(?:FloatID|ID))|Intel(?:900ID|HD(?:4000ID|ID)|X3100ID)|Mesa3DFXID|VTBladeXP2ID))))|JK(?:ItalicRoman(?:O(?:ffSelector|nSelector)|Selector)|RomanSpacingType|SymbolAlt(?:F(?:iveSelector|ourSelector)|OneSelector|T(?:hreeSelector|woSelector)|ernativesType)|VerticalRoman(?:CenteredSelector|HBaselineSelector|PlacementType))|M(?:ActivateTextService|CopyTextServiceInputModeList|DeactivateTextService|F(?:ixTextService|loatBitmapFlags(?:Alpha(?:Premul)?|None|RangeClipped))|Get(?:InputModePaletteMenu|ScriptLangSupport|TextService(?:Menu|Property))|H(?:elpItem(?:AppleGuide|NoHelp|OtherHelp|RemoveHelp)|idePaletteWindows)|In(?:itiateTextService|putModePaletteItemHit)|MenuItemSelected|NothingSelected|S(?:Attr(?:Apple(?:CodesigningHashAgility(?:V2)?|ExpirationTime)|None|S(?:igningTime|mime(?:Capabilities|EncryptionKeyPrefs|MSEncryptionKeyPrefs)))|Certificate(?:Chain(?:WithRoot(?:OrFail)?)?|None|SignerOnly)|Signer(?:Invalid(?:Cert|Index|Signature)|NeedsDetachedContent|Unsigned|Valid)|etTextService(?:Cursor|Property)|howHelpSelected)|Te(?:rminateTextService|xtService(?:Event(?:Ref)?|MenuSelect))|UCTextServiceEvent)|S(?:Accept(?:AllComponentsMode|ThreadSafeComponentsOnlyMode)|DiskSpaceRecoveryOptionNoUI|Identity(?:AuthorityNotAccessibleErr|C(?:lass(?:Group|User)|ommitCompleted)|D(?:eletedErr|uplicate(?:FullNameErr|PosixNameErr))|Flag(?:Hidden|None)|Invalid(?:FullNameErr|PosixNameErr)|PermissionErr|Query(?:Event(?:ErrorOccurred|Results(?:Added|Changed|Removed)|SearchPhaseFinished)|GenerateUpdateEvents|IncludeHiddenIdentities|String(?:BeginsWith|Equals))|UnknownAuthorityErr)|SM_APPLEDL_MASK_MODE|tackBased)|T(?:CharacterCollection(?:Adobe(?:CNS1|GB1|Japan(?:1|2)|Korea1)|IdentityMapping)|F(?:ont(?:BoldTrait|C(?:la(?:rendonSerifsClass|ss(?:ClarendonSerifs|FreeformSerifs|M(?:ask(?:Shift|Trait)|odernSerifs)|O(?:ldStyleSerifs|rnamentals)|S(?:ansSerif|cripts|labSerifs|ymbolic)|TransitionalSerifs|Unknown))|o(?:l(?:lectionCopy(?:DefaultOptions|StandardSort|Unique)|orGlyphsTrait)|mpositeTrait|ndensedTrait))|DescriptorMatching(?:D(?:id(?:Begin|F(?:ailWithError|inish(?:Downloading)?)|Match)|ownloading)|Stalled|WillBegin(?:Downloading|Querying))|ExpandedTrait|F(?:ormat(?:Bitmap|OpenType(?:PostScript|TrueType)|PostScript|TrueType|Unrecognized)|reeformSerifsClass)|ItalicTrait|M(?:anager(?:AutoActivation(?:D(?:efault|isabled)|Enabled)|Error(?:A(?:lreadyRegistered|ssetNotFound)|CancelledByUser|DuplicatedName|ExceededResourceLimit|FileNotFound|In(?:Use|sufficient(?:Info|Permissions)|validF(?:ilePath|ontData))|MissingEntitlement|NotRegistered|RegistrationFailed|SystemRequired|UnrecognizedFormat)|Scope(?:None|P(?:ersistent|rocess)|Session|User))|o(?:dernSerifsClass|noSpaceTrait))|O(?:ldStyleSerifsClass|ptions(?:Default|Pre(?:ferSystemFont|ventAutoActivation))|r(?:ientation(?:Default|Horizontal|Vertical)|namentalsClass))|Priority(?:Computer|Dynamic|Network|Process|System|User)|S(?:ansSerifClass|criptsClass|labSerifsClass|ymbolicClass)|T(?:able(?:A(?:cnt|nkr|var)|B(?:ASE|dat|hed|loc|sln)|C(?:B(?:DT|LC)|FF(?:2)?|OLR|PAL|idg|map|v(?:ar|t))|DSIG|EB(?:DT|LC|SC)|F(?:dsc|eat|mtx|ond|pgm|var)|G(?:DEF|POS|SUB|asp|lyf|var)|H(?:VAR|dmx|ead|hea|mtx|sty)|J(?:STF|ust)|Ker(?:n|x)|L(?:TSH|car|oca|tag)|M(?:ATH|ERG|VAR|axp|eta|or(?:t|x))|Name|O(?:S2|p(?:bd|tionNoOptions))|P(?:CLT|ost|r(?:ep|op))|S(?:TAT|VG|bi(?:t|x))|Trak|V(?:DMX|ORG|VAR|hea|mtx)|Xref|Zapf)|ra(?:it(?:Bold|C(?:lassMask|o(?:lorGlyphs|mposite|ndensed))|Expanded|Italic|MonoSpace|UIOptimized|Vertical)|nsitionalSerifsClass))|U(?:I(?:Font(?:A(?:lertHeader|pplication)|ControlContent|EmphasizedSystem(?:Detail)?|Label|M(?:e(?:nu(?:Item(?:CmdKey|Mark)?|Title)|ssage)|ini(?:EmphasizedSystem|System))|None|P(?:alette|ushButton)|S(?:mall(?:EmphasizedSystem|System|Toolbar)|ystem(?:Detail)?)|Tool(?:Tip|bar)|U(?:ser(?:FixedPitch)?|tilityWindowTitle)|Views|WindowTitle)|OptimizedTrait)|nknownClass)|VerticalTrait)|rameP(?:athFill(?:EvenOdd|WindingNumber)|rogression(?:LeftToRight|RightToLeft|TopToBottom)))|Line(?:B(?:ounds(?:ExcludeTypographic(?:Leading|Shifts)|IncludeLanguageExtents|Use(?:GlyphPathBounds|HangingPunctuation|OpticalBounds))|reakBy(?:C(?:harWrapping|lipping)|Truncating(?:Head|Middle|Tail)|WordWrapping))|Truncation(?:End|Middle|Start))|ParagraphStyleSpecifier(?:Alignment|BaseWritingDirection|Count|DefaultTabInterval|FirstLineHeadIndent|HeadIndent|Line(?:B(?:oundsOptions|reakMode)|HeightMultiple|SpacingAdjustment)|M(?:aximumLine(?:Height|Spacing)|inimumLine(?:Height|Spacing))|ParagraphSpacing(?:Before)?|Ta(?:bStops|ilIndent))|Run(?:Delegate(?:CurrentVersion|Version1)|Status(?:HasNonIdentityMatrix|No(?:Status|nMonotonic)|RightToLeft))|TextAlignment(?:Center|Justified|Left|Natural|Right)|Underline(?:Pattern(?:D(?:ash(?:Dot(?:Dot)?)?|ot)|Solid)|Style(?:Double|None|Single|Thick))|WritingDirection(?:Embedding|LeftToRight|Natural|Override|RightToLeft))|UPSPPDDomain|V(?:AttachmentMode_Should(?:NotPropagate|Propagate)|Pixel(?:Buffer(?:Lock_ReadOnly|PoolFlushExcessBuffers)|FormatType_(?:1(?:28RGBAFloat|4Bayer_(?:BGGR|G(?:BRG|RBG)|RGGB)|6(?:BE5(?:55|65)|Gray|LE5(?:55(?:1)?|65))|IndexedGray_WhiteIsZero|Monochrome)|2(?:4(?:BGR|RGB)|Indexed(?:Gray_WhiteIsZero)?)|3(?:0RGB(?:LEPackedWideGamut)?|2(?:A(?:BGR|RGB|lphaGray)|BGRA|RGBA))|4(?:2(?:0YpCbCr(?:10BiPlanar(?:FullRange|VideoRange)|8(?:BiPlanar(?:FullRange|VideoRange)|Planar(?:FullRange)?|VideoRange_8A_TriPlanar))|2YpCbCr(?:1(?:0(?:BiPlanar(?:FullRange|VideoRange))?|6)|8(?:FullRange|_yuvs)?|_4A_8BiPlanar))|44(?:4(?:AYpCbCr(?:16|8)|YpCbCrA8(?:R)?)|YpCbCr(?:10(?:BiPlanar(?:FullRange|VideoRange))?|8))|8RGB|Indexed(?:Gray_WhiteIsZero)?)|64(?:ARGB|RGBAHalf)|8Indexed(?:Gray_WhiteIsZero)?|ARGB2101010LEPacked|D(?:epthFloat(?:16|32)|isparityFloat(?:16|32))|OneComponent(?:16Half|32Float|8)|TwoComponent(?:16Half|32Float|8)))|Return(?:AllocationFailed|DisplayLink(?:AlreadyRunning|CallbacksNotSet|NotRunning)|Error|First|Invalid(?:Argument|Display|P(?:ixel(?:BufferAttributes|Format)|oolAttributes)|Size)|Last|P(?:ixelBufferNot(?:MetalCompatible|OpenGLCompatible)|oolAllocationFailed)|Retry|Success|Unsupported|WouldExceedAllocationThreshold)|SMPTETime(?:Running|Type(?:2(?:4|5|997(?:Drop)?)|30(?:Drop)?|5994|60)|Valid)|Time(?:IsIndefinite|Stamp(?:BottomField|HostTimeValid|IsInterlaced|RateScalarValid|SMPTETimeValid|TopField|Video(?:HostTimeValid|RefreshPeriodValid|TimeValid))))|a(?:chedDataFolderType|l(?:ibratorNamePrefix|lingConvention(?:Mask|Phase|Width))|n(?:onicalCompositionO(?:ffSelector|nSelector)|t(?:ConfigureCardErr|ReportProcessorTemperatureErr))|r(?:bonLibraryFolderType|d(?:BusCardErr|PowerOffErr))|seSensitive(?:Layout(?:O(?:ffSelector|nSelector)|Type)|SpacingO(?:ffSelector|nSelector))|utionIcon)|e(?:nterOn(?:MainScreen|Screen)|rt(?:Search(?:Any|Decrypt(?:Allowed|Disallowed|Ignored|Mask)|Encrypt(?:Allowed|Disallowed|Ignored|Mask)|PrivKeyRequired|S(?:hift|igning(?:Allowed|Disallowed|Ignored|Mask))|Unwrap(?:Allowed|Disallowed|Ignored|Mask)|Verify(?:Allowed|Disallowed|Ignored|Mask)|Wrap(?:Allowed|Disallowed|Ignored|Mask))|Usage(?:AllAdd|DecryptA(?:dd|skAndAdd)|EncryptA(?:dd|skAndAdd)|KeyExchA(?:dd|skAndAdd)|RootA(?:dd|skAndAdd)|S(?:SLA(?:dd|skAndAdd)|hift|igningA(?:dd|skAndAdd))|VerifyA(?:dd|skAndAdd))|ificateKCItemClass))|h(?:aracter(?:AlternativesType|PaletteInputMethodClass|ShapeType)|e(?:ck(?:BoxDialogItem|CharCode|Unicode)|wableItemsFolderType))|ircleAnnotationSelector|l(?:ass(?:KCItemAttr|ic(?:D(?:esktopFolderType|omain)|PreferencesFolderType))|ea(?:nUpAEUT|rCharCode)|i(?:entRequestDenied|p(?:boardIcon|ping(?:Creator|PictureType(?:Icon)?|SoundType(?:Icon)?|TextType(?:Icon)?|UnknownType(?:Icon)?))))|o(?:l(?:l(?:ate(?:AttributesNotFoundErr|BufferTooSmall|Invalid(?:C(?:har|ollationRef)|Options)|MissingUnicodeTableErr|PatternNotFoundErr|UnicodeConvertFailedErr)|ection(?:AllAttributes|D(?:efaultAttributes|ontWant(?:Attributes|Data|I(?:d|ndex)|Size|Tag))|Lock(?:Bit|Mask)|NoAttributes|Persistence(?:Bit|Mask)|Reserved(?:0(?:Bit|Mask)|1(?:0(?:Bit|Mask)|1(?:Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|4(?:Bit|Mask)|5(?:Bit|Mask)|6(?:Bit|Mask)|7(?:Bit|Mask)|8(?:Bit|Mask)|9(?:Bit|Mask))|User(?:0(?:Bit|Mask)|1(?:0(?:Bit|Mask)|1(?:Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|4(?:Bit|Mask)|5(?:Bit|Mask)|Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|4(?:Bit|Mask)|5(?:Bit|Mask)|6(?:Bit|Mask)|7(?:Bit|Mask)|8(?:Bit|Mask)|9(?:Bit|Mask)|Attributes)))|or(?:Picker(?:AppIsColorSyncAware|Ca(?:llColorProcLive|n(?:AnimatePalette|ModifyPalette))|D(?:etachedFromChoices|ialogIsMo(?:dal|veable))|In(?:ApplicationDialog|PickerDialog|SystemDialog)|sFolderType)|Sync(?:1(?:0BitInteger|6Bit(?:Float|Integer)|BitGamut)|32Bit(?:Float|Integer|NamedColorIndex)|8BitInteger|Alpha(?:First|InfoMask|Last|None(?:Skip(?:First|Last))?|Premultiplied(?:First|Last))|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask)|CMMFolderType|Folder(?:Icon|Type)|ProfilesFolderType|ScriptingFolderType))?)|m(?:m(?:and(?:CharCode|Unicode)|entKCItemAttr|on(?:LigaturesO(?:ffSelector|nSelector)|NameKCItemAttr))|p(?:atibilityCompositionO(?:ffSelector|nSelector)|o(?:nent(?:AliasResourceType|C(?:anDoSelect|loseSelect)|DebugOption|ExecuteWiredActionSelect|Get(?:MPWorkFunctionSelect|PublicResourceSelect)|OpenSelect|Re(?:gisterSelect|sourceType)|TargetSelect|UnregisterSelect|VersionSelect|sFolderType)|sitionsFolderType)|uterIcon))|n(?:figurationLockedErr|n(?:Suite|ect(?:ToIcon|ion(?:AudioStreaming|BlueGammaScale|C(?:h(?:anged|eckEnable)|o(?:lor(?:DepthsSupported|Mode(?:sSupported)?)|ntroller(?:ColorDepth|D(?:epthsSupported|itherControl))))|Display(?:Flags|Parameter(?:Count|s))|Enable(?:Audio)?|Fl(?:ags|ushParameters)|G(?:ammaScale|reenGammaScale)|HandleDisplayPortEvent|Ignore|Overscan|P(?:anelTimingDisable|o(?:stWake|wer)|robe)|RedGammaScale|S(?:tartOfFrameTime|upports(?:AppleSense|HLDDCSense|LLDDCSense)|ync(?:Enable|Flags))|V(?:BLMultiplier|ideoBest))))|t(?:ainer(?:AliasType|CDROMAliasType|F(?:loppyAliasType|olderAliasType)|HardDiskAliasType|ServerAliasType|TrashAliasType)|extual(?:Alternates(?:O(?:ffSelector|nSelector)|Type)|LigaturesO(?:ffSelector|nSelector)|MenuItemsFolder(?:Icon|Type)|SwashAlternatesO(?:ffSelector|nSelector))|rol(?:A(?:dd(?:FontSizeMask|ToMetaFontMask)|utoToggles)|B(?:e(?:havior(?:CommandMenu|MultiValueMenu|OffsetContents|Pushbutton|S(?:ingleValueMenu|ticky)|Toggles)|velButton(?:Align(?:Bottom(?:Left|Right)?|Center|Left|Right|SysDirection|T(?:ext(?:Center|Flush(?:Left|Right)|SysDirection)|op(?:Left|Right)?))|C(?:enterPopupGlyphTag|ontentTag)|Graphic(?:AlignTag|OffsetTag)|IsMultiValueMenuTag|KindTag|La(?:rgeBevel(?:Proc|Variant)?|stMenuTag)|Menu(?:DelayTag|HandleTag|On(?:Bottom|Right(?:Variant)?)|RefTag|ValueTag)|NormalBevel(?:Proc|Variant)?|OwnedMenuRefTag|Place(?:AboveGraphic|BelowGraphic|Normally|SysDirection|To(?:LeftOfGraphic|RightOfGraphic))|S(?:caleIconTag|mallBevel(?:Proc|Variant)?)|T(?:ext(?:AlignTag|OffsetTag|PlaceTag)|ransformTag)))|oundsChange(?:PositionChanged|SizeChanged)|uttonPart)|C(?:h(?:asingArrows(?:AnimatingTag|Proc)|eckBox(?:AutoToggleProc|CheckedValue|MixedValue|P(?:art|roc)|UncheckedValue))|l(?:ickableMetaPart|ock(?:A(?:MPMPart|bsoluteTimeTag|nimatingTag)|DateProc|F(?:lag(?:DisplayOnly|Live|Standard)|ontStyleTag)|HourDayPart|Is(?:DisplayOnly|Live)|LongDateTag|M(?:inuteMonthPart|onthYearProc)|NoFlags|Part|SecondYearPart|T(?:ime(?:Proc|SecondsProc)|ype(?:HourMinute(?:Second)?|Month(?:DayYear|Year)))))|o(?:l(?:lectionTag(?:Bounds|Command|ID(?:ID|Signature)|M(?:aximum|inimum)|RefCon|Title|UnicodeTitle|V(?:a(?:lue|rCode)|i(?:ewSize|sibility)))|orTableResourceType)|ntent(?:AlertIconRes|C(?:GImageRef|Icon(?:Handle|Res))|I(?:CON(?:Res)?|con(?:Ref|Suite(?:Handle|Res)))|MetaPart|Pict(?:Handle|Res)|T(?:ag|extOnly))))|D(?:ataBrowser(?:DraggedPart|EditText(?:KeyFilterTag|ValidationProcTag)|IncludesFrameAndFocusTag|KeyFilterTag|Part)|efProc(?:ResourceType|Type)|i(?:alogItem|s(?:abledPart|closure(?:Button(?:Closed|Disclosed)|TrianglePoint(?:Default|Left|Right))))|ownButtonPart)|E(?:dit(?:Text(?:C(?:FStringTag|harCount)|FixedTextTag|In(?:line(?:InputProc|P(?:ostUpdateProcTag|reUpdateProcTag))|sert(?:CFStringRefTag|TextBufferTag))|Key(?:FilterTag|ScriptBehaviorTag)|LockedTag|P(?:a(?:rt|ssword(?:CFStringTag|Proc|Tag))|roc)|S(?:electionTag|ingleLineTag|pellCheck(?:AsYouTypeTag|ingTag)|tyleTag)|T(?:EHandleTag|extTag)|ValidationProcTag)|UnicodeTextP(?:asswordProc|ostUpdateProcTag|roc))|ntireControl)|Fo(?:cus(?:N(?:extPart|oPart)|PrevPart)|nt(?:BigSystemFont|MiniSystemFont|S(?:mall(?:BoldSystemFont|SystemFont)|tyleTag)|ViewSystemFont))|G(?:etsFocusOnClick|roupBox(?:CheckBoxProc|F(?:ontStyleTag|rameRectTag)|Menu(?:HandleTag|RefTag)|PopupButtonProc|Secondary(?:CheckBoxProc|PopupButtonProc|TextTitleProc)|T(?:extTitleProc|itleRectTag)))|Ha(?:ndlesTracking|s(?:RadioBehavior|SpecialBackground))|I(?:con(?:AlignmentTag|ContentTag|NoTrackProc|P(?:art|roc)|Re(?:f(?:NoTrackProc|Proc)|sourceIDTag)|Suite(?:NoTrackProc|Proc)|TransformTag)|dlesWithTimer|mageWell(?:ContentTag|IsDragDestinationTag|P(?:art|roc)|TransformTag)|n(?:activePart|dicatorPart|vertsUpDownValueMeaning))|K(?:ey(?:Filter(?:BlockKey|PassKey|Tag)|ScriptBehavior(?:AllowAnyScript|PrefersRoman|RequiresRoman))|ind(?:BevelButton|C(?:h(?:asingArrows|eck(?:Box|GroupBox))|lock)|D(?:ataBrowser|isclosure(?:Button|Triangle))|Edit(?:Text|UnicodeText)|GroupBox|HI(?:ComboBox|GrowBoxView|ImageView|MenuView|S(?:crollView|earchField|tandardMenuView)|TextView)|I(?:con|mageWell)|Li(?:stBox|ttleArrows)|P(?:icture|lacard|opup(?:Arrow|Button|GroupBox)|rogressBar|ush(?:Button|IconButton))|R(?:adio(?:Button|Group)|elevanceBar|oundButton)|S(?:croll(?:Bar|ingTextBox)|eparator|ignatureApple|lider|taticText)|Ta(?:bs|g)|UserPane|WindowHeader))|L(?:abelPart|i(?:st(?:Box(?:AutoSizeProc|DoubleClick(?:Part|Tag)|FontStyleTag|KeyFilterTag|L(?:DEFTag|istHandleTag)|P(?:art|roc))|DescResType)|ttleArrows(?:IncrementValueTag|Proc)))|MenuPart|No(?:Content|Part|Variant)|OpaqueMetaPart|P(?:a(?:ge(?:DownPart|UpPart)|nel(?:DisabledFolder(?:Icon|Type)|Folder(?:AliasType|Icon(?:Resource)?|Type)))|icture(?:HandleTag|NoTrackProc|P(?:art|roc))|lacardProc|opup(?:Arrow(?:EastProc|NorthProc|Orientation(?:East|North|South|West)|S(?:ize(?:Normal|Small)|mall(?:EastProc|NorthProc|SouthProc|WestProc)|outhProc)|WestProc)|Button(?:CheckCurrentTag|ExtraHeightTag|Menu(?:HandleTag|IDTag|RefTag)|OwnedMenuRefTag|Proc)|FixedWidthVariant|Use(?:AddResMenuVariant|WFontVariant)|VariableWidthVariant)|ro(?:gressBar(?:AnimatingTag|IndeterminateTag|Proc)|pertyPersistent)|ushBut(?:LeftIconProc|RightIconProc|ton(?:AnimatingTag|C(?:ancelTag|ontentTag)|DefaultTag|I(?:con(?:AlignmentTag|On(?:Left|Right))|sTexturedTag)|Proc)))|R(?:adio(?:Button(?:AutoToggleProc|CheckedValue|MixedValue|P(?:art|roc)|UncheckedValue)|GroupP(?:art|roc))|elevanceBarProc|oundButton(?:ContentTag|LargeSize|NormalSize|SizeTag))|S(?:croll(?:Bar(?:LiveProc|Proc|ShowsArrowsTag)|TextBox(?:A(?:nimatingTag|utoScroll(?:AmountTag|Proc))|ContentsTag|DelayBe(?:foreAutoScrollTag|tweenAutoScrollTag)|Proc))|e(?:archField(?:CancelPart|MenuPart)|paratorLineProc)|ize(?:Auto|Large|Mini|Normal|Small|Tag)|lider(?:DoesNotPoint|HasTickMarks|LiveFeedback|NonDirectional|P(?:oints(?:DownOrRight|UpOrLeft)|roc)|ReverseDirection)|t(?:aticText(?:CFStringTag|IsMultilineTag|Proc|StyleTag|T(?:ext(?:HeightTag|Tag)|runcTag))|r(?:ipModulesFolder(?:Icon|Type)|uctureMetaPart))|upports(?:C(?:alcBestRect|lickActivation|ontextualMenus)|D(?:ataAccess|ragAndDrop)|Embedding|F(?:lattening|ocus)|G(?:etRegion|hosting)|LiveFeedback|SetCursor))|T(?:ab(?:ContentRectTag|Direction(?:East|North|South|West)|EnabledFlagTag|FontStyleTag|I(?:mageContentTag|nfo(?:Tag|Version(?:One|Zero)))|L(?:arge(?:EastProc|NorthProc|Proc|SouthProc|WestProc)|istResType)|S(?:ize(?:Large|Mini|Small)|mall(?:EastProc|NorthProc|Proc|SouthProc|WestProc)))|emplateResourceType|hemeText(?:FontTag|HorizontalFlushTag|InfoTag|TruncationTag|VerticalFlushTag)|riangle(?:AutoToggleProc|L(?:astValueTag|eftFacing(?:AutoToggleProc|Proc))|P(?:art|roc)))|U(?:nicode|pButtonPart|se(?:AllMask|BackColorMask|F(?:aceMask|o(?:ntMask|reColorMask))|JustMask|ModeMask|SizeMask|ThemeFontIDMask|r(?:ItemDrawProcTag|Pane(?:ActivateProcTag|BackgroundProcTag|DrawProcTag|FocusProcTag|HitTestProcTag|IdleProcTag|KeyDownProcTag|Proc|TrackingProcTag))|sOwningWindowsFontVariant))|W(?:ants(?:Activate|Idle)|indow(?:Header(?:IsListHeaderTag|Proc)|ListViewHeaderProc))))|verterPrimeMethod_(?:No(?:ne|rmal)|Pre))|operativeThread|re(?:E(?:ndian(?:AppleEventManagerDomain|ResourceManagerDomain)|ventClass)|ServicesFolderType))|reat(?:e(?:Folder(?:AtBoot(?:Bit)?)?|IfNeeded)|ionDateKCItemAttr|orKCItemAttr)|u(?:r(?:rent(?:MixedModeStateRecord|Process|ThreadID|User(?:Folder(?:Location|Type)|RemoteFolder(?:Location|Type)))|sive(?:ConnectionType|Selector))|stom(?:BadgeResource(?:ID|Type|Version)|Icon(?:KCItemAttr|Resource))))|D(?:0Dispatched(?:CStackBased|PascalStackBased)|1DispatchedPascalStackBased|CM(?:A(?:llowListing|nyFieldT(?:ag|ype))|BasicDictionaryClass|Can(?:AddDictionaryFieldMask|CreateDictionaryMask|HaveMultipleIndexMask|ModifyDictionaryMask|StreamDictionaryMask|Use(?:FileDictionaryMask|MemoryDictionaryMask|TransactionMask))|DictionaryHeader(?:Signature|Version)|Fi(?:ndMethod(?:B(?:ackwardTrie|eginningMatch)|ContainsMatch|E(?:ndingMatch|xactMatch)|ForwardTrie)|xedSizeFieldMask)|HiddenFieldMask|I(?:dentifyFieldMask|ndexedFieldMask)|Japanese(?:AccentT(?:ag|ype)|FukugouInfoT(?:ag|ype)|H(?:inshiT(?:ag|ype)|yokiT(?:ag|ype))|OnKunReadingT(?:ag|ype)|PhoneticT(?:ag|ype)|WeightT(?:ag|ype)|YomiT(?:ag|ype))|ProhibitListing|Re(?:ad(?:OnlyDictionary|WriteDictionary)|quiredFieldMask)|SpecificDictionaryClass|UserDictionaryClass)|M(?:CantBlock|D(?:isplay(?:AlreadyInstalledErr|NotFoundErr)|riverNotDisplayMgrAwareErr)|FoundErr|GenErr|M(?:ainDisplayCannotMoveErr|irroring(?:Blocked|NotOn|OnAlready))|No(?:DeviceTableclothErr|tFoundErr)|SWNotInitializedErr|WrongNumberOfDisplays)|OSJapanese(?:PalmVariant|StandardVariant)|R(?:AudioFileNotSupportedErr|B(?:adLayoutErr|lock(?:Size(?:Audio|DVDData|Mode(?:1Data|2(?:Data|Form(?:1Data|2Data))))|Type(?:Audio|DVDData|Mode(?:1Data|2(?:Data|Form(?:1Data|2Data)))))|urn(?:MediaWriteFailureErr|NotAllowedErr|PowerCalibrationErr|UnderrunErr))|CDText(?:Encoding(?:ASCII|ISOLatin1Modified)|GenreCode(?:A(?:dultContemporary|lternativeRock)|C(?:hildrens|lassical|o(?:ntemporaryChristian|untry))|Dance|E(?:asyListening|rotic)|Folk|Gospel|HipHop|Jazz|Latin|Musical|NewAge|Oper(?:a|etta)|Pop|R(?:ap|eggae|hythmAndBlues|ock)|S(?:ound(?:Effects|track)|pokenWord)|Unknown|WorldMusic))|D(?:ata(?:Form(?:Audio|DVDData|Mode(?:1Data|2(?:Data|Form(?:1Data|2Data))))|ProductionErr)|evice(?:AccessErr|Bu(?:rnStrategyNotAvailableErr|syErr)|C(?:antWrite(?:CDTextErr|I(?:SRCErr|ndexPointsErr)|SCMSErr)|ommunicationErr)|InvalidErr|Not(?:ReadyErr|SupportedErr)|PreGapLengthNotValidErr)|oubleLayerL0(?:AlreadySpecifiedErr|DataZoneBlocksParamErr))|F(?:i(?:le(?:Fork(?:Data|Resource|Size(?:Actual|Estimate))|LocationConflictErr|M(?:essage(?:ForkSize|P(?:ostBurn|r(?:eBurn|oduceData))|Release|VerificationStarting)|odifiedDuringBurnErr)|system(?:Mask(?:Default|HFSPlus|ISO9660|Joliet|UDF)|sNotSupportedErr))|rstErr)|lag(?:NoMoreData|SubchannelDataRequested)|unctionNotSupportedErr)|In(?:ternalErr|validIndexPointsErr)|LinkType(?:FinderAlias|HardLink|SymbolicLink)|Media(?:BusyErr|InvalidErr|Not(?:BlankErr|ErasableErr|PresentErr|SupportedErr|WritableErr))|S(?:essionFormat(?:Audio|CD(?:I|XA)|DVDData|Mode1Data)|peedTestAlreadyRunningErr)|T(?:ooMany(?:NameConflictsErr|TracksForDVDErr)|rack(?:M(?:essage(?:EstimateLength|P(?:ostBurn|r(?:eBurn|oduce(?:Data|PreGap)))|Verif(?:ication(?:Done|Starting)|y(?:Data|PreGap)))|ode(?:1Data|2(?:Data|Form(?:1Data|2Data))|Audio|DVDData))|ReusedErr))|UserCanceledErr|VerificationFailedErr)|Sp(?:Con(?:firmSwitchWarning|text(?:AlreadyReservedErr|Not(?:FoundErr|ReservedErr)))|FrameRateNotReadyErr|In(?:ternalErr|valid(?:AttributesErr|ContextErr))|NotInitializedErr|S(?:tereoContextErr|ystemSWTooOldErr))|TP(?:AbortJobErr|HoldJobErr|StopQueueErr|T(?:hirdPartySupported|ryAgainErr))|ata(?:A(?:ccessKCEvent(?:Mask)?|lignmentException)|Br(?:eakpointException|owser(?:A(?:lwaysExtendSelection|ttribute(?:AutoHideScrollBars|ColumnViewResizeWindow|ListView(?:AlternatingRowColors|DrawColumnDividers)|None|ReserveGrowBoxSpace))|C(?:heckboxT(?:riState|ype)|lientPropertyFlags(?:Mask|Offset)|mdTogglesSelection|o(?:lumnView(?:PreviewProperty)?|nt(?:ainer(?:AliasIDProperty|Clos(?:ed|ing)|Is(?:ClosableProperty|Open(?:ableProperty)?|SortableProperty)|Opened|Sort(?:ed|ing))|entHit))|ustomType)|D(?:ateTime(?:DateOnly|Relative|SecondsToo|T(?:imeOnly|ype))|efaultPropertyFlags|oNotTruncateText|ragSelect)|Edit(?:Msg(?:C(?:lear|opy|ut)|Paste|Redo|SelectAll|Undo)|St(?:arted|opped))|I(?:con(?:AndTextType|Type)|tem(?:A(?:dded|nyState)|D(?:eselected|oubleClicked)|Is(?:ActiveProperty|ContainerProperty|DragTarget|EditableProperty|Select(?:ableProperty|ed))|No(?:Property|State)|ParentContainerProperty|Removed|Sel(?:ected|fIdentityProperty)|s(?:A(?:dd|ssign)|Remove|Toggle)))|L(?:atestC(?:allbacks|ustomCallbacks)|istView(?:AppendColumn|DefaultColumnFlags|LatestHeaderDesc|MovableColumn|NoGapForIconInHeaderButton|S(?:electionColumn|ortableColumn)|TypeSelectColumn)?)|Metric(?:CellContentInset|Disclosure(?:Column(?:EdgeInset|PerDepthGap)|TriangleAndContentGap)|IconAndTextGap|Last)|N(?:everEmptySelectionSet|o(?:DisjointSelection|Item|View|thingHit))|Order(?:Decreasing|Increasing|Undefined)|P(?:opupMenu(?:Buttonless|Type)|ro(?:gressBarType|perty(?:C(?:heckboxPart|ontentPart)|DisclosurePart|EnclosingPart|Flags(?:Mask|Offset)|I(?:conPart|s(?:Editable|Mutable))|ModificationFlags|ProgressBarPart|RelevanceRankPart|SliderPart|TextPart)))|Re(?:l(?:ativeDateTime|evanceRankType)|setSelection|veal(?:AndCenterInView|Only|WithoutSelecting))|S(?:elect(?:OnlyOne|ion(?:Anchor(?:Down|Left|Right|Up)|SetChanged))|lider(?:DownwardThumb|PlainThumb|Type|UpwardThumb)|topTracking)|T(?:a(?:bleView(?:FillHilite|LastColumn|MinimalHilite|SelectionColumn)|rgetChanged)|extType|runcateText(?:At(?:End|Start)|Middle))|U(?:niversalPropertyFlags(?:Mask)?|ser(?:StateChanged|ToggledContainer))|ViewSpecific(?:Flags(?:Mask|Offset)|PropertyFlags))))|e(?:c(?:o(?:mposeDiacriticsSelector|rativeBordersSelector)|ryptKCItemAttr)|epestColorScreen|fault(?:C(?:JKRomanSelector|MMSignature|hangedKCEvent(?:Mask)?|olorPicker(?:Height|Width))|LowerCaseSelector|UpperCaseSelector)|l(?:ayParam_(?:DelayTime|Feedback|LopassCutoff|WetDryMix)|ete(?:AliasIcon|CharCode|KCEvent(?:Mask)?))|s(?:criptionKCItemAttr|ign(?:ComplexityType|Level(?:1Selector|2Selector|3Selector|4Selector|5Selector))|ktop(?:FolderType|Icon(?:Resource)?|P(?:icturesFolderType|rinterAliasType)))|v(?:eloper(?:ApplicationsFolderType|DocsFolderType|FolderType|HelpFolderType)|ice(?:InitiatedWake|ToPCS)))|i(?:a(?:criticsType|gonalFractionsSelector|l(?:ectBundleResType|og(?:F(?:lags(?:HandleMovableModal|Use(?:Co(?:mpositing|ntrolHierarchy)|Theme(?:Background|Controls)))|ont(?:Add(?:FontSizeMask|ToMetaFontMask)|NoFontStyle|Use(?:AllMask|BackColorMask|F(?:aceMask|o(?:nt(?:Mask|NameMask)|reColorMask))|JustMask|ModeMask|SizeMask|ThemeFontIDMask)))|WindowKind))|mond(?:AnnotationSelector|CharCode|Unicode))|ctionar(?:iesFolderType|yFileType)|giHub(?:Blank(?:CD|DVD)|EventClass|MusicCD|PictureCD|VideoDVD)|ngbatsSelector|phthongLigaturesO(?:ffSelector|nSelector)|rectoryServices(?:FolderType|PlugInsFolderType)|s(?:p(?:atched(?:ParameterPhase|SelectorSize(?:Phase|Width))|lay(?:ExtensionsFolderType|Mode(?:A(?:cceleratorBackedFlag|lwaysShowFlag)|BuiltInFlag|DefaultFlag|InterlacedFlag|N(?:ativeFlag|everShowFlag|ot(?:GraphicsQualityFlag|PresetFlag|ResizeFlag))|RequiresPanFlag|S(?:afe(?:Flag|tyFlags)|imulscanFlag|tretchedFlag)|TelevisionFlag|Valid(?:F(?:lag|or(?:AirPlayFlag|HiResFlag|MirroringFlag))|ateAgainstDisplay))|ProductIDGeneric|SubPixel(?:Configuration(?:Delta|Quad|Stripe(?:Offset)?|Undefined)|Layout(?:BGR|QuadGB(?:L|R)|RGB|Undefined)|Shape(?:Elliptical|Oval|R(?:ectangular|ound)|Square|Undefined))|TextSelector|VendorIDUnknown))|tortionParam_(?:CubicTerm|De(?:c(?:ay|imation(?:Mix)?)|lay(?:Mix)?)|FinalMix|LinearTerm|PolynomialMix|R(?:ingMod(?:Balance|Freq(?:1|2)|Mix)|ounding)|S(?:oftClipGain|quaredTerm)))|therAlgorithm_(?:NoiseShaping|TPDF))|o(?:FolderActionEvent|NotActivateAnd(?:HandleClick|IgnoreClick)|cument(?:Window(?:Class|VariantCode)|ationFolderType|sFolder(?:Icon|Type))|main(?:LibraryFolderType|TopLevelFolderType)|nt(?:CreateFolder|FindAppBySignature|PassSelector)|wn(?:ArrowCharCode|loadsFolderType)|ze(?:Demand|Request|ToFullWakeUp|WakeUp))|r(?:a(?:g(?:Action(?:Al(?:ias|l)|Copy|Delete|Generic|Move|Nothing|Private)|Behavior(?:None|ZoomBackAnimation)|D(?:ark(?:Translucency|erTranslucency)|oNotScaleImage)|FlavorType(?:HFS|PromiseHFS)|HasLeftSenderWindow|InsideSender(?:Application|Window)|OpaqueTranslucency|P(?:romisedFlavor(?:FindFile)?|seudo(?:CreatorVolumeOrDirectory|FileType(?:Directory|Volume)))|Region(?:AndImage|Begin|Draw|End|Hide|Idle)|Standard(?:DropLocation(?:Trash|Unknown)|Translucency)|Tracking(?:Enter(?:Control|Handler|Window)|In(?:Control|Window)|Leave(?:Control|Handler|Window)))|w(?:Control(?:EntireControl|IndicatorOnly)|erWindowClass))|op(?:BoxFolderType|Folder(?:AliasType|Icon(?:Resource)?)|IconVariant))|uration(?:Forever|Immediate|Mi(?:crosecond|llisecond))|ynamic(?:RangeControlMode_(?:Heavy|Light|None)|sProcessorParam_(?:AttackTime|CompressionAmount|Expansion(?:Ratio|Threshold)|HeadRoom|InputAmplitude|MasterGain|OutputAmplitude|ReleaseTime|Threshold)))|E(?:A(?:CCESErr|DDR(?:INUSEErr|NOTAVAILErr)|GAINErr|LREADYErr)|B(?:AD(?:FErr|MSGErr)|USYErr)|C(?:ANCELErr|ONN(?:ABORTEDErr|RE(?:FUSEDErr|SETErr)))|DE(?:ADLKErr|STADDRREQErr)|EXISTErr|FAULTErr|HOST(?:DOWNErr|UNREACHErr)|I(?:N(?:PROGRESSErr|TRErr|VALErr)|OErr|SCONNErr)|M(?:SGSIZEErr|ailKCItemAttr)|N(?:ET(?:DOWNErr|RESETErr|UNREACHErr)|O(?:BUFSErr|D(?:ATAErr|EVErr)|ENTErr|M(?:EMErr|SGErr)|PROTOOPTErr|RSRCErr|S(?:RErr|TRErr)|T(?:CONNErr|SOCKErr|TYErr))|XIOErr)|OPNOTSUPPErr|P(?:ERMErr|IPEErr|ROTO(?:Err|NOSUPPORTErr|TYPEErr))|RANGEErr|S(?:HUTDOWNErr|OCKTNOSUPPORTErr|RCHErr)|T(?:IME(?:DOUTErr|Err)|OOMANYREFSErr)|UC_(?:CN_(?:BasicVariant|DOSVariant)|KR_(?:BasicVariant|DOSVariant))|WOULDBLOCKErr|dit(?:TextDialogItem|orsFolderType)|jectMediaIcon|n(?:crypt(?:KCItemAttr|Password)|d(?:CharCode|DateKCItemAttr|Of(?:Sentence|Word))|gravedTextSelector|ter(?:CharCode|Idle|Run|Standby))|scapeCharCode|ve(?:nt(?:A(?:ccessible(?:Get(?:All(?:A(?:ctionNames|ttributeNames)|ParameterizedAttributeNames)|ChildAtPoint|FocusedChild|NamedA(?:ctionDescription|ttribute))|IsNamedAttributeSettable|PerformNamedAction|SetNamedAttribute)|pp(?:A(?:ctiv(?:ated|eWindowChanged)|vailableWindowBoundsChanged)|Deactivated|F(?:ocus(?:Drawer|MenuBar|Next(?:DocumentWindow|FloatingWindow)|Toolbar)|rontSwitched)|GetDockTileMenu|Hidden|IsEventInInstantMouser|Launch(?:Notification|ed)|Quit|S(?:hown|ystemUIModeChanged)|Terminated|UpdateDockTile|earanceScrollBarVariantChanged|leEvent)|ttribute(?:Monitored|None|UserEvent))|C(?:l(?:ass(?:A(?:ccessibility|pp(?:earance|l(?:eEvent|ication)))|C(?:lockView|o(?:mmand|ntrol))|D(?:ataBrowser|elegate)|EPPC|Font|Gesture|HI(?:ComboBox|Object)|Ink|Keyboard|M(?:enu|ouse)|S(?:crollable|e(?:archField|rvice)|ystem)|T(?:SMDocumentAccess|ablet|ext(?:Field|Input)|oolbar(?:Item(?:View)?)?)|Volume|Window)|ockDateOrTimeChanged)|o(?:m(?:boBoxListItemSelected|mand(?:Process|UpdateStatus))|ntrol(?:A(?:ctivate|ddedSubControl|pplyTextColor)|BoundsChanged|C(?:lick|ontextualMenuClick)|D(?:eactivate|ispose|ra(?:g(?:Enter|Leave|Receive|Within)|w))|EnabledStateChanged|FocusPartChanged|G(?:et(?:A(?:ctionProcPart|utoToggleValue)|ClickActivation|Data|F(?:ocusPart|rameMetrics)|IndicatorDragConstraint|NextFocusCandidate|OptimalBounds|Part(?:Bounds|Region)|S(?:crollToHereStartPoint|izeConstraints|ubviewForMouseEvent))|hostingFinished)|Hi(?:liteChanged|t(?:Test)?)|In(?:dicatorMoved|itialize|terceptSubviewClick|validateForSizeChange)|LayoutInfoChanged|O(?:ptimalBoundsChanged|wningWindowChanged)|RemovingSubControl|S(?:et(?:Cursor|Data|FocusPart)|imulateHit)|T(?:itleChanged|rack(?:ingAreaE(?:ntered|xited))?)|V(?:alueFieldChanged|isibilityChanged))))|D(?:ataBrowserDrawCustomItem|elegate(?:Get(?:GroupClasses|TargetClasses)|I(?:nstalled|sGroup)|Removed))|Font(?:PanelClosed|Selection)|Ge(?:sture(?:Ended|Magnify|Rotate|S(?:tarted|wipe))|tSelectedText)|H(?:IObject(?:C(?:onstruct|reatedFromArchive)|Destruct|Encode|GetInitParameters|I(?:nitialize|sEqual)|PrintDebugInfo)|ighLevelEvent|otKey(?:Exclusive|NoOptions|Pressed|Released))|Ink(?:Gesture|Point|Text)|KeyModifier(?:Fn(?:Bit|Mask)|NumLock(?:Bit|Mask))|L(?:eaveInQueue|oopIdleTimer(?:Idling|St(?:arted|opped)))|M(?:enu(?:B(?:ar(?:Hidden|Shown)|e(?:comeScrollable|ginTracking))|C(?:alculateSize|easeToBeScrollable|hangeTrackingMode|losed|reateFrameView)|D(?:ispose|rawItem(?:Content)?)|En(?:ableItems|dTracking)|GetFrameBounds|M(?:atchKey|easureItem(?:Height|Width))|Opening|Populate|TargetItem)|ouse(?:Button(?:Primary|Secondary|Tertiary)|D(?:own|ragged)|E(?:ntered|xited)|Moved|Scroll|Up|Wheel(?:Axis(?:X|Y)|Moved)))|OffsetToPos|P(?:aram(?:A(?:EEvent(?:Class|ID)|TSUFont(?:ID|Size)|ccessib(?:ilityEventQueued|le(?:A(?:ction(?:Description|Name(?:s)?)|ttribute(?:Name(?:s)?|Parameter|Settable|Value))|Child|Object))|fterDelegates|ppleEvent(?:Reply)?|ttributes|vailableBounds)|B(?:eforeDelegates|ounds)|C(?:G(?:ContextRef|ImageRef)|TFontDescriptor|andidateText|lick(?:Activation|Count)|o(?:mboBoxListSelectedItemIndex|ntrol(?:Action|C(?:lickActivationResult|urrent(?:OwningWindow|Part))|D(?:ata(?:Buffer(?:Size)?|Tag)|raw(?:Depth|Engraved|InColor))|F(?:eatures|ocusEverything|rameMetrics)|Hit|I(?:n(?:dicator(?:DragConstraint|Offset|Region)|valRgn)|sGhosting)|Message|O(?:ptimalB(?:aselineOffset|ounds)|riginalOwningWindow)|P(?:ar(?:am|t(?:AutoRepeats|Bounds)?)|re(?:fersShape|viousPart))|Re(?:f|gion|sult)|Sub(?:Control|view)|Value|WouldAcceptDrop))|urrent(?:Bounds|Dock(?:Device|Rect)|MenuTrackingMode|Window))|D(?:ataBrowser(?:Item(?:ID|State)|PropertyID)|e(?:codingForEditor|legate(?:Group(?:Classes|Parameters)|Target(?:Classes)?)|vice(?:Color|Depth))|i(?:ctionary|mensions|rect(?:Object|ionInverted)|splay(?:ChangeFlags|Device))|ragRef)|E(?:nable(?:MenuForKeyEvent|d)|ventRef)|F(?:MFont(?:Family|S(?:ize|tyle))|ontColor)|G(?:Device|rafPort)|HI(?:Archive|Command|ObjectInstance|ViewTrackingArea)|I(?:mageSize|n(?:dex|it(?:Collection|Parameters)|k(?:Gesture(?:Bounds|Hotspot|Kind)|KeyboardShortcut|TextRef))|sInInstantMouser)|Key(?:Code|M(?:acCharCodes|odifiers)|Unicodes|boardType)|L(?:aunch(?:Err|RefCon)|ineSize)|M(?:a(?:gnificationAmount|ximumSize)|enu(?:Co(?:mmand(?:KeyBounds)?|ntext(?:Height)?)|D(?:i(?:rection|smissed)|rawState)|EventOptions|F(?:irstOpen|rameView)|I(?:conBounds|sPopup|tem(?:Bounds|Height|Index|Type|Width))|MarkBounds|PopupItem|Ref|T(?:extB(?:aseline|ounds)|ype)|Virtual(?:Bottom|Top))|inimumSize|o(?:dal(?:ClickResult|Window)|use(?:Button|Chord|Delta|Location|TrackingRef|Wheel(?:Axis|Delta|Smooth(?:HorizontalDelta|VerticalDelta))))|utableArray)|Ne(?:w(?:MenuTrackingMode|ScrollBarVariant)|xtControl)|Origin(?:alBounds)?|P(?:a(?:rentMenu(?:Item)?|steboardRef)|ost(?:Options|Target)|r(?:evious(?:Bounds|Dock(?:Device|Rect)|Window)|ocessID))|R(?:e(?:ason|placementText|sult)|gnHandle|otationAmount)|S(?:crapRef|ervice(?:CopyTypes|MessageName|PasteTypes|UserData)|hape|tartControl|wipeDirection|ystemUI(?:Mode|Options))|T(?:SM(?:DocAccess(?:BaselineDelta|CharacterCount|EffectiveRange|L(?:ineBounds|ockCount)|Re(?:ply(?:ATS(?:Font|UGlyphSelector)|C(?:T(?:FontRef|GlyphInfoRef)|haracter(?:Range|sPtr))|FontSize)|questedCharacterAttributes)|Send(?:C(?:haracter(?:Index|Range|sPtr)|omponentInstance)|RefCon))|Send(?:ComponentInstance|RefCon))|ablet(?:EventType|P(?:oint(?:Rec|erRec)|roximityRec))|ext(?:Input(?:GlyphInfoArray|Reply(?:A(?:TSFont|ttributedString)|CTFontRef|F(?:MFont|ont)|GlyphInfoArray|L(?:eadingEdge|ine(?:Ascent|Height))|MacEncoding|Point(?:Size)?|RegionClass|S(?:LRec|howHide)|Text(?:Angle|Offset)?)|Send(?:AttributedString|C(?:lauseRng|omponentInstance|urrentPoint)|DraggingMode|FixLen|GlyphInfoArray|HiliteRng|KeyboardEvent|LeadingEdge|MouseEvent|PinRng|Re(?:fCon|placeRange)|S(?:LRec|howHide)|Text(?:Offset|Service(?:Encoding|MacEncoding))?|UpdateRng))|Length|Selection)|oolbar(?:Display(?:Mode|Size)|Item(?:ConfigData|Identifier)?)?|ransactionID)|U(?:nconfirmed(?:Range|Text)|serData)|View(?:AttributesDictionary|Size)|Window(?:ContentBounds|D(?:efPart|ragHiliteFlag)|Features|GrowRect|Mo(?:d(?:ality|ifiedFlag)|useLocation)|P(?:artCode|roxy(?:GWorldPtr|ImageRgn|OutlineRgn))|Re(?:f|gionCode)|StateChangedFlags|T(?:itle(?:FullWidth|TextWidth)|ransition(?:Action|Effect))))|osToOffset|r(?:iority(?:High|Low|Standard)|ocessCommand))|QueueOptionsNone|R(?:awKey(?:Down|ModifiersChanged|Repeat|Up)|emoveFromQueue)|S(?:crollable(?:GetInfo|InfoChanged|ScrollTo)|e(?:archField(?:CancelClicked|SearchClicked)|rvice(?:Copy|GetTypes|P(?:aste|erform)))|howHideBottomWindow|ystem(?:Display(?:Reconfigured|sA(?:sleep|wake))|TimeDateChanged|UserSession(?:Activated|Deactivated)))|T(?:SMDocumentAccess(?:Get(?:Characters(?:Ptr(?:ForLargestBuffer)?)?|F(?:irstRectForRange|ont)|GlyphInfo|Length|SelectedRange)|LockDocument|UnlockDocument)|a(?:bletP(?:oint(?:er)?|roximity)|rget(?:DontPropagate|SendToAllHandlers))|ext(?:Accepted|DidChange|Input(?:FilterText|GetSelectedText|IsMouseEventInInlineInputArea|OffsetToPos|PosToOffset|ShowHideBottomWindow|U(?:nicode(?:ForKeyEvent|Text)|pdateActiveInputArea))|ShouldChangeInRange)|oolbar(?:BeginMultiChange|CreateItem(?:FromDrag|WithIdentifier)|Display(?:ModeChanged|SizeChanged)|EndMultiChange|Get(?:AllowedIdentifiers|DefaultIdentifiers|SelectableIdentifiers)|Item(?:A(?:cceptDrop|dded)|C(?:ommandIDChanged|reateCustomView)|EnabledStateChanged|GetPersistentData|HelpTextChanged|ImageChanged|LabelChanged|PerformAction|Removed|SelectedStateChanged|View(?:ConfigFor(?:Mode|Size)|E(?:nterConfigMode|xitConfigMode))|WouldAcceptDrop)|LayoutChanged))|U(?:nicodeForKeyEvent|pdateActiveInputArea)|Volume(?:Mounted|Unmounted)|Window(?:A(?:ctivated|ttributesChanged)|BoundsChang(?:ed|ing)|C(?:lose(?:All|d)?|o(?:l(?:laps(?:e(?:All|d)?|ing)|orSpaceChanged)|n(?:strain|textualMenuSelect))|ursorChange)|D(?:e(?:activated|f(?:D(?:ispose|ra(?:gHilite|w(?:Frame|GrowBox|Part)))|Get(?:GrowImageRegion|Region)|HitTest|Init|M(?:easureTitle|odified)|S(?:etupProxyDragImage|tateChanged)))|ispose|ra(?:g(?:Completed|Hilite|Started)|w(?:Frame|GrowBox|Part|er(?:Clos(?:ed|ing)|Open(?:ed|ing)))))|Expand(?:All|ed|ing)?|F(?:ocus(?:Acquired|Content|Drawer|Lost|Re(?:linquish|stored)|Toolbar)|ullScreenE(?:nter(?:Completed|Started)|xit(?:Completed|Started)))|Get(?:Click(?:Activation|Modality)|DockTileMenu|FullScreenContentSize|GrowImageRegion|IdealS(?:ize|tandardState)|M(?:aximumSize|inimumSize)|Region)|H(?:andle(?:Activate|Deactivate)|i(?:d(?:den|ing)|tTest))|Init|M(?:easureTitle|odified)|P(?:a(?:int|thSelect)|roxy(?:BeginDrag|EndDrag))|Res(?:ize(?:Completed|Started)|tore(?:FromDock|dAfterRelaunch))|S(?:etupProxyDragImage|h(?:eet(?:Clos(?:ed|ing)|Open(?:ed|ing))|ow(?:ing|n))|tateChanged)|T(?:itleChanged|oolbarSwitchMode|ransition(?:Completed|Started))|UpdateDockTile|Zoom(?:All|ed)?))|ryKCEventMask)|x(?:actMatchThread|cludedMemoryException|itIdle|p(?:ertCharactersSelector|o(?:nentsO(?:ffSelector|nSelector)|rtedFolderAliasType))|t(?:AudioFile(?:Error_(?:AsyncWrite(?:BufferOverflow|TooLarge)|Invalid(?:ChannelMap|DataFormat|OperationOrder|Property(?:Size)?|Seek)|MaxPacketSizeUnknown|NonPCMClientFormat)|Property_(?:Audio(?:Converter|File)|C(?:lient(?:ChannelLayout|DataFormat|MaxPacketSize)|o(?:decManufacturer|nverterConfig))|File(?:ChannelLayout|DataFormat|LengthFrames|MaxPacketSize)|IOBuffer(?:SizeBytes)?|PacketTable))|en(?:dedFlag(?:Has(?:CustomBadge|RoutingInfo)|ObjectIsBusy|sAreInvalid)|sion(?:DisabledFolderType|Folder(?:AliasType|Type)|s(?:DisabledFolderIcon|FolderIcon(?:Resource)?))))))|F(?:A(?:AttachCommand|EditCommand|FileParam|IndexParam|RemoveCommand|S(?:erverApp|uiteCode))|BC(?:a(?:ccess(?:Canceled|orStoreFailed)|ddDocFailed|llocFailed|nalysisNotAvailable)|bad(?:IndexFile(?:Version)?|Param|SearchSession)|com(?:mitFailed|pactionFailed)|deletionFailed|f(?:ileNotIndexed|lushFailed)|i(?:llegalSessionChange|ndex(?:CreationFailed|DiskIOFailed|FileDestroyed|Not(?:Available|Found)|ing(?:Canceled|Failed)))|m(?:ergingFailed|oveFailed)|no(?:IndexesFound|S(?:earchSession|uchHit))|s(?:earchFailed|omeFilesNotIndexed|ummarizationCanceled)|tokenizationFailed|v(?:TwinExceptionErr|alidationFailed))|M(?:CurrentFilterFormat|Font(?:C(?:allbackFilterSelector|ontainer(?:AccessErr|FilterSelector))|DirectoryFilterSelector|F(?:amilyCallbackFilterSelector|ileRefFilterSelector)|T(?:ableAccessErr|echnologyFilterSelector))|GenerationFilterSelector|I(?:nvalidFont(?:Err|FamilyErr)|teration(?:Completed|ScopeModifiedErr))|PostScriptFontTechnology|TrueTypeFontTechnology)|N(?:DirectoryModifiedMessage|No(?:ImplicitAllSubscription|tifyInBackground)|S(?:Bad(?:FlattenedSizeErr|ProfileVersionErr|ReferenceVersionErr)|DuplicateReferenceErr|In(?:sufficientDataErr|valid(?:ProfileErr|ReferenceErr))|MismatchErr|NameNotFoundErr))|PUNotNeeded|S(?:Al(?:iasInfo(?:F(?:SInfo|inderInfo)|I(?:Ds|sDirectory)|None|TargetCreateDate|Volume(?:CreateDate|Flags))|lo(?:c(?:AllOrNothingMask|ContiguousMask|DefaultFlags|NoRoundUpMask|ReservedMask)|wConcurrentAsyncIO(?:Bit|Mask)))|CatInfo(?:A(?:ccessDate|llDates|ttrMod)|BackupDate|C(?:ontentMod|reateDate)|DataSizes|F(?:SFileSecurityRef|inder(?:Info|XInfo))|GettableInfo|No(?:de(?:Flags|ID)|ne)|P(?:arentDirID|ermissions)|R(?:eserved|srcSizes)|S(?:et(?:Ownership|tableInfo)|haringFlags)|TextEncoding|User(?:Access|Privs)|V(?:alence|olume))|E(?:jectVolumeForceEject|ventStream(?:CreateFlag(?:FileEvents|IgnoreSelf|No(?:Defer|ne)|UseCFTypes|WatchRoot)|Event(?:Flag(?:EventIdsWrapped|HistoryDone|Item(?:C(?:hangeOwner|reated)|FinderInfoMod|I(?:nodeMetaMod|s(?:Dir|File|Symlink))|Modified|Re(?:moved|named)|XattrMod)|KernelDropped|M(?:ount|ustScanSubDirs)|None|RootChanged|U(?:nmount|serDropped))|IdSinceNow)))|F(?:ileOperation(?:D(?:efaultOptions|oNotMoveAcrossVolumes)|Overwrite|Skip(?:Preflight|SourcePermissionErrors))|orceRead(?:Bit|Mask))|I(?:nvalidVolumeRefNum|terate(?:Delete|Flat|Reserved|Subtree))|KMountVersion|MountServer(?:M(?:arkDoNotDisplay|ount(?:OnMountDir|WithoutNotification))|SuppressConnectionUI)|N(?:ewLine(?:Bit|CharMask|Mask)|o(?:Cache(?:Bit|Mask)|de(?:CopyProtect(?:Bit|Mask)|DataOpen(?:Bit|Mask)|ForkOpen(?:Bit|Mask)|HardLink(?:Bit|Mask)|I(?:nShared(?:Bit|Mask)|s(?:Directory(?:Bit|Mask)|Mounted(?:Bit|Mask)|SharePoint(?:Bit|Mask)))|Locked(?:Bit|Mask)|ResOpen(?:Bit|Mask))))|OperationStage(?:Complete|Preflighting|Running|Undefined)|P(?:athMakeRefD(?:efaultOptions|oNotFollowLeafSymlink)|leaseCache(?:Bit|Mask))|R(?:dVerify(?:Bit|Mask)|eplaceObject(?:D(?:efaultOptions|oNotCheckObjectWriteAccess)|PreservePermissionInfo|Replace(?:Metadata|PermissionInfo)|SaveOriginalAsABackup))|UnmountVolumeForceUnmount|Vol(?:Flag(?:DefaultVolume(?:Bit|Mask)|FilesOpen(?:Bit|Mask)|HardwareLocked(?:Bit|Mask)|JournalingActive(?:Bit|Mask)|SoftwareLocked(?:Bit|Mask))|Info(?:B(?:ackupDate|locks)|C(?:heckedDate|reateDate)|D(?:ataClump|irCount|riveInfo)|F(?:SInfo|i(?:leCount|nderInfo)|lags)|GettableInfo|ModDate|N(?:ext(?:Alloc|ID)|one)|RsrcClump|S(?:ettableInfo|izes))))|TPServerIcon|avorite(?:ItemsIcon|sFolder(?:Icon|Type))|e(?:male|tchReference)|i(?:l(?:eSystemSupportFolderType|lScreen)|nd(?:ByContent(?:FolderType|IndexesFolderType|PluginsFolderType)|SupportFolderType|erIcon)|rst(?:FailKCStopOn|IOKitNotificationType|MagicBusyFiletype|PassKCStopOn)|tToScreen)|l(?:avorType(?:Clipping(?:Filename|Name)|DragToTrashOnly|FinderNoTrackingBehavior|UnicodeClipping(?:Filename|Name))|euronsSelector|o(?:ating(?:PointException|Window(?:Class|Definition))|ppyIconResource))|o(?:lder(?:Action(?:Code|sFolderType)|C(?:losedEvent|reated(?:AdminPrivs(?:Bit)?|Invisible(?:Bit)?|NameLocked(?:Bit)?))|I(?:n(?:LocalOrRemoteUserFolder|RemoteUserFolderIfAvailable(?:Bit)?|UserFolder(?:Bit)?)|tems(?:AddedEvent|RemovedEvent))|M(?:anager(?:FolderInMacOS9FolderIfMacOSXIsInstalled(?:Bit|Mask)|LastDomain|N(?:ewlyCreatedFolder(?:IsLocalizedBit|ShouldHaveDotLocalizedCreatedWithinMask)|otCreatedOnRemoteVolumes(?:Bit|Mask)))|ustStayOnSameVolume(?:Bit)?)|NeverMatchedInIdentifyFolder(?:Bit)?|OpenedEvent|TrackedByAlias(?:Bit)?|WindowMovedEvent)|nt(?:A(?:lbanianLanguage|mharic(?:Language|Script)|r(?:abic(?:Language|Script)|menian(?:Language|Script))|ssameseLanguage|ymaraLanguage|zerbaijan(?:ArLanguage|iLanguage))|B(?:asqueLanguage|engali(?:Language|Script)|u(?:lgarianLanguage|rmese(?:Language|Script))|yelorussianLanguage)|C(?:atalanLanguage|h(?:ewaLanguage|ineseScript)|o(?:llectionsFolderType|pyrightName)|roatianLanguage|ustom(?:16BitScript|8(?:16BitScript|BitScript)|Platform)|yrillicScript|zechLanguage)|D(?:anishLanguage|e(?:s(?:criptionName|igner(?:Name|URLName))|vanagariScript)|utchLanguage|zongkhaLanguage)|E(?:astEuropeanRomanScript|nglishLanguage|s(?:perantoLanguage|tonianLanguage)|thiopicScript|xtendedArabicScript)|F(?:a(?:eroeseLanguage|milyName|rsiLanguage)|innishLanguage|lemishLanguage|renchLanguage|ullName)|G(?:allaLanguage|e(?:ezScript|orgian(?:Language|Script)|rmanLanguage)|reek(?:Language|Script)|u(?:araniLanguage|jarati(?:Language|Script)|rmukhiScript))|H(?:ebrew(?:Language|Script)|indiLanguage|ungarianLanguage)|I(?:SO10646_1993Semantics|celandicLanguage|ndonesianLanguage|rishLanguage|talianLanguage)|Ja(?:panese(?:Language|Script)|vaneseRomLanguage)|K(?:a(?:nnada(?:Language|Script)|shmiriLanguage|zakhLanguage)|hmer(?:Language|Script)|irghizLanguage|orean(?:Language|Script)|urdishLanguage)|L(?:a(?:o(?:Language|tianScript)|ppishLanguage|stReservedName|t(?:inLanguage|vianLanguage))|ettishLanguage|i(?:cense(?:DescriptionName|InfoURLName)|thuanianLanguage))|M(?:a(?:c(?:CompatibleFullName|edonianLanguage|intoshPlatform)|l(?:a(?:gasyLanguage|y(?:ArabicLanguage|RomanLanguage|alam(?:Language|Script)))|teseLanguage)|nufacturerName|rathiLanguage)|icrosoft(?:Platform|S(?:tandardScript|ymbolScript)|UCS4Script)|o(?:ldavianLanguage|ngolian(?:CyrLanguage|Language|Script)))|N(?:epaliLanguage|o(?:Language(?:Code)?|Name(?:Code)?|Platform(?:Code)?|Script(?:Code)?|rwegianLanguage))|Or(?:iya(?:Language|Script)|omoLanguage)|P(?:ashtoLanguage|ersianLanguage|o(?:lishLanguage|rtugueseLanguage|st(?:ScriptCIDName|scriptName))|referred(?:FamilyName|SubfamilyName)|unjabiLanguage)|QuechuaLanguage|R(?:SymbolScript|eservedPlatform|oman(?:Script|ianLanguage)|u(?:andaLanguage|ndiLanguage|ssian(?:Language)?))|S(?:a(?:amiskLanguage|mpleTextName|nskritLanguage)|e(?:lection(?:ATSUIType|CoreTextType|QD(?:StyleVersionZero|Type))|rbianLanguage)|i(?:mp(?:ChineseLanguage|leChineseScript)|n(?:dhi(?:Language|Script)|halese(?:Language|Script)))|l(?:avicScript|ov(?:akLanguage|enianLanguage))|omaliLanguage|panishLanguage|tyleName|u(?:itcaseIcon|ndaneseRomLanguage)|w(?:ahiliLanguage|edishLanguage))|T(?:a(?:galogLanguage|jikiLanguage|mil(?:Language|Script)|tarLanguage)|elugu(?:Language|Script)|hai(?:Language|Script)|i(?:betan(?:Language|Script)|grinyaLanguage)|rad(?:ChineseLanguage|emarkName|itionalChineseScript)|urk(?:ishLanguage|menLanguage))|U(?:ighurLanguage|krainianLanguage|ni(?:code(?:DefaultSemantics|Platform|V(?:1_1Semantics|2_0(?:BMPOnlySemantics|FullCoverageSemantics)|4_0VariationSequenceSemantics)|_FullRepertoire)|nterpretedScript|queName)|rduLanguage|zbekLanguage)|V(?:e(?:ndorURLName|rsionName)|ietnamese(?:Language|Script))|WelshLanguage|YiddishLanguage|sFolder(?:Icon(?:Resource)?|Type))|r(?:kInfoFlags(?:FileLocked(?:Bit|Mask)|LargeFile(?:Bit|Mask)|Modified(?:Bit|Mask)|OwnClump(?:Bit|Mask)|Resource(?:Bit|Mask)|SharedWrite(?:Bit|Mask)|Write(?:Bit|Locked(?:Bit|Mask)|Mask))|m(?:FeedCharCode|InterrobangO(?:ffSelector|nSelector))|wardArrowIcon)|urByteCode)|ra(?:ctionsType|gment(?:IsPrepared|NeedsPreparing)|me(?:buffer(?:DisableAltivecAccess|Supports(?:CopybackCache|GammaCorrection|WritethruCache))|worksFolderType))|u(?:ll(?:TrashIcon(?:Resource)?|Width(?:CJKRomanSelector|IdeographsSelector|KanaSelector))|nctionKeyCharCode))|G(?:SSSelect(?:Ge(?:nericToRealID|t(?:DefaultScriptingComponent|ScriptingComponent(?:FromStored)?))|OutOfRange|RealToGenericID|SetDefaultScriptingComponent)|UARD_EXC_(?:DE(?:ALLOC_GAP|STROY)|I(?:MMOVABLE|N(?:CORRECT_GUARD|VALID_(?:ARGUMENT|NAME|RIGHT|VALUE)))|KERN_(?:FAILURE|NO_SPACE|RESOURCE)|MOD_REFS|R(?:CV_(?:GUARDED_DESC|INVALID_NAME)|IGHT_EXISTS)|S(?:E(?:ND_INVALID_(?:R(?:EPLY|IGHT)|VOUCHER)|T_CONTEXT)|TRICT_REPLY)|UNGUARDED)|e(?:n(?:EditorsFolderType|er(?:alFailureErr|ic(?:ApplicationIcon(?:Resource)?|C(?:DROMIcon(?:Resource)?|o(?:mponent(?:Icon|Version)|ntrol(?:PanelIcon|StripModuleIcon)))|D(?:eskAccessoryIcon(?:Resource)?|ocumentIcon(?:Resource)?)|E(?:ditionFileIcon(?:Resource)?|xtensionIcon(?:Resource)?)|F(?:ileServerIcon(?:Resource)?|loppyIcon|o(?:lderIcon(?:Resource)?|nt(?:Icon|ScalerIcon)))|HardDiskIcon(?:Resource)?|IDiskIcon|KCItemAttr|MoverObjectIcon(?:Resource)?|NetworkIcon|P(?:CCardIcon|asswordKCItemClass|referencesIcon(?:Resource)?)|QueryDocumentIcon(?:Resource)?|R(?:AMDiskIcon(?:Resource)?|emovableMediaIcon)|S(?:haredLibaryIcon|tationeryIcon(?:Resource)?|uitcaseIcon(?:Resource)?)|URLIcon|W(?:ORMIcon|indowIcon))))|t(?:AE(?:TE|UT)|DebugOption|Power(?:Info|Level)|SelectedText|WakeOnNetInfo))|lyphCollection(?:Adobe(?:CNS1|GB1|Japan(?:1|2)|Korea1)|GID|Unspecified)|r(?:aphicEQParam_NumberOfBands|idIcon|oup(?:I(?:D2Name|con)|Name2ID))|uestUserIcon)|H(?:ALOutputParam_Volume|FS(?:A(?:llocationFileID|ttribute(?:DataFileID|sFileID)|utoCandidate(?:Bit|Mask))|B(?:adBlockFileID|inaryCompare|o(?:gusExtentFileID|otVolumeInconsistent(?:Bit|Mask)))|C(?:a(?:seFolding|talog(?:FileID|KeyM(?:aximumLength|inimumLength)|NodeIDsReused(?:Bit|Mask)))|ontentProtection(?:Bit|Mask))|DoNotFastDevPin(?:Bit|Mask)|Extent(?:Density|KeyMaximumLength|sFileID)|F(?:astDev(?:Candidate(?:Bit|Mask)|Pinned(?:Bit|Mask))|i(?:le(?:Locked(?:Bit|Mask)|Record|ThreadRecord)|rstUserCatalogNodeID)|older(?:Record|ThreadRecord))|Has(?:Attributes(?:Bit|Mask)|ChildLink(?:Bit|Mask)|DateAdded(?:Bit|Mask)|FolderCount(?:Bit|Mask)|LinkChain(?:Bit|Mask)|Security(?:Bit|Mask))|JMountVersion|M(?:DBAttributesMask|ax(?:AttrNameLen|FileNameChars|VolumeNameChars))|Plus(?:Attr(?:Extents|ForkData|InlineData|MinNodeSize)|C(?:atalog(?:KeyM(?:aximumLength|inimumLength)|MinNodeSize)|reator)|Extent(?:Density|KeyMaximumLength|MinNodeSize)|F(?:ile(?:Record|ThreadRecord)|older(?:Record|ThreadRecord))|M(?:axFileNameChars|ountVersion)|SigWord|Version)|R(?:epairCatalogFileID|oot(?:FolderID|ParentID))|S(?:igWord|tartupFileID)|ThreadExists(?:Bit|Mask)|UnusedNode(?:Fix(?:Bit|Mask)|sFixDate)|Volume(?:HardwareLock(?:Bit|Mask)|Inconsistent(?:Bit|Mask)|Journaled(?:Bit|Mask)|NoCacheRequired(?:Bit|Mask)|S(?:oftwareLock(?:Bit|Mask)|paredBlocks(?:Bit|Mask))|Unmounted(?:Bit|Mask))|X(?:SigWord|Version))|I(?:ArchiveDecod(?:eSuperclassForUnregisteredObjects|ingForEditor)|C(?:lassOptionSingleton|o(?:m(?:boBox(?:Auto(?:CompletionAttribute|DisclosureAttribute|S(?:izeListAttribute|ortAttribute))|DisclosurePart|EditTextPart|List(?:Pixel(?:HeightTag|WidthTag)|Tag)|N(?:oAttributes|umVisibleItemsTag)|StandardAttributes)|mand(?:A(?:bout|ppHelp|rrangeInFront)|BringAllToFront|C(?:ancel|h(?:angeSpelling|eckSpelling(?:AsYouType)?)|l(?:ear|ose(?:All|File)?)|opy|u(?:stomizeToolbar|t)|ycleToolbarMode(?:Larger|Smaller))|From(?:Control|Menu|Window)|Hide(?:Others|Toolbar)?|IgnoreSpelling|LearnWord|M(?:aximize(?:All|Window)|inimize(?:All|Window))|New|O(?:K|pen|ther)|P(?:a(?:geSetup|ste)|r(?:eferences|int))|Quit(?:And(?:DiscardWindows|KeepWindows))?|R(?:e(?:do|vert)|otate(?:FloatingWindows(?:Backward|Forward)|Windows(?:Backward|Forward)))|S(?:ave(?:As)?|elect(?:All|Window)|how(?:All|CharacterPalette|HideFontPanel|SpellingPanel|Toolbar)|tartDictation)|Toggle(?:AllToolbars|FullScreen|Toolbar)|Undo|WindowList(?:Separator|Terminator)|ZoomWindow))|ordSpace(?:72DPIGlobal|ScreenPixel|View|Window)))|D(?:B(?:a(?:d(?:Log(?:PhysValuesErr|icalM(?:aximumErr|inimumErr))|ParameterErr)|seError)|ufferTooSmallErr)|DeviceNotReady|EndOfDescriptorErr|In(?:compatibleReportErr|v(?:alid(?:PreparsedDataErr|R(?:angePageErr|eport(?:LengthErr|TypeErr)))|erted(?:LogicalRangeErr|PhysicalRangeErr|UsageRangeErr)))|N(?:ot(?:EnoughMemoryErr|ValueArrayErr)|ull(?:PointerErr|StateErr))|Report(?:CountZeroErr|IDZeroErr|SizeZeroErr)|Success|U(?:nmatched(?:DesignatorRangeErr|StringRangeErr|UsageRangeErr)|sage(?:NotFoundErr|PageZeroErr))|V(?:alueOutOfRangeErr|ersionIncompatibleErr)|elegate(?:A(?:fter|ll)|Before))|HotKeyModeAll(?:Disabled(?:ExceptUniversalAccess)?|Enabled)|ImageView(?:AutoTransform(?:None|OnD(?:eactivate|isable))|ImageTag)|Layout(?:Bind(?:Bottom|Left|M(?:ax|in)|None|Right|Top)|InfoVersionZero|Position(?:Bottom|Center|Left|M(?:ax|in)|None|Right|Top)|ScaleAbsolute)|M(?:enu(?:AppendItem|CenterDirection|DismissedBy(?:A(?:ctivationChange|ppSwitch)|CancelMenuTracking|FocusChange|KeyEvent|Mouse(?:Down|Up)|Selection|Timeout|UserCancel)|LeftDirection|RightDirection)|odalClick(?:A(?:llowEvent|nnounce)|IsModal|RaiseWindow))|S(?:crollView(?:Options(?:AllowGrow|DisableSmoothScrolling|FillGrowArea|HorizScroll|VertScroll)|Page(?:Down|Left|Right|Up)|ScrollTo(?:Bottom|Left|Right|Top)|ValidOptions)|e(?:archField(?:Attributes(?:Cancel|SearchIcon)|NoAttributes)|gment(?:Behavior(?:Momentary|Radio|Sticky|Toggles)|NoAttributes|SendCmdToUserFocus|edViewKind))|hape(?:Enumerate(?:Init|Rect|Terminate)|ParseFrom(?:Bottom(?:Right)?|Left|Right|Top(?:Left)?)))|T(?:heme(?:F(?:ocusRing(?:Above|Below|Only)|rame(?:ListBox|TextField(?:Round(?:Mini|Small)?|Square)))|Gro(?:upBoxKind(?:Primary(?:Opaque)?|Secondary(?:Opaque)?)|wBox(?:KindNo(?:ne|rmal)|Size(?:Normal|Small)))|HeaderKind(?:List|Window)|Menu(?:DrawInfoVersion(?:One|Zero)|TitleDrawCondensed)|Orientation(?:Inverted|Normal)|S(?:egment(?:Adornment(?:Focus|LeadingSeparator|None|TrailingSeparator)|Kind(?:Inset|Normal|Textured)|Position(?:First|Last|Middle|Only)|Size(?:Mini|Normal|Small))|plitterAdornment(?:Metal|None))|T(?:ab(?:Adornment(?:Focus|LeadingSeparator|None|TrailingSeparator)|KindNormal|P(?:aneAdornmentNormal|osition(?:First|Last|Middle|Only))|Size(?:Mini|Normal|Small))|ext(?:BoxOption(?:DontClip|Engraved|None|StronglyVertical)|HorizontalFlush(?:Center|Default|Left|Right)|InfoVersion(?:One|Zero)|Truncation(?:Default|End|Middle|None)|VerticalFlush(?:Bottom|Center|Default|Top))))|oolbar(?:AutoSavesConfig|CommandPressAction|Display(?:Mode(?:Default|Icon(?:AndLabel|Only)|LabelOnly)|Size(?:Default|Normal|Small))|I(?:sConfigurable|tem(?:A(?:llowDuplicates|nchoredLeft)|CantBeRemoved|Disabled|IsSeparator|LabelDisabled|MutableAttrs|NoAttributes|Se(?:lected|ndCmdToUserFocus)|ValidAttrs))|NoAttributes|V(?:alidAttrs|iewDrawBackgroundTag))|ransform(?:Disabled|None|Selected))|View(?:A(?:llowsSubviews|ttribute(?:IsFieldEditor|SendCommandToUserFocus)|utoToggles)|C(?:lickableMetaPart|ontent(?:AlertIconType|CGImageRef|I(?:con(?:Ref|SuiteRef|TypeAndCreator)|mage(?:File|Resource))|MetaPart|N(?:SImage|one)|TextOnly))|D(?:isabledPart|oesNot(?:Draw|UseSpecialParts))|EntireView|F(?:eature(?:A(?:llowsSubviews|utoToggles)|DoesNot(?:Draw|UseSpecialParts)|GetsFocusOnClick|I(?:dlesWithTimer|gnoresClicks|nvertsUpDownValueMeaning|sOpaque)|Supports(?:Ghosting|LiveFeedback|RadioBehavior))|ocus(?:N(?:extPart|oPart)|OnAnyControl|PrevPart|Traditionally|WithoutWrapping))|GetsFocusOnClick|I(?:dlesWithTimer|gnoresClicks|n(?:activePart|dicatorPart|vertsUpDownValueMeaning)|sOpaque)|KindSignatureApple|NoPart|O(?:ffscreenImageUseWindowBackingResolution|paqueMetaPart)|S(?:endCommandToUserFocus|tructureMetaPart|upports(?:Ghosting|LiveFeedback|RadioBehavior))|ValidFeaturesForPanther|ZOrder(?:Above|Below))|Window(?:B(?:ackingLocation(?:Default|MainMemory|VideoMemory)|ehavior(?:Stationary|Transient)|it(?:A(?:syncDrag|uto(?:Calibration|ViewDragTracking))|C(?:anBeVisibleWithoutLogin|loseBox|o(?:llapseBox|mpositing))|DoesNot(?:Cycle|Hide)|F(?:rameworkScaled|ullScreen(?:Auxiliary|Primary))|Hi(?:deOn(?:FullScreen|Suspend)|ghResolutionCapable)|I(?:gnoreClicks|nWindowMenu)|LiveResize|No(?:Activates|Constrain|Shadow|T(?:exturedContentSeparator|itleBar)|Updates)|OpaqueForEvents|R(?:esizable|oundBottomBarCorners)|S(?:ideTitlebar|tandardHandler)|T(?:extured(?:SquareCorners)?|oolbarButton)|UnifiedTitleAndToolbar|ZoomBox))|CanJoinAllSpaces|D(?:epth(?:32Bit|64Bit|Float|Invalid)|ragPart)|ExposeHidden|IgnoreObscuringWindows|M(?:enu(?:Creator|WindowTag)|oveToActiveSpace)|S(?:caleMode(?:FrameworkScaled|Magnified|Unscaled)|haring(?:None|Read(?:Only|Write)))|Title(?:BarPart|ProxyIconPart)|VisibleInAllSpaces))|M(?:AbsoluteCenterAligned|Bottom(?:LeftCorner|RightCorner|Side)|C(?:FString(?:Content|LocalizedContent)|ontent(?:NotProvided(?:DontPropagate)?|Provided))|D(?:efaultSide|isposeContent)|H(?:elpMenuID|ideTag(?:Fade|Immediately))|I(?:llegalContentForMinimumState|nside(?:Bottom(?:CenterAligned|LeftCorner|RightCorner)|LeftCenterAligned|RightCenterAligned|Top(?:CenterAligned|LeftCorner|RightCorner)))|Left(?:BottomCorner|Side|TopCorner)|M(?:aximumContentIndex|inimumContentIndex)|NoContent|Outside(?:Bottom(?:CenterAligned|LeftAligned|RightAligned|ScriptAligned)|Left(?:BottomAligned|CenterAligned|TopAligned)|Right(?:BottomAligned|CenterAligned|TopAligned)|Top(?:CenterAligned|LeftAligned|RightAligned|ScriptAligned))|PascalStrContent|Right(?:BottomCorner|Side|TopCorner)|S(?:tr(?:ResContent|ingResContent)|upplyContent)|T(?:EHandleContent|extResContent|op(?:LeftCorner|RightCorner|Side)))|TTPServerIcon|a(?:lfWidth(?:CJKRomanSelector|IdeographsSelector|TextSelector)|n(?:dle(?:IsResource(?:Bit|Mask)|Locked(?:Bit|Mask)|Purgeable(?:Bit|Mask))|jaToHangul(?:Alt(?:OneSelector|T(?:hreeSelector|woSelector))|Selector))|rd(?:LinkFileType|wareCursor(?:DescriptorM(?:ajorVersion|inorVersion)|InfoM(?:ajorVersion|inorVersion)))|s(?:B(?:eenInited|undle)|CustomIcon|NoINITs))|e(?:brew(?:FigureSpaceVariant|StandardVariant)|lp(?:CharCode|DialogItem|Folder(?:Icon|Type)|Icon(?:Resource)?|TagEventHandlerTag|WindowClass))|i(?:deDiacriticsSelector|erarchicalFontMenuOption|gh(?:LevelEvent|ShelfParam_(?:CutOffFrequency|Gain))|nt(?:Advanced|Basic|Hidden)|passParam_(?:CutoffFrequency|Resonance)|raganaToKatakanaSelector|storicalLigaturesO(?:ffSelector|nSelector))|o(?:joCharactersSelector|meCharCode|rizontalConstraint)|uge(?:1BitMask|32BitData|4BitData|8Bit(?:Data|Mask))|yphen(?:To(?:EnDashO(?:ffSelector|nSelector)|MinusO(?:ffSelector|nSelector))|sToEmDashO(?:ffSelector|nSelector)))|I(?:BCarbonRuntime(?:CantFind(?:NibFile|Object)|ObjectNotOfRequestedType)|C(?:Attr(?:Locked(?:Bit|Mask)|NoChange|Volatile(?:Bit|Mask))|C(?:omponent(?:InterfaceVersion(?:0|1|2|3|4)?|Version)|reator)|EditPreferenceEvent(?:Class)?|File(?:SpecHeaderSize|Type)|Map(?:Binary(?:Bit|Mask)|DataFork(?:Bit|Mask)|FixedLength|Not(?:Incoming(?:Bit|Mask)|Outgoing(?:Bit|Mask))|Post(?:Bit|Mask)|ResourceFork(?:Bit|Mask))|N(?:ilProfileID|oUserInteraction(?:Bit|Mask)|umVersion)|Services(?:TCP(?:Bit|Mask)|UDP(?:Bit|Mask)))|MJaTypingMethod(?:Kana|Property|Roman)|O(?:A(?:nalogS(?:etupExpected|ignalLevel_(?:07(?:00_0(?:000|300)|14_0286)|1000_0400))|sync(?:C(?:allout(?:Count|FuncIndex|RefconIndex)|ompletionNotificationType)|Reserved(?:Count|Index)))|B(?:itsPerColorComponent(?:1(?:0|2|6)|6|8|NotSupported)|uiltinPanelPowerAttribute)|C(?:LUTPixels|SyncDisable|apturedAttribute|lamshellStateAttribute|o(?:lorimetry(?:AdobeRGB|BT(?:2(?:020|100)|601|709)|DCIP3|N(?:ativeRGB|otSupported)|WGRGB|sRGB|xvYCC)|nnect(?:MethodVarOutputSize|ion(?:BuiltIn|StereoSync))|pyback(?:Cache|InnerCache))|ursorControlAttribute)|D(?:PEvent(?:AutomatedTestRequest|ContentProtection|ForceRetrain|Idle|MCCS|RemoteControlCommandPending|S(?:inkSpecific|tart))|SCBlockPredEnable|e(?:f(?:ault(?:Cache|MemoryType)|erCLUTSetAttribute)|tailedTimingValid)|i(?:gitalSignal|splay(?:ColorMode|Dither(?:All|D(?:efault|isable)|FrameRateControl|RGBShift|Spatial|Temporal|YCbCr4(?:22Shift|44Shift))|ModeID(?:BootProgrammable|ReservedBase)|NeedsCEAUnderscan|PowerState(?:MinUsable|O(?:ff|n))|RGBColorComponentBits(?:1(?:0|2|4|6)|6|8|Unknown)|YCbCr4(?:22ColorComponentBits(?:1(?:0|2|4|6)|6|8|Unknown)|44ColorComponentBits(?:1(?:0|2|4|6)|6|8|Unknown))))|riverPowerAttribute|ynamicRange(?:Dolby(?:NormalMode|TunnelMode)|HDR10|NotSupported|SDR|TraditionalGammaHDR))|F(?:B(?:AVSignalType(?:D(?:P|VI)|HDMI|Unknown|VGA)|B(?:itRate(?:HBR(?:2)?|RBR)|lueGammaScaleAttribute)|C(?:hangedInterruptType|onnectInterruptType)|Display(?:Port(?:InterruptType|LinkChangeInterruptType|TrainingAttribute)|State(?:_(?:AlreadyActive|Mask|PipelineBlack|RestoredProfile))?)|FrameInterruptType|GreenGammaScaleAttribute|H(?:BLInterruptType|D(?:CPLimit_(?:AllowAll|NoHDCP(?:1x|20Type(?:0|1)))|RMetaDataAttribute))|Li(?:mitHDCP(?:Attribute|StateAttribute)|nk(?:Downspread(?:Max|None)|PreEmphasisLevel(?:0|1|2|3)|Scrambler(?:Alternate|Normal)|VoltageLevel(?:0|1|2|3)))|MCCSInterruptType|NS_(?:D(?:i(?:m|splayState(?:Mask|Shift))|oze)|Generation(?:Mask|Shift)|MessageMask|Rendezvous|Sleep|UnDim|Wake)|O(?:fflineInterruptType|nlineInterruptType)|RedGammaScaleAttribute|S(?:erverConnectType|haredConnectType|top|ystemAperture)|UserRequestProbe|V(?:BLInterruptType|ariableRefreshRate)|WakeInterruptType)|ixedCLUTPixels)|GDiagnose(?:ConnectType|GTraceType)|H(?:SyncDisable|ardwareCursorAttribute|ibernatePreview(?:Active|Updates))|In(?:hibitCache|ter(?:estCallout(?:Count|FuncIndex|RefconIndex|ServiceIndex)|lacedCEATiming))|KitNotication(?:MsgSizeMask|Type(?:Mask|SizeAdjShift))|M(?:a(?:p(?:Anywhere|C(?:ache(?:Mask|Shift)|opyback(?:Cache|InnerCache))|DefaultCache|InhibitCache|Overwrite|P(?:osted(?:CombinedReordered|Reordered|Write)|refault)|Re(?:a(?:dOnly|lTimeCache)|ference)|Static|U(?:nique|serOptionsMask)|Write(?:CombineCache|ThruCache))|tchingCallout(?:Count|FuncIndex|RefconIndex)|xPixelBits)|irror(?:Attribute|Default(?:Attribute)?|Forced|HWClipped|Is(?:Mirrored|Primary))|ono(?:DirectPixels|InverseDirectPixels))|N(?:TSCTiming|oSeparateSyncControl)|P(?:ALTiming|ixelEncoding(?:NotSupported|RGB444|YCbCr4(?:2(?:0|2)|44))|o(?:sted(?:CombinedReordered|Reordered|Write)|wer(?:Attribute|StateAttribute)))|R(?:GB(?:DirectPixels|Signed(?:DirectPixels|FloatingPointPixels))|ange(?:BitsPerColorComponent(?:1(?:0|2|6)|6|8|NotSupported)|Colorimetry(?:AdobeRGB|BT(?:2(?:020|100)|601|709)|DCIP3|N(?:ativeRGB|otSupported)|WGRGB|sRGB|xvYCC)|DynamicRange(?:Dolby(?:NormalMode|TunnelMode)|HDR10|NotSupported|SDR|TraditionalGammaHDR)|PixelEncoding(?:NotSupported|RGB444|YCbCr4(?:2(?:0|2)|44))|Supports(?:CompositeSync|InterlacedCEATiming(?:WithConfirm)?|S(?:eparateSyncs|ignal_(?:07(?:00_0(?:000|300)|14_0286)|1000_0400)|yncOnGreen)|VSyncSerration))|e(?:alTimeCache|gistryIterate(?:Parents|Recursively)))|S(?:cal(?:e(?:Can(?:BorderInsetOnly|DownSamplePixels|Rotate|S(?:caleInterlaced|upportInset)|UpSamplePixels)|Invert(?:X|Y)|Rotate(?:0|180|270|90|Flags)|S(?:tretch(?:Only|ToFit)|wapAxes))|ingInfoValid)|ervice(?:InteractionAllowed|M(?:atchedNotificationType|essageNotificationType)|PublishNotificationType|TerminatedNotificationType)|urface(?:Co(?:mponent(?:Name(?:Alpha|Blue|Chroma(?:Blue|Red)|Green|Luma|Red|Unknown)|Range(?:FullRange|Unknown|VideoRange|WideRange)|Type(?:Float|SignedInteger|Un(?:known|signedInteger)))|pyback(?:Cache|InnerCache))|DefaultCache|InhibitCache|Lock(?:AvoidSync|ReadOnly)|Map(?:C(?:acheShift|opyback(?:Cache|InnerCache))|DefaultCache|InhibitCache|Write(?:CombineCache|ThruCache))|Purgeable(?:Empty|KeepCurrent|NonVolatile|Volatile)|Subsampling(?:4(?:11|2(?:0|2))|None|Unknown)|Write(?:CombineCache|ThruCache))|y(?:nc(?:On(?:Blue|Green|Red)|PositivePolarity)|stemPowerAttribute))|T(?:iming(?:ID(?:Apple(?:NTSC_(?:FF(?:conv)?|ST(?:conv)?)|PAL_(?:FF(?:conv)?|ST(?:conv)?)|_(?:0x0_0hz_Offline|1(?:024x768_75hz|152x870_75hz)|5(?:12x384_60hz|60x384_60hz)|640x(?:4(?:00_67hz|80_67hz)|8(?:18_75hz|70_75hz))|832x624_75hz|FixedRateLCD))|FilmRate_48hz|GTF_640x480_120hz|Invalid|S(?:MPTE240M_60hz|ony_1(?:600x1024_76hz|920x1(?:080_(?:60hz|72hz)|200_76hz)))|VESA_(?:1(?:024x768_(?:60hz|7(?:0hz|5hz)|85hz)|152x864_75hz|280x(?:1024_(?:60hz|75hz|85hz)|960_(?:60hz|75hz|85hz))|360x768_60hz|600x1200_(?:6(?:0hz|5hz)|7(?:0hz|5hz)|8(?:0hz|5hz))|792x1344_(?:60hz|75hz)|856x1392_(?:60hz|75hz)|920x1440_(?:60hz|75hz))|640x480_(?:60hz|7(?:2hz|5hz)|85hz)|8(?:00x600_(?:56hz|60hz|7(?:2hz|5hz)|85hz)|48x480_60hz)))|RangeV(?:1|2))|riStateSyncs)|V(?:RAMSaveAttribute|SyncDisable)|W(?:SAA_(?:Accelerated|D(?:efer(?:End|Start)|riverOpen)|From_Accelerated|Hibernate|NonConsoleDevice|Reserved|S(?:leep|tateMask)|T(?:o_Accelerated|ransactional)|Unaccelerated)|indowServerActiveAttribute|rite(?:CombineCache|ThruCache)))|PFileServerIcon|S(?:OLatin(?:1(?:MusicCDVariant|StandardVariant)|Arabic(?:ExplicitOrderVariant|ImplicitOrderVariant|VisualOrderVariant)|Hebrew(?:ExplicitOrderVariant|ImplicitOrderVariant|VisualOrderVariant))|SDownloadsFolderType|p(?:BufferToSmallErr|Device(?:ActiveErr|InactiveErr)|Element(?:InListErr|NotInListErr)|InternalErr|ListBusyErr|System(?:ActiveErr|InactiveErr|ListErr)))|con(?:DialogItem|FamilyType|Services(?:1(?:024PixelDataARGB|28PixelDataARGB|6PixelDataARGB)|256PixelDataARGB|32PixelDataARGB|48PixelDataARGB|512PixelDataARGB|CatalogInfoMask|No(?:BadgeFlag|rmalUsageFlag)|UpdateIfNeededFlag))|d(?:eographic(?:Alt(?:F(?:iveSelector|ourSelector)|OneSelector|T(?:hreeSelector|woSelector)|ernativesType)|SpacingType)|leKCEvent(?:Mask)?)|ll(?:egal(?:ClockValueErr|InstructionException)|uminatedCapsSelector)|mmediate|n(?:DeferredTaskMask|NestedInterruptMask|SecondaryIntHandlerMask|UseErr|VBLTaskMask|dexFilesFolderType|equalityLigaturesO(?:ffSelector|nSelector)|feriorsSelector|itialCaps(?:AndSmallCapsSelector|Selector)|kInputMethodClass|putM(?:anagersFolderType|ethodsFolderType)|s(?:ertHierarchicalMenu|t(?:aller(?:LogsFolderType|ReceiptsFolderType)|ru(?:ctionBreakpointException|mentType_(?:A(?:UPreset|udiofile)|DLSPreset|EXS24|SF2Preset))))|te(?:gerException|rn(?:ation(?:ResourcesIcon|al(?:ResourcesIcon|SymbolsSelector))|et(?:EventClass|Folder(?:Icon|Type)|Location(?:A(?:FP|pple(?:ShareIcon|Talk(?:ZoneIcon)?))|Creator|F(?:TP(?:Icon)?|ile(?:Icon)?)|Generic(?:Icon)?|HTTP(?:Icon)?|Mail(?:Icon)?|N(?:NTP|SL(?:NeighborhoodIcon)?|ewsIcon))|P(?:asswordKCItemClass|lugInFolder(?:Icon|Type))|S(?:earchSitesFolder(?:Icon|Type)|itesFolderType))))|v(?:alid(?:CSClientErr|DeviceNumber|Font(?:Family)?|Generation|RegEntryErr)|ert(?:Highlighting|ed(?:BoxAnnotationSelector|CircleAnnotationSelector|RoundedBoxAnnotationSelector)|ingEncod(?:edPixel|ing(?:Shift)?))|isibleKCItemAttr))|s(?:Alias|Invisible|OnDesk|S(?:hared|tationery)|suer(?:KCItemAttr|URLKCItemAttr))|t(?:alicCJKRomanType|em(?:DisableBit|List)))|J(?:I(?:Journal(?:InFSMask|NeedInitMask|OnOtherDeviceMask)|S(?:19(?:78CharactersSelector|83CharactersSelector|90CharactersSelector)|2004CharactersSelector))|S(?:ClassAttributeNo(?:AutomaticPrototype|ne)|PropertyAttribute(?:Dont(?:Delete|Enum)|None|ReadOnly)|Type(?:Boolean|Nu(?:ll|mber)|Object|String|Undefined|dArrayType(?:ArrayBuffer|Float(?:32Array|64Array)|Int(?:16Array|32Array|8Array)|None|Uint(?:16Array|32Array|8(?:Array|ClampedArray)))))|UST(?:CurrentVersion|KashidaPriority|LetterPriority|NullPriority|Override(?:Limits|Priority|Unlimited)|Priority(?:Count|Mask)|S(?:pacePriority|tandardFormat)|Tag|Unlimited|noGlyphcode|pc(?:ConditionalAddAction|D(?:ecompositionAction|uctilityAction)|Glyph(?:RepeatAddAction|StretchAction)|UnconditionalAddAction))|apanese(?:BasicVariant|PostScript(?:PrintVariant|ScrnVariant)|St(?:andardVariant|dNoVerticalsVariant)|VertAtKuPlusTenVariant))|K(?:C(?:AuthType(?:D(?:PA|efault)|HTTPDigest|MSN|NTLM|RPA)|ProtocolType(?:A(?:FP|ppleTalk)|FTP(?:Account)?|HTTP|I(?:MAP|RC)|LDAP|NNTP|POP3|S(?:MTP|OCKS)|Telnet))|ER(?:N(?:C(?:rossStream(?:ResetNote)?|urrentVersion)|FormatMask|IndexArray|Line(?:EndKerning|Start)|No(?:CrossKerning|StakeNote|t(?:Applied|esRequested))|OrderedList|ResetCrossStream|S(?:impleArray|tateTable)|Tag|UnusedBits|V(?:ariation|ertical))|X(?:Action(?:OffsetMask|Type(?:AnchorPoints|Co(?:ntrolPoints|ordinates)|Mask))|C(?:ontrolPoint|rossStream(?:ResetNote)?|urrentVersion)|Descending|FormatMask|IndexArray|Line(?:EndKerning|Start)|No(?:CrossKerning|StakeNote|t(?:Applied|esRequested))|OrderedList|ResetCrossStream|S(?:impleArray|tateTable)|Tag|Unused(?:Bits|Flags)|V(?:a(?:luesAreLong|riation)|ertical)))|L(?:GroupIdentifier|I(?:con|dentifier)|K(?:CHR(?:Data|Kind|uchrKind)|ind)|L(?:anguageCode|ocalizedName)|Name|USKeyboard|uchr(?:Data|Kind))|a(?:na(?:SpacingType|ToRomanizationSelector)|takanaToHiraganaSelector)|e(?:epArrangedIcon|rnelExtensionsFolderType|y(?:board(?:ANSI|I(?:SO|nputMethodClass)|JIS|Layout(?:Icon|sFolderType)|Unknown)|chain(?:FolderType|ListChangedKCEvent))))|L(?:A(?:AllMorphemes|DefaultEdge|EndOfSourceTextMask|FreeEdge|IncompleteEdge|MorphemesArrayVersion|Speech(?:BagyouGodan|Chimei(?:Setsubigo)?|Do(?:kuritsugo|ushi)|Fu(?:kushi|tsuuMeishi)|G(?:agyouGodan|odanDoushi)|IchidanDoushi|J(?:inmei(?:Mei|Se(?:i|tsubigo))?|o(?:doushi|shi))|K(?:a(?:gyouGodan|henDoushi|ndoushi|tsuyou(?:Gokan|Katei|M(?:ask|eirei|izen)|Ren(?:tai|you)|Syuushi))|ei(?:douMeishi|you(?:doushi|shi))|igou|oyuuMeishi|uten)|M(?:agyouGodan|e(?:diumClassMask|ishi)|uhinshi)|NagyouGodan|R(?:agyouGodan|entaishi|oughClassMask)|S(?:a(?:gyouGodan|hen(?:Doushi|Meishi))|e(?:iku|t(?:su(?:bi(?:Chimei|go)|zokushi)|tougo))|oshikimei(?:Setsubigo)?|trictClassMask|uu(?:jiSet(?:subigo|tougo)|shi))|T(?:a(?:gyouGodan|nkanji)|outen)|WagyouGodan|ZahenDoushi))|CAR(?:C(?:tlPointFormat|urrentVersion)|LinearFormat|Tag)|S(?:A(?:ccept(?:AllowLoginUI|Default)|pp(?:DoesNot(?:ClaimTypeErr|SupportSchemeWarning)|InTrashErr|licationNotFoundErr)|ttributeNot(?:FoundErr|SettableErr))|CannotSetInfoErr|Data(?:Err|TooOldErr|UnavailableErr)|ExecutableIncorrectFormat|GarbageCollectionUnsupportedErr|Incompatible(?:ApplicationVersionErr|SystemVersionErr)|Launch(?:A(?:nd(?:DisplayErrors|Hide(?:Others)?|Print)|sync)|D(?:efaults|ont(?:AddToRecents|Switch))|InProgressErr|NewInstance)|MultipleSessionsNotSupportedErr|No(?:32BitEnvironmentErr|ClassicEnvironmentErr|ExecutableErr|LaunchPermissionErr|R(?:egistrationInfoErr|osettaEnvironmentErr)|t(?:AnApplicationErr|InitializedErr|RegisteredErr))|Roles(?:All|Editor|None|Shell|Viewer)|S(?:erverCommunicationErr|haredFileList(?:DoNotMountVolumes|NoUserInteraction))|Unknown(?:Creator|Err|Type(?:Err)?))|TAGCurrentVersion|a(?:belKCItemAttr|nguageTagType|rge(?:1BitMask|32BitData|4Bit(?:Data|IconSize)|8Bit(?:Data|IconSize|Mask)|IconSize)|st(?:DomainConstant|FeatureType|IOKitNotificationType|MagicBusyFiletype)|unch(?:ToGetTerminology|erItemsFolderType))|e(?:ft(?:ArrowCharCode|ToRight)|tterCaseType)|i(?:braryAssistantsFolderType|gaturesType|miterParam_(?:AttackTime|DecayTime|PreGain)|n(?:e(?:F(?:eedCharCode|inalSwashesO(?:ffSelector|nSelector))|InitialSwashesO(?:ffSelector|nSelector)|arPCMFormatFlag(?:Is(?:AlignedHigh|BigEndian|Float|Non(?:Interleaved|Mixable)|Packed|SignedInteger)|s(?:AreAllClear|SampleFraction(?:Mask|Shift))))|guisticRearrangement(?:O(?:ffSelector|nSelector)|Type))|stDef(?:ProcPtr|Standard(?:IconType|TextType)|UserProcType))|o(?:c(?:al(?:Domain|PPDDomain|e(?:A(?:llPartsMask|ndVariantNameMask)|Language(?:Mask|VariantMask)|NameMask|OperationVariantNameMask|Region(?:Mask|VariantMask)|Script(?:Mask|VariantMask)|s(?:BufferTooSmallErr|DefaultDisplayStatus|Folder(?:Icon|Type)|TableFormatErr)))|k(?:KCEvent(?:Mask)?|ed(?:BadgeIcon|Icon)))|g(?:osO(?:ffSelector|nSelector)|sFolderType)|w(?:PassParam_(?:CutoffFrequency|Resonance)|erCase(?:NumbersSelector|PetiteCapsSelector|SmallCapsSelector|Type))))|M(?:68kISA|D(?:Label(?:LocalDomain|UserDomain)|Query(?:AllowFSTranslation|ReverseSortOrderFlag|Synchronous|WantsUpdates))|IDI(?:DriversFolderType|I(?:DNotUnique|nvalid(?:Client|Port|UniqueID))|M(?:essageSendErr|sg(?:IOError|Object(?:Added|Removed)|PropertyChanged|Se(?:rialPortOwnerChanged|tupChanged)|ThruConnectionsChanged))|No(?:C(?:onnection|urrentSetup)|tPermitted)|Object(?:NotFound|Type_(?:De(?:stination|vice)|E(?:ntity|xternal(?:De(?:stination|vice)|Entity|Source))|Other|Source))|Se(?:rverStartErr|tupFormatErr)|Unknown(?:E(?:ndpoint|rror)|Property)|Wrong(?:EndpointType|PropertyType|Thread))|OR(?:T(?:C(?:o(?:ntextualType|ver(?:Descending|IgnoreVertical|TypeMask|Vertical))|urr(?:Insert(?:Before|Count(?:Mask|Shift)|KashidaLike)|JustTableCount(?:Mask|Shift)|entVersion))|DoInsertionsBefore|I(?:nsertion(?:Type|sCountMask)|sSplitVowelPiece)|Lig(?:FormOffset(?:Mask|Shift)|LastAction|StoreLigature|atureType)|Mark(?:Insert(?:Before|Count(?:Mask|Shift)|KashidaLike)|JustTableCount(?:Mask|Shift))|RearrangementType|SwashType|Tag|ra(?:CDx(?:A(?:B)?|BA)?|D(?:Cx(?:A(?:B)?|BA)?|x(?:A(?:B)?|BA)?)|NoAction|x(?:A(?:B)?|BA)))|X(?:C(?:over(?:Descending|IgnoreVertical|LogicalOrder|TypeMask|Vertical)|urrentVersion)|Tag))|P(?:A(?:ddressSpaceInfoVersion|llocate(?:1(?:024ByteAligned|6ByteAligned)|32ByteAligned|4096ByteAligned|8ByteAligned|AltiVecAligned|ClearMask|DefaultAligned|GloballyMask|InterlockAligned|MaxAlignment|No(?:CreateMask|GrowthMask)|ResidentMask|VM(?:PageAligned|XAligned))|nyRemoteContext|syncInterruptRemoteContext)|BlueBlockingErr|Cr(?:eateTask(?:NotDebuggableMask|SuspendedMask|TakesAllExceptionsMask|ValidOptionsMask)|iticalRegionInfoVersion)|DeletedErr|E(?:G4Object_(?:AAC_(?:L(?:C|TP)|Main|S(?:BR|SR|calable))|CELP|HVXC|TwinVQ)|ventInfoVersion)|HighLevelDebugger|I(?:n(?:sufficientResourcesErr|terruptRemoteContext|validIDErr)|terationEndErr)|LowLevelDebugger|M(?:axAllocSize|idLevelDebugger)|N(?:anokernelNeedsMemoryErr|o(?:ID|tificationInfoVersion))|OwningProcessRemoteContext|Pr(?:eserveTimerIDMask|ivilegedErr|ocess(?:CreatedErr|TerminatedErr))|QueueInfoVersion|SemaphoreInfoVersion|T(?:ask(?:AbortedErr|Blocked(?:Err)?|CreatedErr|InfoVersion|Propagate(?:Mask)?|R(?:e(?:ady|sume(?:Branch(?:Mask)?|Mask|Step(?:Mask)?))|unning)|St(?:ate(?:32BitMemoryException|FPU|Machine|Registers|TaskInfo|Vectors)|oppedErr))|ime(?:IsD(?:eltaMask|urationMask)|outErr)))|a(?:c(?:Arabic(?:AlBayanVariant|StandardVariant|T(?:huluthVariant|rueTypeVariant))|C(?:roatian(?:CurrencySignVariant|DefaultVariant|EuroSignVariant)|yrillic(?:CurrSign(?:StdVariant|UkrVariant)|DefaultVariant|EuroSignVariant))|Farsi(?:StandardVariant|TrueTypeVariant)|Greek(?:DefaultVariant|EuroSignVariant|NoEuroSignVariant)|He(?:brew(?:FigureSpaceVariant|StandardVariant)|lpVersion)|Icelandic(?:St(?:andardVariant|d(?:CurrSignVariant|DefaultVariant|EuroSignVariant))|T(?:T(?:CurrSignVariant|DefaultVariant|EuroSignVariant)|rueTypeVariant))|Japanese(?:BasicVariant|PostScript(?:PrintVariant|ScrnVariant)|St(?:andardVariant|dNoVerticalsVariant)|VertAtKuPlusTenVariant)|MemoryMaximumMemoryManagerBlockSize|OSReadMe(?:FolderIcon|sFolderType)|Roman(?:CurrencySignVariant|DefaultVariant|EuroSignVariant|Latin1(?:CroatianVariant|DefaultVariant|IcelandicVariant|RomanianVariant|StandardVariant|TurkishVariant)|StandardVariant|ian(?:CurrencySignVariant|DefaultVariant|EuroSignVariant))|VT100(?:CurrencySignVariant|DefaultVariant|EuroSignVariant)|hineNameStrID)|gic(?:BusyCreationDate|TemporaryItemsFolderType)|le|nagedItemsFolderType|t(?:h(?:SymbolsSelector|ematical(?:ExtrasType|GreekO(?:ffSelector|nSelector)))|rixMixerParam_(?:Enable|P(?:ost(?:AveragePower(?:Linear)?|PeakHoldLevel(?:Linear)?)|re(?:AveragePower(?:Linear)?|PeakHoldLevel(?:Linear)?))|Volume))|x(?:AsyncArgs|InputLengthOfAppleJapaneseEngine|K(?:anjiLengthInAppleJapaneseDictionary|eyLength)|YomiLengthInAppleJapaneseDictionary|imumBlocksIn4GB))|enu(?:A(?:ppleLogo(?:FilledGlyph|OutlineGlyph)|ttr(?:AutoDisable|CondenseSeparators|DoNot(?:CacheImage|UseUserCommandKeys)|ExcludesMarkColumn|Hidden|UsePencilGlyph))|BlankGlyph|C(?:GImageRefType|a(?:lcItemMsg|psLockGlyph)|heckmarkGlyph|learGlyph|o(?:lorIconType|mmandGlyph|nt(?:ext(?:Co(?:mmandIDSearch|ntextualMenu)|DontUpdate(?:Enabled|Icon|Key|Text)|Inspection|KeyMatching|Menu(?:Bar(?:Tracking)?|Enabling)|P(?:opUp(?:Tracking)?|ullDown)|Submenu|ualMenuGlyph)|rol(?:Glyph|ISOGlyph|Modifier))))|D(?:e(?:f(?:ClassID|ProcPtr)|lete(?:LeftGlyph|RightGlyph))|i(?:amondGlyph|sposeMsg)|own(?:ArrowGlyph|wardArrowDashedGlyph)|raw(?:ItemsMsg|Msg))|E(?:isuGlyph|jectGlyph|nterGlyph|scapeGlyph|vent(?:DontCheckSubmenus|IncludeDisabledItems|QueryOnly))|F(?:1(?:0Glyph|1Glyph|2Glyph|3Glyph|4Glyph|5Glyph|6Glyph|7Glyph|8Glyph|9Glyph|Glyph)|2Glyph|3Glyph|4Glyph|5Glyph|6Glyph|7Glyph|8Glyph|9Glyph|indItemMsg)|H(?:elpGlyph|iliteItemMsg)|I(?:con(?:Re(?:fType|sourceType)|SuiteType|Type)|nitMsg|tem(?:Attr(?:Auto(?:Disable|Repeat)|CustomDraw|D(?:isabled|ynamic)|Hidden|I(?:conDisabled|gnoreMeta|ncludeInCmdKeyMatching)|NotPreviousAlternate|S(?:e(?:ctionHeader|parator)|ubmenuParentChoosable)|U(?:pdateSingleItem|seVirtualKey))|Data(?:A(?:llDataVersion(?:One|T(?:hree|wo))|ttribute(?:dText|s))|C(?:FString|md(?:Key(?:Glyph|Modifiers)?|VirtualKey)|ommandID)|Enabled|Font(?:ID)?|I(?:con(?:Enabled|Handle|ID)|ndent)|Mark|Properties|Refcon|S(?:tyle|ubmenu(?:Handle|ID))|Text(?:Encoding)?)))|KanaGlyph|Left(?:Arrow(?:DashedGlyph|Glyph)|DoubleQuotesJapaneseGlyph)|N(?:o(?:CommandModifier|Icon|Modifiers|nmarkingReturnGlyph|rthwestArrowGlyph)|ullGlyph)|Option(?:Glyph|Modifier)|P(?:a(?:ge(?:DownGlyph|UpGlyph)|ragraphKoreanGlyph)|encilGlyph|o(?:pUpMsg|werGlyph)|ropertyPersistent)|R(?:eturn(?:Glyph|R2LGlyph)|ight(?:Arrow(?:DashedGlyph|Glyph)|DoubleQuotesJapaneseGlyph))|S(?:h(?:ift(?:Glyph|Modifier)|rinkIconType)|izeMsg|mallIconType|outheastArrowGlyph|paceGlyph|tdMenu(?:BarProc|Proc)|ystemIconSelectorType)|T(?:ab(?:LeftGlyph|RightGlyph)|hemeSavvyMsg|ra(?:ckingMode(?:Keyboard|Mouse)|demarkJapaneseGlyph))|UpArrow(?:DashedGlyph|Glyph))|i(?:crosecondScale|llisecondScale|ni(?:1BitMask|4BitData|8BitData))|o(?:d(?:DateKCItemAttr|al(?:DialogVariantCode|WindowClass)|em(?:OutOfMemory|PreferencesMissing|Script(?:Missing|sFolderType)))|nospaced(?:NumbersSelector|TextSelector)|u(?:nted(?:BadgeIcon|Folder(?:AliasType|Icon(?:Resource)?))|se(?:Params(?:ClickAndHold|DragInitiation|ProxyIcon|Sticky)|Tracking(?:ClientEvent|KeyModifiersChanged|Mouse(?:D(?:own|ragged)|E(?:ntered|xited)|Moved|Pressed|Released|Up)|ScrollWheel|TimedOut|UserCancelled)|UpOutOfSlop))|v(?:able(?:Alert(?:VariantCode|WindowClass)|Modal(?:DialogVariantCode|WindowClass))|ieDocumentsFolderType))|u(?:lti(?:ChannelMixerParam_(?:Enable|P(?:an|ost(?:AveragePower|PeakHoldLevel)|re(?:AveragePower|PeakHoldLevel))|Volume)|band(?:CompressorParam_(?:AttackTime|C(?:ompressionAmount(?:1|2|3|4)|rossover(?:1|2|3))|EQ(?:1|2|3|4)|Headroom(?:1|2|3|4)|InputAmplitude(?:1|2|3|4)|OutputAmplitude(?:1|2|3|4)|P(?:ostgain|regain)|ReleaseTime|Threshold(?:1|2|3|4))|Filter_(?:Bandwidth(?:1|2|3)|Center(?:Freq(?:1|2|3)|Gain(?:1|2|3))|High(?:F(?:ilterType|requency)|Gain)|Low(?:F(?:ilterType|requency)|Gain)))|processingFolderType)|sic(?:D(?:evice(?:MIDIEventSelect|P(?:aram_(?:ReverbVolume|Tuning|Volume)|r(?:epareInstrumentSelect|operty_(?:BankName|DualSchedulingMode|GroupOutputBus|Instrument(?:Count|N(?:ame|umber))|MIDIXMLNames|PartGroup|S(?:oundBank(?:Data|FS(?:Ref|Spec)|URL)|treamFromDisk|upportsStartStopNote)|UsesInternalReverb)))|R(?:ange|eleaseInstrumentSelect)|S(?:ampleFrameMask_(?:IsScheduled|SampleOffset)|t(?:artNoteSelect|opNoteSelect)|ysExSelect))|ocumentsFolderType)|EventType_(?:AUPreset|Extended(?:Control|Note|Tempo)|M(?:IDI(?:ChannelMessage|NoteMessage|RawData)|eta)|NULL|Parameter|User)|NoteEvent_U(?:nused|seGroupInstrument)|Sequence(?:File(?:Flags_(?:Default|EraseFile)|_(?:AnyType|MIDIType|iMelodyType))|LoadSMF_(?:ChannelsToTracks|PreserveTracks)|Type_(?:Beats|S(?:amples|econds))))))|N(?:LCCharactersSelector|S(?:L(?:68kContextNotSupported|B(?:ad(?:ClientInfoPtr|DataTypeErr|NetConnection|ProtocolTypeErr|ReferenceErr|ServiceTypeErr|URLSyntax)|ufferTooSmallForData)|CannotContinueLookup|ErrNullPtrError|In(?:itializationFailed|sufficient(?:OTVer|SysVer)|validPluginSpec)|N(?:o(?:C(?:arbonLib|ontextAvailable)|ElementsInList|PluginsFo(?:rSearch|und)|SupportForService|tI(?:mplementedYet|nitialized))|ull(?:ListPtr|NeighborhoodPtr))|PluginLoadFailed|RequestBufferAlreadyInList|S(?:chedulerError|earchAlreadyInProgress|omePluginsFailedToLoad)|UILibraryNotAvailable)|p(?:A(?:dd(?:PlayerFailedErr|ressInUseErr)|lready(?:AdvertisingErr|InitializedErr))|C(?:antBlockErr|onnectFailedErr|reateGroupFailedErr)|F(?:eatureNotImplementedErr|reeQExhaustedErr)|GameTerminatedErr|HostFailedErr|In(?:itializationFailedErr|valid(?:AddressErr|DefinitionErr|G(?:ameRefErr|roupIDErr)|P(?:arameterErr|layerIDErr|rotocol(?:ListErr|RefErr))))|JoinFailedErr|Me(?:mAllocationErr|ssageTooBigErr)|N(?:ameRequiredErr|o(?:GroupsErr|HostVolunteersErr|PlayersErr|tAdvertisingErr))|OT(?:NotPresentErr|VersionTooOldErr)|P(?:ipeFullErr|rotocolNotAvailableErr)|RemovePlayerFailedErr|SendFailedErr|T(?:imeoutErr|opologyNotSupportedErr)))|a(?:meLocked|nosecondScale|v(?:CustomControlMessageFailedErr|Invalid(?:CustomControlMessageErr|SystemConfigErr)|MissingKindStringErr|WrongDialog(?:ClassErr|StateErr)))|e(?:gativeKCItemAttr|twork(?:Domain|PPDDomain)|ut(?:er|ralScript)|verAuthenticate|w(?:DebugHeap|S(?:izeParameter|tyleHeap|uspend)|TimePitchParam_(?:EnablePeakLocking|Overlap|Pitch|Rate))|xt(?:Body|WindowGroup))|o(?:A(?:lternatesSelector|nnotationSelector)|ByteCode|C(?:JK(?:ItalicRomanSelector|SymbolAlternativesSelector)|ard(?:BusCISErr|E(?:nablersFoundErr|rr)|SevicesSocketsErr)|lientTableErr|o(?:mpatibleNameErr|nstraint))|En(?:ablerForCardErr|dingProsody)|F(?:ilesIcon|olderIcon|ractionsSelector)|I(?:OWindowRequestedErr|deographicAlternativesSelector)|More(?:I(?:nterruptSlotsErr|temsErr)|TimerClientsErr)|OrnamentsSelector|Process|RubyKanaSelector|S(?:peechInterrupt|tyl(?:eOptionsSelector|isticAlternatesSelector)|uchPowerSource)|T(?:hreadID|imeOut|rans(?:form|literationSelector))|UserAuthentication|WriteIcon|n(?:BreakingSpaceCharCode|FinalSwashesO(?:ffSelector|nSelector)|eKCStopOn)|rmalPositionSelector|t(?:Paged|ReadyErr|ZVCapableErr|eIcon))|u(?:llCharCode|m(?:AUNBandEQFilterTypes|ber(?:C(?:aseType|tlCTabEntries)|OfResponseFrequencies|SpacingType))))|O(?:CRInputMethodClass|PBD(?:C(?:ontrolPointFormat|urrentVersion)|DistanceFormat|Tag)|S(?:A(?:C(?:anGetSource|omponentType)|DontUsePhac|Error(?:A(?:pp|rgs)|BriefMessage|ExpectedType|Message|Number|OffendingObject|PartialResult|Range)|FileType|GenericScriptingComponentSubtype|Mode(?:A(?:lwaysInteract|ugmentContext)|C(?:an(?:Interact|tSwitchLayer)|ompileIntoContext)|D(?:isp(?:atchToDirectObject|layForHumans)|o(?:Record|nt(?:Define|GetDataForArguments|Reconnect|StoreParent)))|FullyQualifyDescriptors|N(?:everInteract|ull)|PreventGetSource)|N(?:oDispatch|ull(?:Mode|Script))|RecordedText|S(?:cript(?:BestType|Is(?:Modified|Type(?:CompiledScript|Script(?:Context|Value)))|ResourceType)|elect(?:AvailableDialect(?:CodeList|s)|Co(?:erce(?:FromDesc|ToDesc)|mp(?:ile(?:Execute)?|onentSpecificStart)|py(?:DisplayString|ID|S(?:cript|ourceString)))|D(?:isp(?:lay|ose)|o(?:Event|Script))|Execute(?:Event)?|Get(?:ActiveProc|C(?:reateProc|urrentDialect)|DialectInfo|ResumeDispatchProc|S(?:criptInfo|endProc|ource))|Load(?:Execute)?|MakeContext|S(?:cript(?:Error|ingComponentName)|et(?:ActiveProc|C(?:reateProc|urrentDialect)|DefaultTarget|ResumeDispatchProc|S(?:criptInfo|endProc))|t(?:artRecording|o(?:pRecording|re))))|u(?:ite|pports(?:AE(?:Coercion|Sending)|Co(?:mpiling|nvenience)|Dialects|EventHandling|GetSource|Recording)))|UseStandardDispatch|sync(?:CompleteMessageID|Ref(?:64(?:Count|Size)|Count|Size)))|IZ(?:CodeInSharedLibraries|DontOpenResourceFile|OpenWithReadPermission|dontAcceptRemoteEvents)|NotificationMessageID)|T(?:A(?:ccessErr|ddressBusyErr)|B(?:ad(?:AddressErr|ConfigurationErr|DataErr|FlagErr|NameErr|OptionErr|QLenErr|ReferenceErr|S(?:equenceErr|yncErr))|ufferOverflowErr)|C(?:anceledErr|lientNotInittedErr|onfigurationChangedErr)|DuplicateFoundErr|FlowErr|IndOutErr|LookErr|No(?:AddressErr|D(?:ataErr|isconnectErr)|Error|ReleaseErr|StructureTypeErr|UDErrErr|t(?:FoundErr|SupportedErr))|Out(?:OfMemoryErr|StateErr)|P(?:ort(?:HasDiedErr|LostConnection|WasEjectedErr)|ro(?:tocolErr|viderMismatchErr))|QFullErr|Res(?:AddressErr|QLenErr)|S(?:tateChangeErr|ysErrorErr)|UserRequestedErr)|ff(?:linePreflight_(?:NotRequired|Optional|Required)|set2Pos)|ld68kRTA|n(?:AppropriateDisk|SystemDisk|eByteCode)|p(?:aque(?:A(?:ddressSpaceID|nyID|reaID)|C(?:o(?:herenceID|nsoleID)|puID|riticalRegionID)|EventID|NotificationID|ProcessID|QueueID|SemaphoreID|T(?:askID|imerID))|en(?:D(?:oc(?:EditorsFolderType|FolderType|LibrariesFolderType|ShellPlugInsFolderType)|ropIconVariant)|FolderIcon(?:Resource)?|IconVariant)|tionUnicode)|r(?:Connections|dinalsSelector|namentSetsType)|therPluginFormat_(?:AU|Undefined|k(?:MAS|VST))|ut(?:OfResourceErr|putTextInUnicodeEncoding(?:Bit|Mask))|verla(?:ppingCharactersType|yWindowClass)|wne(?:dFolderIcon(?:Resource)?|r(?:I(?:D2Name|con)|Name2ID)))|P(?:CSTo(?:Device|PCS)|EF(?:AbsoluteExport|Co(?:deS(?:ection|ymbol)|nstantSection)|D(?:ataSymbol|ebugSection)|Ex(?:ceptionSection|ecDataSection|pSym(?:ClassShift|MaxNameOffset|NameOffsetMask))|FirstSectionHeaderOffset|Gl(?:obalShare|ueSymbol)|Hash(?:LengthShift|MaxLength|Slot(?:FirstKeyMask|Max(?:KeyIndex|SymbolCount)|SymCountShift)|ValueMask)|I(?:mpSym(?:ClassShift|MaxNameOffset|NameOffsetMask)|nitLibBeforeMask)|LoaderSection|P(?:ackedDataSection|kData(?:Block|Count5Mask|MaxCount5|OpcodeShift|Repeat(?:Block|Zero)?|VCount(?:EndMask|Mask|Shift)|Zero)|ro(?:cessShare|tectedShare))|Re(?:exportedImport|loc(?:B(?:asicOpcodeRange|ySect(?:C|D(?:WithSkip)?))|I(?:mportRun|ncrPosition(?:MaxOffset)?)|Lg(?:By(?:Import(?:MaxIndex)?|SectionSubopcode)|Repeat(?:Max(?:ChunkCount|RepeatCount))?|Set(?:OrBySection(?:MaxIndex)?|Sect(?:CSubopcode|DSubopcode)))|RunMaxRunLength|S(?:etPos(?:MaxOffset|ition)|m(?:By(?:Import|Section)|IndexMaxIndex|Repeat(?:Max(?:ChunkCount|RepeatCount))?|SetSect(?:C|D)))|TVector(?:12|8)|UndefinedOpcode|VTable8|WithSkipMax(?:RelocCount|SkipCount)))|T(?:OCSymbol|VectorSymbol|ag(?:1|2)|racebackSection)|Un(?:definedSymbol|packedDataSection)|Version|WeakImport(?:LibMask|SymMask))|M(?:AllocationFailure|Border(?:Double(?:Hairline|Thickline)|Single(?:Hairline|Thickline))|C(?:MYKColorSpaceModel|VMSymbolNotFound|ancel|loseFailed|overPage(?:After|Before|None)|reateMessageFailed)|D(?:ataFormatXML(?:Compressed|Default|Minimal)|e(?:leteSubTicketFailed|stination(?:F(?:ax|ile)|Invalid|Pr(?:eview|inter|ocessPDF))|vNColorSpaceModel)|o(?:cumentNotFound|ntSwitchPDEError)|uplex(?:No(?:Tumble|ne)|Tumble))|EditRequestFailed|F(?:eatureNotInstalled|ileOrDirOperationFailed|ontN(?:ameTooLong|otFound))|G(?:eneral(?:CGError|Error)|rayColorSpaceModel)|HideInlineItems|I(?:O(?:AttrNotAvailable|MSymbolNotFound)|n(?:ternalError|valid(?:Allocator|C(?:VMContext|alibrationTarget|onnection)|FileType|I(?:OMContext|ndex|tem)|Job(?:ID|Template)|Key|LookupSpec|Object|P(?:BMRef|DEContext|MContext|a(?:geFormat|per|rameter)|r(?:eset|int(?:Se(?:ssion|ttings)|er(?:Address|Info)?)))|Reply|S(?:tate|ubTicket)|T(?:icket|ype)|Value))|temIsLocked)|Job(?:Busy|Canceled|GetTicket(?:BadFormatError|ReadError)|ManagerAborted|NotFound|Stream(?:EndError|OpenFailed|ReadFailed))|Key(?:Not(?:Found|Unique)|OrValueNotFound)|La(?:ndscape|stErrorCodeToMakeMaintenanceOfThisListEasier|yout(?:BottomTop(?:LeftRight|RightLeft)|LeftRight(?:BottomTop|TopBottom)|RightLeft(?:BottomTop|TopBottom)|TopBottom(?:LeftRight|RightLeft)))|MessagingError|No(?:Default(?:Item|Printer|Settings)|Error|PrinterJobID|S(?:electedPrinters|uchEntry)|tImplemented)|O(?:bjectInUse|penFailed|utOfScope)|P(?:MSymbolNotFound|a(?:geToPaperMapping(?:None|ScaleToFit)|perType(?:Coated|Glossy|P(?:lain|remium)|T(?:Shirt|ransparency)|Unknown))|ermissionError|lugin(?:NotFound|RegisterationFailed)|ortrait|r(?:BrowserNoUI|int(?:AllPages|er(?:Idle|Processing|Stopped))))|Qu(?:ality(?:Best|Draft|Highest|InkSaver|Lowest|Normal|Photo)|eue(?:AlreadyExists|JobFailed|NotFound))|R(?:GBColorSpaceModel|e(?:ad(?:Failed|GotZeroData)|verse(?:Landscape|Portrait)))|S(?:caling(?:CenterOn(?:ImgArea|Paper)|Pin(?:Bottom(?:Left|Right)|Top(?:Left|Right)))|erver(?:A(?:lreadyRunning|ttributeRestricted)|CommunicationFailed|NotFound|Suspended)|how(?:DefaultInlineItems|Inline(?:Copies|Orientation|Pa(?:geRange(?:WithSelection)?|perSize)|Scale)|PageAttributesPDE)|implexTumble|t(?:atusFailed|ringConversionFailure)|ubTicketNotFound|yncRequestFailed)|T(?:emplateIsLocked|icket(?:IsLocked|TypeNotFound))|U(?:n(?:ableToFindProcess|expectedImagingError|known(?:ColorSpaceModel|DataType|Message)|locked|supportedConnection)|pdateTicketFailed|serOrGroupNotFound)|Val(?:idateTicketFailed|ueOutOfRange)|WriteFailed|XMLParseError)|OSIXError(?:Base|E(?:2BIG|A(?:CCES|DDR(?:INUSE|NOTAVAIL)|FNOSUPPORT|GAIN|LREADY|UTH)|B(?:AD(?:ARCH|EXEC|F|M(?:ACHO|SG)|RPC)|USY)|C(?:ANCELED|HILD|ONN(?:ABORTED|RE(?:FUSED|SET)))|D(?:E(?:ADLK|STADDRREQ|VERR)|OM|QUOT)|EXIST|F(?:AULT|BIG|TYPE)|HOST(?:DOWN|UNREACH)|I(?:DRM|LSEQ|N(?:PROGRESS|TR|VAL)|O|S(?:CONN|DIR))|LOOP|M(?:FILE|LINK|SGSIZE|ULTIHOP)|N(?:AMETOOLONG|E(?:EDAUTH|T(?:DOWN|RESET|UNREACH))|FILE|O(?:ATTR|BUFS|D(?:ATA|EV)|E(?:NT|XEC)|L(?:CK|INK)|M(?:EM|SG)|PROTOOPT|S(?:PC|R|TR|YS)|T(?:BLK|CONN|DIR|EMPTY|S(?:OCK|UP)|TY))|XIO)|O(?:PNOTSUPP|VERFLOW)|P(?:ERM|FNOSUPPORT|IPE|RO(?:C(?:LIM|UNAVAIL)|G(?:MISMATCH|UNAVAIL)|TO(?:NOSUPPORT|TYPE)?)|WROFF)|R(?:ANGE|EMOTE|OFS|PCMISMATCH)|S(?:H(?:LIBVERS|UTDOWN)|OCKTNOSUPPORT|PIPE|RCH|TALE)|T(?:IME(?:DOUT)?|OOMANYREFS|XTBSY)|USERS|XDEV))|ROP(?:A(?:LDirectionClass|NDirectionClass)|BNDirectionClass|C(?:SDirectionClass|anHang(?:LTMask|RBMask)|urrentVersion)|DirectionMask|E(?:NDirectionClass|SDirectionClass|TDirectionClass)|IsFloaterMask|L(?:DirectionClass|R(?:EDirectionClass|ODirectionClass))|N(?:SMDirectionClass|umDirectionClasses)|ONDirectionClass|P(?:DFDirectionClass|SDirectionClass|airOffset(?:Mask|S(?:hift|ign)))|R(?:DirectionClass|L(?:EDirectionClass|ODirectionClass)|ightConnectMask)|S(?:DirectionClass|ENDirectionClass)|Tag|UseRLPairMask|WSDirectionClass|ZeroReserved)|a(?:ckageAliasType|ge(?:DownCharCode|InMemory|OnDisk|UpCharCode)|nn(?:erParam_(?:Azimuth|CoordScale|Distance|Elevation|Gain|RefDistance)|ingMode_(?:SoundField|VectorBasedPanning))|r(?:amet(?:erEvent_(?:Immediate|Ramped)|ricEQParam_(?:CenterFreq|Gain|Q))|enthesisAnnotationSelector|tiallyConnectedSelector)|s(?:calStackBased|s(?:CallToChainErr|Selector|word(?:ChangedKCEvent(?:Mask)?)?)|teboard(?:ClientIsOwner|Flavor(?:No(?:Flags|tSaved)|Promised|RequestOnly|S(?:ender(?:Only|Translated)|ystemTranslated))|Modified|StandardLocation(?:Trash|Unknown)))|thKCItemAttr)|e(?:ncil(?:LeftUnicode|Unicode)|riod(?:AnnotationSelector|sToEllipsisO(?:ffSelector|nSelector)))|i(?:CharactersSelector|ctureD(?:ialogItem|ocumentsFolderType))|l(?:ain(?:DialogVariantCode|WindowClass)|otIconRefNo(?:Image|Mask|rmalFlags))|o(?:licyKCStopOn|rtKCItemAttr|s(?:2Offset|tCardEventErr)|wer(?:Handler(?:ExistsForDeviceErr|NotFoundFor(?:DeviceErr|ProcErr))|Mgt(?:MessageNotHandled|RequestDenied)|PC(?:ISA|RTA)))|r(?:e(?:MacOS91(?:A(?:ppl(?:eExtrasFolderType|icationsFolderType)|ssistantsFolderType|utomountedServersFolderType)|In(?:stallerLogsFolderType|ternetFolderType)|MacOSReadMesFolderType|StationeryFolderType|UtilitiesFolderType)|emptiveThread|f(?:erence(?:PanesFolderType|sFolder(?:AliasType|Icon(?:Resource)?|Type))|lightThenPause)|v(?:entOverlapO(?:ffSelector|nSelector)|ious(?:Body|WindowGroup)))|i(?:nt(?:Monitor(?:DocsFolder(?:AliasType|Type)|FolderIcon(?:Resource)?)|er(?:D(?:escriptionFolder(?:Icon|Type)|riverFolder(?:Icon|Type))|sFolderType)|ingPlugInsFolderType)|v(?:ateF(?:olderIcon(?:Resource)?|rameworksFolderType)|ilegeViolationException))|o(?:c(?:DescriptorIs(?:Absolute|Index|ProcPtr|Relative)|ess(?:DictionaryIncludeAllInformationMask|TransformTo(?:BackgroundApplication|ForegroundApplication|UIElementApplication)|orTempRoutineRequiresMPLib2))|gramTargetLevel_(?:Minus(?:2(?:0dB|3dB)|31dB)|None)|portional(?:CJKRomanSelector|IdeographsSelector|KanaSelector|NumbersSelector|TextSelector)|t(?:ected(?:ApplicationFolderIcon|SystemFolderIcon)|ocolKCItemAttr)))|ublic(?:Folder(?:Icon|Type)|KeyHashKCItemAttr|ThemeFontCount))|Q(?:D(?:C(?:orruptPICTDataErr|ursor(?:AlreadyRegistered|NotRegistered))|No(?:ColorHWCursorSupport|Palette))|LPreviewPDF(?:PagesWithThumbnailsOn(?:LeftStyle|RightStyle)|StandardStyle)|TSSUnknownErr|u(?:arterWidth(?:NumbersSelector|TextSelector)|estionMarkIcon|i(?:ck(?:LookFolderType|Time(?:ComponentsFolderType|ExtensionsFolderType))|t(?:AtNormalTimeMask|Before(?:FBAsQuitMask|NormalTimeMask|ShellQuitsMask|TerminatorAppQuitsMask)|N(?:everMask|otQuitDuring(?:InstallMask|LogoutMask))|OptionsMask))))|R(?:A(?:ATalkInactive|C(?:allBackFailed|on(?:figurationDBInitErr|nectionCanceled))|DuplicateIPAddr|ExtAuthenticationFailed|In(?:compatiblePrefs|itOpenTransportFailed|stallationDamaged|ternalError|valid(?:P(?:a(?:rameter|ssword)|ort(?:State)?)|SerialProtocol))|MissingResources|N(?:CPRejectedbyPeer|ot(?:Connected|Enabled|PrimaryInterface|Supported))|OutOfMemory|P(?:PP(?:AuthenticationFailed|NegotiationFailed|P(?:eerDisconnected|rotocolRejected)|UserDisconnected)|eerNotResponding|ort(?:Busy|SetupFailed))|RemoteAccessNotReady|StartupFailed|TCPIP(?:Inactive|NotConfigured)|U(?:nknown(?:PortState|User)|ser(?:InteractionRequired|LoginDisabled|Pwd(?:ChangeRequired|EntryRequired))))|a(?:dioButtonDialogItem|ndomParam_(?:Bound(?:A|B)|Curve)|reLigaturesO(?:ffSelector|nSelector))|dPermKCStatus|e(?:ad(?:ExtensionTermsMask|FailureErr|OnlyMemoryException|Reference|yThreadState)|busPicturesO(?:ffSelector|nSelector)|cent(?:ApplicationsFolder(?:Icon|Type)|DocumentsFolder(?:Icon|Type)|ItemsIcon|ServersFolder(?:Icon|Type))|d(?:irectedRelativeFolder|rawHighlighting)|gister(?:A(?:0|1|2|3|4|5|6)|Based|D(?:0|1|2|3|4|5|6|7)|Parameter(?:Mask|Phase|Size(?:Phase|Width)|W(?:hich(?:Phase|Width)|idth))|ResultLocation(?:Phase|Width))|lativeFolder|nderQuality_(?:High|Low|M(?:ax|edium|in))|quiredLigaturesO(?:ffSelector|nSelector)|s(?:FileNotOpened|o(?:lveAlias(?:FileNoUI|TryFileIDFirst)|urceControlDialogItem)|ultSize(?:Mask|Phase|Width))|turn(?:CharCode|Next(?:Group|U(?:G|ser)))|verb(?:2Param_(?:D(?:ecayTimeAt(?:0Hz|Nyquist)|ryWetMix)|Gain|M(?:axDelayTime|inDelayTime)|RandomizeReflections)|Param_(?:DryWetMix|Filter(?:Bandwidth|Enable|Frequency|Gain|Type)|Large(?:Brightness|De(?:lay(?:Range)?|nsity)|Size)|Modulation(?:Depth|Rate)|PreDelay|Small(?:Brightness|De(?:layRange|nsity)|LargeMix|Size))|RoomType_(?:Cathedral|Large(?:Chamber|Hall(?:2)?|Room(?:2)?)|Medium(?:Chamber|Hall(?:2|3)?|Room)|Plate|SmallRoom)))|ight(?:ArrowCharCode|ContainerArrowIcon|ToLeft)|o(?:gerBeepParam_(?:InGateThreshold(?:Time)?|OutGateThreshold(?:Time)?|Roger(?:Gain|Type)|Sensitivity)|lloverIconVariant|man(?:NumeralAnnotationSelector|izationTo(?:HiraganaSelector|KatakanaSelector))|otFolder|u(?:nd(?:TripAACParam_(?:BitRate|CompressedFormatSampleRate|EncodingStrategy|Format|Quality|RateOrQuality)|WindowDefinition|edBoxAnnotationSelector)|tin(?:e(?:DescriptorVersion|Is(?:DispatchedDefaultRoutine|NotDispatchedDefaultRoutine))|gResource(?:ID|Type))))|srcChain(?:AboveA(?:llMaps|pplicationMap)|Below(?:ApplicationMap|SystemMap))|u(?:byKana(?:O(?:ffSelector|nSelector)|Selector|Type)|nningThreadState))|S(?:C(?:BondStatus(?:LinkInvalid|No(?:Partner|tInActiveGroup)|OK|Unknown)|Network(?:Connection(?:Connect(?:ed|ing)|Disconnect(?:ed|ing)|Invalid|PPP(?:Authenticating|Connect(?:ed|ingLink)|Di(?:alOnTraffic|sconnect(?:ed|ingLink))|HoldingLinkOff|Initializing|Negotiating(?:Link|Network)|Suspended|Terminating|WaitingFor(?:CallBack|Redial)))|Flags(?:Connection(?:Automatic|Required)|I(?:nterventionRequired|s(?:Direct|LocalAddress))|Reachable|TransientConnection)|ReachabilityFlags(?:Connection(?:Automatic|On(?:Demand|Traffic)|Required)|I(?:nterventionRequired|s(?:Direct|LocalAddress|WWAN))|Reachable|TransientConnection))|PreferencesNotification(?:Apply|Commit)|Status(?:AccessError|Connection(?:Ignore|NoService)|Failed|InvalidArgument|KeyExists|Locked|MaxLink|N(?:eedLock|o(?:ConfigFile|Key|Link|PrefsSession|StoreSe(?:rver|ssion)|tifierActive))|OK|PrefsBusy|ReachabilityUnknown|Stale))|FNTLookup(?:S(?:egment(?:Array|Single)|i(?:mpleArray|ngleTable))|TrimmedArray|Vector)|K(?:DocumentState(?:AddPending|DeletePending|Indexed|NotIndexed)|Index(?:Inverted(?:Vector)?|Unknown|Vector)|Search(?:BooleanRanked|Option(?:Default|FindSimilar|NoRelevanceScores|SpaceMeansOR)|PrefixRanked|R(?:anked|equiredRanked)))|MPTETime(?:Running|Type(?:2(?:398|4|5|997(?:Drop)?)|30(?:Drop)?|5(?:0|994(?:Drop)?)|60(?:Drop)?)|Unknown|Valid)|O(?:AP(?:1999Schema|2001Schema)|CKS5NoAcceptableMethod)|R(?:A(?:lready(?:Finished|Listening|Released)|utoFinishingParam)|B(?:ad(?:Parameter|Selector)|lock(?:Background|Modally)|ufferTooSmall)|C(?:a(?:llBackParam|n(?:celOnSoundOut|ned22kHzSpeechSource|t(?:Add|GetProperty|ReadLanguageObject|Set(?:DuringRecognition|Property))))|leanupOnClientExit|omponentNotFound)|Default(?:Re(?:cognitionSystemID|jectionLevel)|SpeechSource)|E(?:nabled|xpansionTooDeep)|F(?:eedback(?:AndListeningModes|NotAvail)|oregroundOnly)|Has(?:FeedbackHasListenModes|NoSubItems)|I(?:dleRecognizer|nternalError)|Key(?:Expected|Word)|L(?:MObjType|anguageModel(?:Format|T(?:ooBig|ype))|i(?:stenKey(?:Combo|Mode|Name)|veDesktopSpeechSource))|M(?:odelMismatch|ustCancelSearch)|No(?:ClientLanguageModel|Feedback(?:HasListenModes|NoListenModes)|PendingUtterances|t(?:A(?:RecSystem|SpeechObject|vailable)|FinishedWithRejection|ImplementedYet|ListeningState|if(?:icationParam|yRecognition(?:Beginning|Done))))|O(?:ptional|therRecAlreadyModal|utOfMemory)|P(?:a(?:ramOutOfRange|th(?:Format|Type))|endingSearch|hrase(?:Format|Type))|Re(?:adAudio(?:FSSpec|URL)|cognition(?:Canceled|Done)|fCon|ject(?:able|edWord|ionLevel)|peatable)|S(?:earch(?:InProgress|StatusParam|WaitForAllClients)|ndInSourceDisconnected|oundInVolume|pe(?:edVsAccuracyParam|lling)|ubItemNotFound)|T(?:EXTFormat|ooManyElements)|Use(?:PushToTalk|ToggleListen)|W(?:ants(?:AutoFBGestures|ResultTextDrawn)|ord(?:NotFound|Type)))|S(?:LCiphersuiteGroup(?:ATS(?:Compatibility)?|Compatibility|Default|Legacy)|p(?:CantInstallErr|InternalErr|ParallelUpVectorErr|ScaleToZeroErr|VersionErr))|T(?:Class(?:DeletedGlyph|EndOf(?:Line|Text)|OutOfBounds)|KCrossStreamReset|LigActionMask|MarkEnd|NoAdvance|RearrVerbMask|SetMark|XHasLigAction)|c(?:heduledAudioSliceFlag_(?:BeganToRender(?:Late)?|Complete|Interrupt(?:AtLoop)?|Loop)|ientificInferiorsSelector|r(?:ap(?:ClearNamedScrap|Flavor(?:Mask(?:None|SenderOnly|Translated)|SizeUnknown|Type(?:Movie|Picture|Sound|Text(?:Style)?|U(?:TF16External|nicode(?:Style)?)))|GetNamedScrap|ReservedFlavorType)|eenSaversFolderType|ipt(?:CodeKCItemAttr|ingAdditionsFolder(?:Icon|Type)|sFolder(?:Icon|Type))|oll(?:Bars(?:AlwaysActive|SyncWithFocus)|Window(?:EraseToPortBackground|Invalidate|NoOptions))))|e(?:c(?:3DES192|A(?:ES(?:1(?:28|92)|256)|ccountItemAttr|dd(?:Event(?:Mask)?|ressItemAttr)|lias|uthenticationType(?:Any|D(?:PA|efault)|HT(?:MLForm|TP(?:Basic|Digest))|ItemAttr|MSN|NTLM|RPA))|C(?:S(?:BasicValidateOnly|C(?:alculateCMSDigest|heck(?:AllArchitectures|GatekeeperArchitectures|NestedCode|TrustedAnchors)|on(?:siderExpiration|tentInformation))|D(?:e(?:dicatedHost|faultFlags)|oNotValidate(?:Executable|Resources)|ynamicInformation)|EnforceRevocationChecks|FullReport|GenerateGuestHash|InternalInformation|NoNetworkAccess|QuickCheck|Re(?:portProgress|quirementInformation|strict(?:S(?:idebandData|ymlinks)|ToAppLike))|S(?:i(?:gningInformation|ngleThreaded)|kipResourceDirectory|trictValidate)|Use(?:AllArchitectures|SoftwareSigningCert)|ValidatePEH)|ert(?:EncodingItemAttr|TypeItemAttr|ificate(?:Encoding|ItemClass|Type))|o(?:deS(?:ignature(?:Adhoc|Enforcement|Force(?:Expiration|Hard|Kill)|H(?:ashSHA(?:1|256(?:Truncated)?|384|512)|ost)|LibraryValidation|NoHash|R(?:estrict|untime))|tatus(?:Debugged|Hard|Kill|Platform|Valid))|mmentItemAttr)|r(?:e(?:at(?:ionDateItemAttr|orItemAttr)|dentialType(?:Default|NoUI|WithUI))|l(?:Encoding|Type))|ustomIconItemAttr)|De(?:fault(?:ChangedEvent(?:Mask)?|KeySize)|leteEvent(?:Mask)?|s(?:criptionItemAttr|ignatedRequirementType))|EveryEventMask|Format(?:BSAFE|NetscapeCertSequence|OpenSSL|P(?:EMSequence|KCS(?:12|7))|RawKey|SSH(?:v2)?|Unknown|Wrapped(?:LSH|OpenSSL|PKCS8|SSH)|X509Cert)|G(?:eneric(?:ItemAttr|PasswordItemClass)|uestRequirementType)|Ho(?:norRoot|stRequirementType)|I(?:n(?:ternetPasswordItemClass|v(?:alidRequirementType|isibleItemAttr))|ssuerItemAttr|tem(?:PemArmour|Type(?:Aggregate|Certificate|P(?:rivateKey|ublicKey)|SessionKey|Unknown)))|Key(?:A(?:l(?:ias|waysSensitive)|pplicationTag)|De(?:crypt|rive)|E(?:ffectiveKeySize|n(?:crypt|dDate)|xtractable)|ImportOnlyOne|Key(?:C(?:lass|reator)|SizeInBits|Type)|Label|Modifiable|N(?:everExtractable|oAccessControl)|P(?:ermanent|ri(?:ntName|vate))|S(?:e(?:curePassphrase|nsitive)|ign(?:Recover)?|tartDate)|U(?:nwrap|sage(?:All|C(?:RLSign|ontentCommitment|ritical)|D(?:ataEncipherment|ecipherOnly|igitalSignature)|EncipherOnly|Key(?:Agreement|CertSign|Encipherment)|NonRepudiation|Unspecified))|Verify(?:Recover)?|Wrap|chain(?:ListChanged(?:Event|Mask)|Prompt(?:Invalid(?:Act)?|RequirePassphase|Unsigned(?:Act)?)))|L(?:abelItemAttr|ibraryRequirementType|ockEvent(?:Mask)?)|M(?:atchBits|odDateItemAttr)|N(?:egativeItemAttr|oGuest)|OptionReserved|P(?:a(?:dding(?:None|OAEP|PKCS1(?:MD(?:2|5)|SHA(?:1|2(?:24|56)|384|512))?|SigRaw)|sswordChangedEvent(?:Mask)?|thItemAttr)|luginRequirementType|ortItemAttr|r(?:eferencesDomain(?:Common|Dynamic|System|User)|ivateKeyItemClass|otocol(?:ItemAttr|Type(?:A(?:FP|ny|ppleTalk)|C(?:IFS|VSpserver)|DAAP|EPPC|FTP(?:Account|Proxy|S)?|HTTP(?:Proxy|S(?:Proxy)?)?|I(?:MAP(?:S)?|PP|RC(?:S)?)|LDAP(?:S)?|NNTP(?:S)?|POP3(?:S)?|RTSP(?:Proxy)?|S(?:M(?:B|TP)|OCKS|SH|VN)|Telnet(?:S)?)))|ublicKey(?:HashItemAttr|ItemClass))|R(?:SAM(?:ax|in)|e(?:adPermStatus|quirementTypeCount|vocation(?:CRLMethod|NetworkAccessDisabled|OCSPMethod|PreferCRL|RequirePositiveResponse|UseAnyAvailableMethod)))|S(?:criptCodeItemAttr|e(?:curityDomainItemAttr|r(?:ialNumberItemAttr|v(?:erItemAttr|iceItemAttr)))|ignatureItemAttr|ubject(?:ItemAttr|KeyIdentifierItemAttr)|ymmetricKeyItemClass)|T(?:r(?:ansform(?:Error(?:A(?:bort(?:InProgress|ed)|ttributeNotFound)|Invalid(?:Algorithm|Connection|Input(?:Dictionary)?|Length|Operation|Type)|M(?:issingParameter|oreThanOneOutput)|N(?:ameAlreadyRegistered|otInitializedCorrectly)|UnsupportedAttribute)|Invalid(?:Argument|Override)|MetaAttribute(?:CanCycle|Deferred|Externalize|Has(?:InboundConnection|OutboundConnections)|Name|Re(?:f|quire(?:d|sOutboundConnection))|Stream|Value)|OperationNotSupportedOnGroup|TransformIs(?:Executing|NotRegistered))|ust(?:Option(?:AllowExpired(?:Root)?|FetchIssuerFromNet|ImplicitAnchors|LeafIsCA|RequireRevPerCert|UseTrustSettings)|Result(?:Deny|FatalTrustFailure|Invalid|OtherError|Proceed|RecoverableTrustFailure|Unspecified)|Settings(?:ChangedEvent(?:Mask)?|Domain(?:Admin|System|User)|KeyUse(?:Any|EnDecrypt(?:Data|Key)|KeyExchange|Sign(?:Cert|Revocation|ature))|Result(?:Deny|Invalid|Trust(?:AsRoot|Root)|Unspecified))))|ypeItemAttr)|U(?:nlock(?:Event(?:Mask)?|StateStatus)|pdateEvent(?:Mask)?|seOnly(?:GID|UID))|VolumeItemAttr|WritePermStatus|ondScale|p(?:192r1|256r1|384r1|521r1)|urityDomainKCItemAttr)|lector(?:All(?:1BitData|32BitData|4BitData|8BitData|AvailableData|HugeData|LargeData|MiniData|SmallData)|Huge(?:1Bit|32Bit|4Bit|8Bit(?:Mask)?)|Large(?:1Bit|32Bit|4Bit|8Bit(?:Mask)?)|Mini(?:1Bit|4Bit|8Bit)|Small(?:1Bit|32Bit|4Bit|8Bit(?:Mask)?)|sAre(?:Indexable|NotIndexable))|quenceTrackProperty_(?:AutomatedParameters|LoopInfo|MuteStatus|OffsetTime|SoloStatus|T(?:imeResolution|rackLength))|r(?:ialNumberKCItemAttr|v(?:erKCItemAttr|ice(?:KCItemAttr|sFolderType)))|t(?:CLUT(?:ByValue|Immediately|WithLuminance)|DebugOption|FrontProcess(?:CausedByUser|FrontWindowOnly)|PowerLevel))|h(?:a(?:dowDialogVariantCode|r(?:ed(?:BadgeIcon|Folder(?:AliasType|Icon(?:Resource)?)|LibrariesFolder(?:Icon|Type)|UserDataFolderType)|ingPrivs(?:NotApplicableIcon|Read(?:OnlyIcon|WriteIcon)|UnknownIcon|WritableIcon)))|eet(?:AlertWindowClass|WindowClass)|ift(?:JIS_(?:BasicVariant|DOSVariant|MusicCDVariant)|Unicode)|o(?:rtcutIcon|w(?:DiacriticsSelector|HideInputWindow))|utdown(?:FolderType|Items(?:DisabledFolder(?:Icon|Type)|FolderIcon)))|i(?:deFloaterVariantCode|gn(?:KCItemAttr|atureKCItemAttr)|mpl(?:eWindowClass|ifiedCharactersSelector))|l(?:ash(?:ToDivideO(?:ffSelector|nSelector)|edZeroO(?:ffSelector|nSelector))|eep(?:De(?:mand|ny)|Now|Re(?:quest|voke)|Unlock|WakeUp))|ma(?:ll(?:1BitMask|32BitData|4Bit(?:Data|IconSize)|8Bit(?:Data|IconSize|Mask)|CapsSelector|IconSize)|rt(?:QuotesO(?:ffSelector|nSelector)|SwashType))|o(?:rt(?:AscendingIcon|DescendingIcon)|und(?:FileIcon|SetsFolderType))|p(?:a(?:ceCharCode|tial(?:Mixer(?:AttenuationCurve_(?:Exponential|Inverse|Linear|Power)|Param_(?:Azimuth|Distance|E(?:levation|nable)|G(?:ain|lobalReverbGain)|M(?:axGain|inGain)|O(?:bstructionAttenuation|cclusionAttenuation)|PlaybackRate|ReverbBlend)|RenderingFlags_(?:DistanceAttenuation|InterAuralDelay))|izationAlgorithm_(?:EqualPowerPanning|HRTF(?:HQ)?|S(?:oundField|phericalHead|tereoPassThrough)|VectorBasedPanning)))|e(?:ak(?:ableItemsFolder(?:Type)?|erConfiguration_(?:5_(?:0|1)|HeadPhones|Quad|Stereo))|cial(?:Case(?:CaretHook|DrawHook|EOLHook|GNEFilterProc|Hi(?:ghHook|tTestHook)|MBarHook|NWidthHook|ProtocolHandler|S(?:elector(?:Mask|Phase|Width)|ocketListener)|T(?:E(?:DoText|FindWord|Recalc)|extWidthHook)|WidthHook)?|Folder)|ech(?:FolderType|GenerateTune|InputMethodClass|Relative(?:Duration|Pitch)|ShowSyllables))|otlight(?:ImportersFolderType|MetadataCacheFolderType|SavedSearchesFolderType))|quaredLigaturesO(?:ffSelector|nSelector)|t(?:a(?:ck(?:DispatchedPascalStackBased|OverflowException|Parameter(?:Mask|Phase|Width))|ndardWindowDefinition|rt(?:DateKCItemAttr|up(?:Folder(?:AliasType|IconResource|Type)|Items(?:DisabledFolder(?:Icon|Type)|FolderIcon)))|ti(?:cTextDialogItem|oneryFolderType))|d(?:AlertDoNot(?:AnimateOn(?:Cancel|Default|Other)|CloseOnHelp|DisposeSheet)|C(?:FStringAlertVersion(?:One|Two)|ancelItemIndex)|OkItemIndex)|ereoMixerParam_(?:P(?:an|ost(?:AveragePower|PeakHoldLevel)|re(?:AveragePower|PeakHoldLevel))|Volume)|illIdle|o(?:p(?:Icon|pedThreadState)|red(?:BasicWindowDescriptionID|Window(?:PascalTitleID|SystemTag|TitleCFStringID)))|yl(?:eOptionsType|isticAlt(?:E(?:ight(?:O(?:ffSelector|nSelector)|eenO(?:ffSelector|nSelector))|levenO(?:ffSelector|nSelector))|F(?:i(?:fteenO(?:ffSelector|nSelector)|veO(?:ffSelector|nSelector))|our(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector)))|Nine(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector))|OneO(?:ffSelector|nSelector)|S(?:even(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector))|ix(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector)))|T(?:enO(?:ffSelector|nSelector)|h(?:irteenO(?:ffSelector|nSelector)|reeO(?:ffSelector|nSelector))|w(?:e(?:lveO(?:ffSelector|nSelector)|ntyO(?:ffSelector|nSelector))|oO(?:ffSelector|nSelector)))|ernativesType)))|u(?:b(?:jectKCItemAttr|stituteVerticalFormsO(?:ffSelector|nSelector))|p(?:eriorsSelector|ports(?:FileTranslation|ScrapTranslation))|spend(?:Demand|Re(?:quest|voke)|Wake(?:ToDoze|Up)))|washAlternatesO(?:ffSelector|nSelector)|y(?:m(?:Link(?:Creator|FileType)|bolLigaturesO(?:ffSelector|nSelector))|s(?:SWTooOld|tem(?:ControlPanelFolderType|D(?:esktopFolderType|omain)|E(?:ventKCEventMask|xtensionDisabledFolder(?:Icon|Type))|Folder(?:AliasType|Icon(?:Resource)?|Type)|IconsCreator|KCEvent|P(?:PDDomain|r(?:eferencesFolderType|ocess))|ResFile|S(?:ound(?:ID_(?:FlashScreen|UserPreferredAlert|Vibrate)|sFolderType)|uitcaseIcon)|TrashFolderType))))|T(?:EC(?:A(?:dd(?:F(?:allbackInterrupt(?:Bit|Mask)|orceASCIIChanges(?:Bit|Mask))|TextRunHeuristics(?:Bit|Mask))|rrayFullErr|vailable(?:EncodingsResType|SniffersResType))|B(?:adTextRunErr|ufferBelowMinimumSizeErr)|C(?:hinesePluginSignature|o(?:nversionInfoResType|rruptConverterErr))|Di(?:rectionErr|sable(?:Fallbacks(?:Bit|Mask)|LooseMappings(?:Bit|Mask)))|FallbackTextLengthFix(?:Bit|Mask)|GlobalsUnavailableErr|I(?:n(?:completeElementErr|foCurrentFormat|ternetName(?:DefaultUsageMask|StrictUsageMask|TolerantUsageMask|sResType))|temUnavailableErr)|JapanesePluginSignature|K(?:eepInfoFix(?:Bit|Mask)|oreanPluginSignature)|M(?:ailEncodingsResType|issingTableErr)|N(?:eedFlushStatus|oConversionPathErr)|OutputBufferFullStatus|P(?:artialCharErr|lugin(?:Creator|DispatchTable(?:CurrentVersion|Version1(?:_(?:1|2))?)|ManyToOne|OneTo(?:Many|One)|SniffObj|Type)|referredEncodingFix(?:Bit|Mask))|ResourceID|S(?:ignature|ubTextEncodingsResType)|T(?:able(?:ChecksumErr|FormatErr)|ext(?:RunBitClearFix(?:Bit|Mask)|ToUnicodeScanFix(?:Bit|Mask)))|U(?:n(?:icodePluginSignature|mappableElementErr)|sedFallbacksStatus)|WebEncodingsResType|_MIBEnumDontCare)|MTaskActive|RAK(?:CurrentVersion|Tag|UniformFormat)|SM(?:15Version|2(?:0Version|2Version|3Version|4Version)|Doc(?:Access(?:EffectiveRangeAttribute(?:Bit)?|FontSizeAttribute(?:Bit)?)|ument(?:EnabledInputSourcesPropertyTag|Input(?:ModePropertyTag|SourceOverridePropertyTag)|Property(?:SupportGlyphInfo|UnicodeInputWindow)|RefconPropertyTag|Support(?:DocumentAccessPropertyTag|GlyphInfoPropertyTag)|T(?:SMTEPropertyTag|extServicePropertyTag)|U(?:nicode(?:InputWindowPropertyTag|PropertyTag)|seFloatingWindowPropertyTag)|WindowLevelPropertyTag))|Hilite(?:BlockFillText|C(?:aretPosition|onvertedText)|NoHilite|OutlineText|RawText|Selected(?:ConvertedText|RawText|Text))|InsideOf(?:ActiveInputArea|Body)|OutsideOfBody|TEDocumentInterfaceType|Version)|XN(?:A(?:IFFFile|TSUI(?:Font(?:FeaturesAttribute|VariationsAttribute)|IsNotInstalledErr|Style(?:Continuous(?:Bit|Mask)|Size)?)|l(?:ign(?:CenterAction|LeftAction|RightAction)|lCountMask|readyInitializedErr|waysWrapAtViewEdge(?:Bit|Mask))|ttributeTagInvalidForRunErr|uto(?:Indent(?:O(?:ff|n)|StateTag)|Scroll(?:BehaviorTag|InsertionIntoView|Never|WhenInsertionVisible)|Wrap))|Ba(?:ckgroundTypeRGB|dDefaultFileTypeWarning)|C(?:annot(?:AddFrameErr|SetAutoIndentErr|TurnTSMOffWhenUsingUnicodeErr)|enter(?:Tab)?|hange(?:Font(?:Action|ColorAction|SizeAction)|StyleAction)|lear(?:Action|Th(?:eseFontFeatures|isControl))|o(?:lorContinuous(?:Bit|Mask)|pyNotAllowedInEchoModeErr)|utAction)|D(?:ataTypeNotAllowedErr|e(?:crementTypeSize|stinationRectKey)|isable(?:DragAndDrop(?:Bit|Mask|Tag)?|LayoutAndDraw(?:Tag)?|dFunctionalityErr)|o(?:FontSubstitution(?:Bit|Mask)?|NotInstallDragProcs(?:Bit|Mask)|nt(?:CareTypeS(?:ize|tyle)|Draw(?:CaretWhenInactive(?:Bit|Mask)?|SelectionWhenInactive(?:Bit|Mask)?)))|r(?:aw(?:CaretWhenInactive(?:Tag)?|GrowIcon(?:Bit|Mask)|Item(?:AllMask|Scrollbars(?:Bit|Mask)|Text(?:AndSelection(?:Bit|Mask)|Bit|Mask))|SelectionWhenInactive(?:Tag)?)|opAction))|En(?:able(?:DragAndDrop|LayoutAndDraw)|d(?:IterationErr|Offset)|tireWord(?:Bit|Mask))|F(?:l(?:attenMoviesTag|ush(?:Default|Left|Right))|o(?:nt(?:Continuous(?:Bit|Mask)|FeatureAction|SizeAttributeSize|VariationAction)|rceFullJust)|ullJust)|Horizontal(?:ScrollBarRectKey)?|I(?:OPrivilegesTag|gnoreCase(?:Bit|Mask)|llegalToCrossDataBoundariesErr|mageWithQD(?:Bit|Mask)|n(?:crementTypeSize|lineStateTag|valid(?:FrameIDErr|RunIndex)))|JustificationTag|L(?:eftT(?:ab|oRight)|in(?:eDirectionTag|k(?:NotPressed|Tracking|WasPressed)))|M(?:a(?:cOSEncoding|rginsTag)|o(?:nostyledText(?:Bit|Mask)|veAction)|ultiple(?:FrameType|StylesPerTextDocumentResType))|No(?:A(?:ppleEventHandlers(?:Bit|Mask)|utoWrap)|FontVariations|MatchErr|Selection(?:Bit|Mask)|TSMEver(?:Bit|Mask)|UserIOTag)|O(?:perationNotAllowedErr|utsideOf(?:FrameErr|LineErr))|Pa(?:geFrameType|steAction)|QDFont(?:ColorAttribute(?:Size)?|FamilyIDAttribute(?:Size)?|NameAttribute(?:Size)?|S(?:izeAttribute(?:Size)?|tyleAttribute(?:Size)?))|R(?:e(?:ad(?:Only(?:Bit|Mask)?|Write)|fConTag|startAppleEventHandlers(?:Bit|Mask))|i(?:chTextFormatData|ghtT(?:ab|oLeft))|un(?:Count(?:Bit|Mask)|IndexOutofBoundsErr))|S(?:aveStylesAsSTYLResource(?:Bit|Mask)|crollUnitsIn(?:Lines|Pixels|ViewRects)|election(?:O(?:ff|n)|StateTag)|how(?:End|Start|Window(?:Bit|Mask))|i(?:ngle(?:L(?:evelUndoTag|ineOnly(?:Bit|Mask))|StylePerTextDocumentResType)|zeContinuous(?:Bit|Mask))|omeOrAllTagsInvalidForRunErr|t(?:artOffset|yleContinuous(?:Bit|Mask))|upport(?:EditCommand(?:Processing|Updating)|FontCommand(?:Processing|Updating)|SpellCheckCommand(?:Processing|Updating))|ystemDefaultEncoding)|T(?:abSettingsTag|ext(?:Data|E(?:ditStyleFrameType|ncodingAttribute(?:Size)?)|File|InputCount(?:Bit|Mask)|RectKey|ensionFile)|ypingAction)|U(?:RLAttribute|n(?:doLastAction|icode(?:Encoding|Text(?:Data|File)))|se(?:Bottomline|C(?:arbonEvents|urrentSelection)|EncodingWordRules(?:Bit|Mask)|Inline|QDforImaging(?:Bit|Mask)|ScriptDefaultValue|rCanceledOperationErr))|V(?:ertical(?:ScrollBarRectKey)?|i(?:ewRectKey|sibilityTag))|W(?:ant(?:HScrollBar(?:Bit|Mask)|VScrollBar(?:Bit|Mask))|illDefaultTo(?:ATSUI(?:Bit|Mask)|CarbonEvent(?:Bit|Mask))|ordWrapStateTag))|a(?:bCharCode|llCapsSelector|sk(?:CreationException|TerminationException))|e(?:mporary(?:FolderType|ItemsIn(?:CacheDataFolderType|UserDomainFolderType))|xt(?:Center|Encoding(?:ANSEL|B(?:aseName|ig5(?:_(?:E|HKSCS_1999))?)|CNS_11643_92_P(?:1|2|3)|D(?:OS(?:Arabic|BalticRim|C(?:anadianFrench|hinese(?:Simplif|Trad)|yrillic)|Greek(?:1|2)?|Hebrew|Icelandic|Japanese|Korean|Latin(?:1|2|US)|Nordic|Portuguese|Russian|T(?:hai|urkish))|efault(?:Format|Variant))|E(?:BCDIC_(?:CP037|LatinCore|US)|UC_(?:CN|JP|KR|TW))|F(?:ormatName|ullName)|GB(?:K_95|_(?:18030_200(?:0|5)|2312_80))|HZ_GB_2312|ISO(?:10646_1993|Latin(?:1(?:0)?|2|3|4|5|6|7|8|9|Arabic|Cyrillic|Greek|Hebrew)|_2022_(?:CN(?:_EXT)?|JP(?:_(?:1|2|3))?|KR))|JIS_(?:C6226_78|X02(?:0(?:1_76|8_(?:83|90))|1(?:2_90|3_MenKuTen)))|K(?:OI8_(?:R|U)|SC_5601_(?:87|92_Johab))|M(?:ac(?:Ar(?:abic|menian)|B(?:engali|urmese)|C(?:e(?:ltic|ntralEurRoman)|hinese(?:Simp|Trad)|roatian|yrillic)|D(?:evanagari|ingbats)|E(?:astEurRoman|thiopic|xtArabic)|Farsi|G(?:aelic|e(?:ez|orgian)|reek|u(?:jarati|rmukhi))|H(?:FS|ebrew)|I(?:celandic|nuit)|Japanese|K(?:annada|eyboardGlyphs|hmer|orean)|Laotian|M(?:alayalam|ongolian)|Oriya|R(?:Symbol|oman(?:Latin1|ian)?)|S(?:i(?:mpChinese|nhalese)|ymbol)|T(?:amil|elugu|hai|ibetan|radChinese|urkish)|U(?:krainian|ni(?:code|nterp))|V(?:T100|ietnamese))|ultiRun)|NextStep(?:Japanese|Latin)|ShiftJIS(?:_X0213(?:_00)?)?|U(?:S_ASCII|n(?:icode(?:Default|V(?:1(?:0_0|1_0|2_1|_1)|2_(?:0|1)|3_(?:0|1|2)|4_0|5_(?:0|1)|6_(?:0|1|3)|7_0|8_0|9_0))|known))|V(?:ISCII|ariantName)|Windows(?:A(?:NSI|rabic)|BalticRim|Cyrillic|Greek|Hebrew|KoreanJohab|Latin(?:1|2|5)|Vietnamese)|sFolder(?:Icon|Type))|Flush(?:Default|Left|Right)|LanguageDontCare|MalformedInputErr|RegionDontCare|S(?:criptDontCare|ervice(?:Class|DocumentInterfaceType|InputModePropertyTag|JaTypingMethodPropertyTag)?|pacingType)|ToSpeech(?:SynthType|Voice(?:BundleType|FileType|Type))|Un(?:definedElementErr|supportedEncodingErr)))|h(?:eme(?:A(?:ctive(?:Alert(?:BackgroundBrush|TextColor)|BevelButtonTextColor|D(?:ialog(?:BackgroundBrush|TextColor)|ocumentWindowTitleTextColor)|M(?:enuItemTextColor|o(?:delessDialog(?:BackgroundBrush|TextColor)|vableModalWindowTitleTextColor))|P(?:lacardTextColor|opup(?:ArrowBrush|ButtonTextColor|LabelTextColor|WindowTitleColor)|ushButtonTextColor)|RootMenuTextColor|ScrollBarDelimiterBrush|UtilityWindow(?:BackgroundBrush|TitleTextColor)|WindowHeaderTextColor)|dornment(?:Arrow(?:Do(?:ubleArrow|wnArrow)|LeftArrow|RightArrow|UpArrow)|D(?:efault|rawIndicatorOnly)|Focus|Header(?:Button(?:LeftNeighborSelected|NoS(?:hadow|ortArrow)|RightNeighborSelected|S(?:hadowOnly|ortUp))|MenuButton)|No(?:Shadow|ne)|RightToLeft|ShadowOnly)|l(?:ert(?:HeaderFont|Window)|iasArrowCursor)|pp(?:earanceFileNameTag|l(?:eGuideCoachmarkBrush|icationFont))|rrow(?:3pt|5pt|7pt|9pt|Button(?:Mini|Small)?|Cursor|Down|Left|Right|Up))|B(?:ackground(?:ListViewWindowHeader|Metal|Placard|SecondaryGroupBox|TabPane|WindowHeader)|evelButton(?:Inset|Large|Medium|Small)?|ottom(?:InsideArrowPressed|OutsideArrowPressed|TrackPressed)|rush(?:A(?:ctiveAreaFill|l(?:ertBackground(?:Active|Inactive)|ternatePrimaryHighlightColor)|ppleGuideCoachmark)|B(?:evel(?:Active(?:Dark|Light)|Inactive(?:Dark|Light))|lack|utton(?:Active(?:Dark(?:Highlight|Shadow)|Light(?:Highlight|Shadow))|F(?:ace(?:Active|Inactive|Pressed)|rame(?:Active|Inactive))|Inactive(?:Dark(?:Highlight|Shadow)|Light(?:Highlight|Shadow))|Pressed(?:Dark(?:Highlight|Shadow)|Light(?:Highlight|Shadow))))|ChasingArrows|D(?:ialogBackground(?:Active|Inactive)|ocumentWindowBackground|ra(?:gHilite|werBackground))|F(?:inderWindowBackground|ocusHighlight)|IconLabelBackground(?:Selected)?|ListView(?:Background|ColumnDivider|EvenRowBackground|OddRowBackground|S(?:eparator|ortColumnBackground))|M(?:enuBackground(?:Selected)?|o(?:delessDialogBackground(?:Active|Inactive)|vableModalBackground))|NotificationWindowBackground|P(?:assiveAreaFill|opupArrow(?:Active|Inactive|Pressed)|rimaryHighlightColor)|S(?:crollBarDelimiter(?:Active|Inactive)|econdaryHighlightColor|heetBackground(?:Opaque|Transparent)?|taticAreaFill)|ToolbarBackground|UtilityWindowBackground(?:Active|Inactive)|White)|utton(?:Mixed|O(?:ff|n)))|C(?:h(?:asingArrowsBrush|eckBox(?:C(?:heckMark|lassicX)|Mini|Small)?)|losedHandCursor|o(?:mboBox(?:Mini|Small)?|nt(?:extualMenuArrowCursor|rolSoundsMask)|pyArrowCursor|unting(?:DownHandCursor|Up(?:AndDownHandCursor|HandCursor)))|rossCursor|u(?:rrentPortFont|stomThemesFileType))|D(?:ataFileType|blClickCollapseTag|e(?:faultAdornment|sktopP(?:attern(?:NameTag|Tag)|icture(?:Ali(?:asTag|gnmentTag)|NameTag)))|i(?:alogWindow|s(?:abled(?:MenuItemTextColor|RootMenuTextColor)|closure(?:Button|Down|Left|Right|Triangle)))|ocumentWindow(?:BackgroundBrush)?|ra(?:g(?:HiliteBrush|Sound(?:Dragging|Grow(?:UtilWindow|Window)|Move(?:Alert|Dialog|Icon|UtilWindow|Window)|None|S(?:crollBar(?:Arrow(?:Decreasing|Increasing)|Ghost|Thumb)|lider(?:Ghost|Thumb))))|w(?:IndicatorOnly|erWindow)))|E(?:mphasizedSystemFont|xamplePictureIDTag)|F(?:inder(?:SoundsMask|WindowBackgroundBrush)|ocus(?:Adornment|HighlightBrush))|Grow(?:Down|Left|Right|Up)|HighlightColor(?:NameTag|Tag)|I(?:BeamCursor|conLabel(?:BackgroundBrush|TextColor)|n(?:active(?:Alert(?:BackgroundBrush|TextColor)|BevelButtonTextColor|D(?:ialog(?:BackgroundBrush|TextColor)|ocumentWindowTitleTextColor)|Mo(?:delessDialog(?:BackgroundBrush|TextColor)|vableModalWindowTitleTextColor)|P(?:lacardTextColor|opup(?:ArrowBrush|ButtonTextColor|LabelTextColor|WindowTitleColor)|ushButtonTextColor)|ScrollBarDelimiterBrush|UtilityWindow(?:BackgroundBrush|TitleTextColor)|WindowHeaderTextColor)|cDecButton(?:Mini|Small)?|determinateBar(?:Large|M(?:edium|ini))?))|L(?:a(?:belFont|rge(?:BevelButton|IndeterminateBar|ProgressBar|RoundButton|TabHeight(?:Max)?))|eft(?:InsideArrowPressed|OutsideArrowPressed|TrackPressed)|ist(?:HeaderButton|View(?:BackgroundBrush|S(?:eparatorBrush|ortColumnBackgroundBrush)|TextColor)))|M(?:e(?:dium(?:BevelButton|IndeterminateBar|ProgressBar|S(?:crollBar|lider))|nu(?:Active|Bar(?:Inactive|Normal|Selected)|Disabled|Item(?:A(?:lignRight|t(?:Bottom|Top))|CmdKeyFont|Font|H(?:asIcon|ier(?:Background|archical))|MarkFont|NoBackground|P(?:lain|opUpBackground)|Scroll(?:DownArrow|UpArrow))|S(?:elected|oundsMask|quareMenuBar)|T(?:itleFont|ype(?:Hierarchical|Inactive|P(?:opUp|ullDown))))|tric(?:B(?:estListHeaderHeight|uttonRounded(?:Height|RecessedHeight))|C(?:heckBox(?:GlyphHeight|Height|Width)|omboBox(?:Large(?:BottomShadowOffset|DisclosureWidth|RightShadowOffset)|Mini(?:BottomShadowOffset|DisclosureWidth|RightShadowOffset)|Small(?:BottomShadowOffset|DisclosureWidth|RightShadowOffset)))|Disclosure(?:Button(?:Height|Size|Width)|Triangle(?:Height|Width))|EditText(?:FrameOutset|Whitespace)|FocusRectOutset|HSlider(?:Height|Tick(?:Height|Offset))|ImageWellThickness|L(?:arge(?:ProgressBarThickness|RoundButtonSize|Tab(?:CapsWidth|Height))|i(?:st(?:BoxFrameOutset|HeaderHeight)|ttleArrows(?:Height|Mini(?:Height|Width)|Small(?:Height|Width)|Width)))|M(?:enu(?:ExcludedMarkColumnWidth|I(?:conTrailingEdgeMargin|ndentWidth)|Mark(?:ColumnWidth|Indent)|Text(?:LeadingEdgeMargin|TrailingEdgeMargin))|ini(?:CheckBox(?:Height|Width)|DisclosureButton(?:Height|Width)|HSlider(?:Height|MinThumbWidth|Tick(?:Height|Offset))|P(?:opupButtonHeight|u(?:llDownHeight|shButtonHeight))|RadioButton(?:Height|Width)|Tab(?:CapsWidth|FrameOverlap|Height|Overlap)|VSlider(?:MinThumbHeight|Tick(?:Offset|Width)|Width)))|NormalProgressBarThickness|P(?:aneSplitterHeight|opupButtonHeight|r(?:imaryGroupBoxContentInset|ogressBar(?:ShadowOutset|Thickness))|u(?:llDownHeight|shButtonHeight))|R(?:adioButton(?:GlyphHeight|Height|Width)|e(?:levanceIndicatorHeight|sizeControlHeight)|ound(?:ButtonSize|TextField(?:Content(?:Height|Inset(?:Bottom|Left|Right|Top|WithIcon(?:Left|Right)))|MiniContent(?:Height|Inset(?:Bottom|Left|Right|Top|WithIcon(?:Left|Right)))|SmallContent(?:Height|Inset(?:Bottom|Left|Right|Top|WithIcon(?:Left|Right))))))|S(?:crollBar(?:MinThumb(?:Height|Width)|Overlap|Width)|e(?:condaryGroupBoxContentInset|paratorSize)|liderMinThumb(?:Height|Width)|mall(?:CheckBox(?:Height|Width)|DisclosureButton(?:Height|Width)|HSlider(?:Height|MinThumbWidth|Tick(?:Height|Offset))|P(?:aneSplitterHeight|opupButtonHeight|rogressBar(?:ShadowOutset|Thickness)|u(?:llDownHeight|shButtonHeight))|R(?:adioButton(?:Height|Width)|esizeControlHeight)|ScrollBar(?:MinThumb(?:Height|Width)|Width)|Tab(?:CapsWidth|FrameOverlap|Height|Overlap)|VSlider(?:MinThumbHeight|Tick(?:Offset|Width)|Width)))|T(?:ab(?:FrameOverlap|IndentOrStyle|Overlap)|extured(?:PushButtonHeight|SmallPushButtonHeight)|itleBarControlsHeight)|VSlider(?:Tick(?:Offset|Width)|Width)))|ini(?:CheckBox|IndeterminateBar|ProgressBar|RadioButton|S(?:crollBar|lider|ystemFont))|ovable(?:AlertWindow|DialogWindow))|N(?:ameTag|o(?:Adornment|Sounds|rmal(?:CheckBox|RadioButton)|tAllowedCursor))|OpenHandCursor|P(?:l(?:a(?:inDialogWindow|tinumFileType)|usCursor)|o(?:intingHandCursor|ofCursor|pup(?:Button(?:Mini|Normal|Small)?|Tab(?:CenterOn(?:Offset|Window)|NormalPosition)|Window))|r(?:essed(?:BevelButtonTextColor|P(?:lacardTextColor|opup(?:ArrowBrush|ButtonTextColor)|ushButtonTextColor))|ogressBar(?:Large|M(?:edium|ini))?)|ushButton(?:Font|Inset(?:Small)?|Mini|Normal|Small|Textured(?:Small)?)?)|R(?:adioButton(?:Mini|Small)?|e(?:levanceBar|size(?:DownCursor|Left(?:Cursor|RightCursor)|RightCursor|Up(?:Cursor|DownCursor)))|ight(?:InsideArrowPressed|OutsideArrowPressed|T(?:oLeftAdornment|rackPressed))|ound(?:Button(?:Help|Large)?|edBevelButton))|S(?:avvyMenuResponse|crollBar(?:Arrow(?:StyleTag|s(?:LowerRight|Single))|M(?:edium|ini)|Small|Thumb(?:Normal|Proportional|StyleTag))?|elected(?:MenuItemTextColor|RootMenuTextColor)|h(?:adowDialogWindow|eetWindow)|lider(?:M(?:edium|ini)|Small)?|m(?:all(?:BevelButton|CheckBox|EmphasizedSystemFont|RadioButton|S(?:crollBar|lider|ystemFont(?:Tag)?)|TabHeight(?:Max)?)|oothFont(?:EnabledTag|MinSizeTag))|ound(?:Alert(?:Close|Open)|B(?:alloon(?:Close|Open)|evel(?:E(?:nter|xit)|Press|Release)|utton(?:E(?:nter|xit)|Press|Release))|C(?:ancelButton(?:E(?:nter|xit)|Press|Release)|heckbox(?:E(?:nter|xit)|Press|Release)|opyDone)|D(?:efaultButton(?:E(?:nter|xit)|Press|Release)|i(?:alog(?:Close|Open)|s(?:closure(?:E(?:nter|xit)|Press|Release)|k(?:Eject|Insert)))|ragTarget(?:Drop|Hilite|Unhilite))|EmptyTrash|FinderDragO(?:ffIcon|nIcon)|L(?:aunchApp|ittleArrow(?:Dn(?:Press|Release)|E(?:nter|xit)|Up(?:Press|Release)))|M(?:askTag|enu(?:Close|Item(?:Hilite|Release)|Open))|N(?:ewItem|one)|Popup(?:E(?:nter|xit)|Press|Release|Window(?:Close|Open))|R(?:adio(?:E(?:nter|xit)|Press|Release)|e(?:ceiveDrop|solveAlias))|S(?:croll(?:Arrow(?:E(?:nter|xit)|Press|Release)|EndOfTrack|TrackPress)|electItem|lider(?:EndOfTrack|TrackPress))|T(?:ab(?:E(?:nter|xit)|Pressed|Release)|rack(?:FileType|NameTag))|UtilWin(?:C(?:lose(?:E(?:nter|xit)|Press|Release)|ollapse(?:E(?:nter|xit)|Press|Release))|DragBoundary|Zoom(?:E(?:nter|xit)|Press|Release)|dow(?:Activate|C(?:lose|ollapse(?:Down|Up))|Open|Zoom(?:In|Out)))|Window(?:Activate|C(?:lose(?:E(?:nter|xit)|Press|Release)?|ollapse(?:Down|E(?:nter|xit)|Press|Release|Up))|DragBoundary|Open|Zoom(?:E(?:nter|xit)|In|Out|Press|Release))|sEnabledTag)|p(?:ecifiedFont|inningCursor)|tate(?:Active|Disabled|Inactive|Pressed(?:Down|Up)?|Rollover|Unavailable(?:Inactive)?)|ystemFont(?:Detail(?:Emphasized)?|Tag)?)|T(?:ab(?:East|Front(?:Inactive|Unavailable)?|No(?:nFront(?:Inactive|Pressed|Unavailable)?|rth)|PaneOverlap|South|West)|extColor(?:Alert(?:Active|Inactive)|B(?:evelButton(?:Active|Inactive|Pressed|Sticky(?:Active|Inactive))|lack)|D(?:ialog(?:Active|Inactive)|ocumentWindowTitle(?:Active|Inactive))|IconLabel(?:Selected)?|ListView|M(?:enuItem(?:Active|Disabled|Selected)|o(?:delessDialog(?:Active|Inactive)|vableModalWindowTitle(?:Active|Inactive)))|Notification|P(?:lacard(?:Active|Inactive|Pressed)|opup(?:Button(?:Active|Inactive|Pressed)|Label(?:Active|Inactive)|WindowTitle(?:Active|Inactive))|ushButton(?:Active|Inactive|Pressed))|RootMenu(?:Active|Disabled|Selected)|SystemDetail|Tab(?:Front(?:Active|Inactive)|NonFront(?:Active|Inactive|Pressed))|UtilityWindowTitle(?:Active|Inactive)|W(?:hite|indowHeader(?:Active|Inactive)))|humb(?:Downward|P(?:lain|ressed)|Upward)|o(?:olbarFont|p(?:InsideArrowPressed|OutsideArrowPressed|TrackPressed))|rack(?:Active|Disabled|H(?:asFocus|ideTrack|orizontal)|Inactive|No(?:ScrollBarArrows|thingToScroll)|RightToLeft|ShowThumb|ThumbRgnIsNotGhost))|U(?:serDefinedTag|tility(?:SideWindow|Window(?:TitleFont)?))|V(?:ariant(?:BaseTintTag|NameTag)|iewsFont(?:SizeTag|Tag)?)|W(?:atchCursor|i(?:dget(?:C(?:loseBox|ollapseBox)|DirtyCloseBox|ToolbarButton|ZoomBox)|ndow(?:Has(?:C(?:loseBox|ollapseBox)|Dirty|FullZoom|Grow|HorizontalZoom|T(?:itleText|oolbarButton)|VerticalZoom)|IsCollapsed|SoundsMask|TitleFont)))|sFolderType)|i(?:nkCStackBased|rdWidth(?:NumbersSelector|TextSelector))|umbnail(?:32BitData|8BitMask))|i(?:ckScale|le(?:IconVariant|dOnScreen)|mePitchParam_(?:EffectBlend|Pitch|Rate)|tlingCapsSelector)|oo(?:ManyIOWindowsErr|lbar(?:A(?:dvancedIcon|pplicationsFolderIcon)|CustomizeIcon|D(?:e(?:leteIcon|sktopFolderIcon)|o(?:cumentsFolderIcon|wnloadsFolderIcon))|FavoritesIcon|HomeIcon|InfoIcon|L(?:abelsIcon|ibraryFolderIcon)|M(?:ovieFolderIcon|usicFolderIcon)|P(?:icturesFolderIcon|ublicFolderIcon)|SitesFolderIcon|UtilitiesFolderIcon|WindowClass))|r(?:a(?:c(?:eException|kMouseLocationOption(?:DontConsumeMouseUp|IncludeScrollWheel))|ditional(?:Alt(?:F(?:iveSelector|ourSelector)|OneSelector|T(?:hreeSelector|woSelector))|CharactersSelector|NamesCharactersSelector)|ns(?:codingCompositionO(?:ffSelector|nSelector)|form(?:Disabled|Label(?:1|2|3|4|5|6|7)|None|O(?:ffline|pen)|Selected(?:Disabled|O(?:ffline|pen))?)|l(?:at(?:e(?:Get(?:FileTranslationList|ScrapTranslationList(?:ConsideringData)?|TranslatedFilename)|Identify(?:File|Scrap)|Translate(?:File|Scrap))|ion(?:DataTranslation|FileTranslation|ScrapProgressDialogID)|orCanGenerateFilename)|iterationType)|parentEncod(?:edPixel|ing(?:Shift)?))|pException|sh(?:FolderType|Icon(?:Resource)?))|ueType(?:F(?:latFontIcon|ontIcon)|MultiFlatFontIcon)|yAuthenticate)|wo(?:ByteCode|WayEncryptPassword)|yp(?:eKCItemAttr|ographicExtrasType))|U(?:AZoomFocusType(?:InsertionPoint|Other)|C(?:BidiCat(?:ArabicNumber|B(?:lockSeparator|oundaryNeutral)|CommonNumberSeparator|EuroNumber(?:Separator|Terminator)?|FirstStrongIsolate|LeftRight(?:Embedding|Isolate|Override)?|No(?:nSpacingMark|tApplicable)|OtherNeutral|PopDirectional(?:Format|Isolate)|RightLeft(?:Arabic|Embedding|Isolate|Override)?|SegmentSeparator|Whitespace)|C(?:harPropType(?:BidiCategory|CombiningClass|DecimalDigitValue|GenlCategory)|ollate(?:C(?:aseInsensitiveMask|omposeInsensitiveMask)|Di(?:acritInsensitiveMask|gits(?:AsNumberMask|OverrideMask))|PunctuationSignificantMask|StandardOptions|Type(?:HFSExtended|Mask|S(?:hiftBits|ourceMask))|WidthInsensitiveMask))|GenlCat(?:Letter(?:Lowercase|Modifier|Other|Titlecase|Uppercase)|Mark(?:Enclosing|NonSpacing|SpacingCombining)|Number(?:DecimalDigit|Letter|Other)|Other(?:Control|Format|NotAssigned|PrivateUse|Surrogate)|Punct(?:C(?:lose|onnector)|Dash|FinalQuote|InitialQuote|O(?:pen|ther))|S(?:eparator(?:Line|Paragraph|Space)|ymbol(?:Currency|M(?:ath|odifier)|Other)))|HighSurrogateRange(?:End|Start)|Key(?:Action(?:AutoKey|D(?:isplay|own)|Up)|Layout(?:FeatureInfoFormat|HeaderFormat)|ModifiersToTableNumFormat|Output(?:GetIndexMask|S(?:equenceIndexMask|tateIndexMask)|TestForIndexMask)|S(?:equenceDataIndexFormat|tate(?:Entry(?:RangeFormat|TerminalFormat)|RecordsIndexFormat|TerminatorsFormat))|T(?:oCharTableIndexFormat|ranslateNoDeadKeys(?:Bit|Mask)))|LowSurrogateRange(?:End|Start)|OutputBufferTooSmall|T(?:S(?:Direction(?:Next|Previous)|NoKeysAddedToObjectErr|Options(?:DataIsOrderedMask|NoneMask|ReleaseStringMask)|SearchListErr)|extBreak(?:C(?:harMask|lusterMask)|GoBackwardsMask|IterateMask|L(?:eadingEdgeMask|ineMask|ocatorMissingType)|ParagraphMask|WordMask)|oken(?:NotFound|izer(?:IterationFinished|UnknownLang))|ypeSelectMaxListSize))|I(?:Mode(?:All(?:Hidden|Suppressed)|Content(?:Hidden|Suppressed)|Normal)|Option(?:A(?:nimateMenuBar|utoShowMenuBar)|Disable(?:AppleMenu|ForceQuit|Hide|MenuBarTransparency|ProcessSwitch|SessionTerminate)))|RL(?:68kNotSupportedError|A(?:bort(?:Initiated(?:Event|Mask)|ingState)|ccessNotAvailableError|ll(?:BufferEventsMask|EventsMask|NonBufferEventsMask)|uthenticationError)|BinHexFileFlag|Co(?:mpleted(?:Event(?:Mask)?|State)|nnectingState)|D(?:ataAvailable(?:Event(?:Mask)?|State)|e(?:binhexOnlyFlag|stinationExistsError)|i(?:rectoryListingFlag|splay(?:AuthFlag|ProgressFlag))|o(?:Not(?:DeleteOnErrorFlag|TryAnonymousFlag)|wnloading(?:Event|Mask|State)))|E(?:rrorOccurred(?:Event(?:Mask)?|State)|x(?:pand(?:AndVerifyFlag|FileFlag)|tensionFailureError))|FileEmptyError|I(?:n(?:itiat(?:edEvent(?:Mask)?|ingState)|valid(?:C(?:allError|onfigurationError)|URL(?:Error|ReferenceError)))|sDirectoryHintFlag)|LookingUpHostState|N(?:oAutoRedirectFlag|ullState)|P(?:er(?:centEvent(?:Mask)?|iodicEvent(?:Mask)?)|ro(?:gressAlreadyDisplayedError|perty(?:BufferTooSmallError|ChangedEvent(?:Mask)?|NotYetKnownError)))|Re(?:placeExistingFlag|s(?:ervedFlag|ourceFound(?:Event(?:Mask)?|State)|umeDownloadFlag))|S(?:erverBusyError|ystemEvent(?:Mask)?)|TransactionComplete(?:Event(?:Mask)?|State)|U(?:n(?:knownPropertyError|s(?:ettablePropertyError|upportedSchemeError))|pload(?:Flag|ing(?:Event|Mask|State))))|SB(?:A(?:bortedError|lreadyOpenErr)|B(?:adDispatchTable|itstufErr|uf(?:OvrRunErr|UnderRunErr))|C(?:RCErr|ompletionError)|D(?:ataToggleErr|evice(?:Busy|Disconnected|Err|NotSuspended|PowerProblem|Suspended))|EndpointStallErr|FlagsError|In(?:correctTypeErr|ternal(?:Err|Reserved(?:1(?:0)?|2|3|4|5|6|7|8|9))|validBuffer)|LinkErr|No(?:BandwidthError|De(?:lay|viceErr)|Err|Tran|t(?:Found|Handled|RespondingErr|Sent(?:1Err|2Err)))|O(?:utOfMemoryErr|verRunErr)|P(?:B(?:LengthError|VersionError)|IDCheckErr|ending|ipe(?:IdleError|StalledError)|ortDisabled)|Queue(?:Aborted|Full)|R(?:es(?:1Err|2Err)|qErr)|T(?:imedOut|ooMany(?:PipesErr|TransactionsErr))|Un(?:derRunErr|known(?:DeviceErr|InterfaceErr|Notification|PipeErr|RequestErr))|WrongPIDErr)|TC(?:DefaultOptions|OverflowErr|UnderflowErr)|YVY422PixelFormat|n(?:connectedSelector|icode(?:16BitFormat|32BitFormat|ByteOrderMark|C(?:anonical(?:CompVariant|DecompVariant)|ollationClass)|D(?:e(?:compositionType|faultDirection(?:Mask)?)|irectionality(?:Bits|Mask)|ocument(?:InterfaceType)?)|F(?:allback(?:Custom(?:First|Only)|Default(?:First|Only)|InterruptSafeMask|Sequencing(?:Bits|Mask))|orceASCIIRange(?:Bit|Mask))|HFSPlus(?:CompVariant|DecompVariant)|Keep(?:Info(?:Bit|Mask)|SameEncoding(?:Bit|Mask))|L(?:eftToRight(?:Mask)?|ooseMappings(?:Bit|Mask))|Ma(?:pLineFeedToReturn(?:Bit|Mask)|tch(?:Other(?:Base(?:Bit|Mask)|Format(?:Bit|Mask)|Variant(?:Bit|Mask))|Unicode(?:Base(?:Bit|Mask)|Format(?:Bit|Mask)|Variant(?:Bit|Mask)))|xDecomposedVariant)|No(?:Co(?:mp(?:atibilityVariant|osedVariant)|rporateVariant)|HalfwidthChars(?:Bit|Mask)|Subset|rmalizationForm(?:C|D)|t(?:AChar|FromInputMethod))|ObjectReplacement|R(?:eplacementChar|ightToLeft(?:Mask)?)|S(?:CSUFormat|tringUnterminated(?:Bit|Mask)|wappedByteOrderMark)|Text(?:BreakClass|Run(?:Bit|Heuristics(?:Bit|Mask)|Mask))|U(?:TF(?:16(?:BEFormat|Format|LEFormat)|32(?:BEFormat|Format|LEFormat)|7Format|8Format)|se(?:ExternalEncodingForm(?:Bit|Mask)|Fallbacks(?:Bit|Mask)|HFSPlusMapping|LatestMapping))|VerticalForm(?:Bit|Mask))|known(?:Exception|FSObjectIcon|Language|Script)|lock(?:KCEvent(?:Mask)?|StateKCStatus|edIcon)|mappedMemoryException|resolvablePageFaultException|supported(?:CardErr|FunctionErr|ModeErr|VsErr)|wrapKCItemAttr)|p(?:ArrowCharCode|date(?:A(?:E(?:TE|UT)|ctiveInputArea)|KCEvent(?:Mask)?)|per(?:AndLowerCaseSelector|Case(?:NumbersSelector|PetiteCapsSelector|SmallCapsSelector|Type)))|se(?:AtoB|B(?:estGuess|to(?:A|B))|CurrentISA|NativeISA|Pr(?:emadeThread|ofileIntent)|WidePositioning|r(?:D(?:ialogItem|omain)|FolderIcon|I(?:DiskIcon|con)|NameAndPasswordFlag|P(?:PDDomain|referredAlert)|SpecificTmpFolderType|sFolder(?:Icon|Type)))|tilit(?:iesFolder(?:Icon|Type)|yWindowClass))|V(?:CBFlags(?:H(?:FSPlusAPIs(?:Bit|Mask)|ardwareGone(?:Bit|Mask))|IdleFlush(?:Bit|Mask)|VolumeDirty(?:Bit|Mask))|K_(?:ANSI_(?:0|1|2|3|4|5|6|7|8|9|A|B(?:ackslash)?|C(?:omma)?|D|E(?:qual)?|F|G(?:rave)?|H|I|J|K(?:eypad(?:0|1|2|3|4|5|6|7|8|9|Clear|D(?:ecimal|ivide)|E(?:nter|quals)|M(?:inus|ultiply)|Plus))?|L(?:eftBracket)?|M(?:inus)?|N|O|P(?:eriod)?|Q(?:uote)?|R(?:ightBracket)?|S(?:emicolon|lash)?|T|U|V|W|X|Y|Z)|C(?:apsLock|o(?:mmand|ntrol))|D(?:elete|ownArrow)|E(?:nd|scape)|F(?:1(?:0|1|2|3|4|5|6|7|8|9)?|2(?:0)?|3|4|5|6|7|8|9|orwardDelete|unction)|H(?:elp|ome)|ISO_Section|JIS_(?:Eisu|K(?:ana|eypadComma)|Underscore|Yen)|LeftArrow|Mute|Option|Page(?:Down|Up)|R(?:eturn|ight(?:Arrow|Co(?:mmand|ntrol)|Option|Shift))|S(?:hift|pace)|Tab|UpArrow|Volume(?:Down|Up))|LibTag2|arispeedParam_Playback(?:Cents|Rate)|er(?:ifyKCItemAttr|tical(?:Constraint|FractionsSelector|PositionType|SubstitutionType|TabCharCode))|o(?:icesFolder(?:Icon|Type)|lume(?:KCItemAttr|RootFolderType|SettingsFolderType)))|W(?:akeToDoze|hereToEmptyTrashFolderType|i(?:d(?:ePosOffsetBit|getsFolderType)|ndow(?:A(?:ctivationScope(?:All|Independent|None)|lertP(?:osition(?:MainScreen|On(?:MainScreen|ParentWindow(?:Screen)?)|ParentWindow(?:Screen)?)|roc)|syncDragAttribute)|BoundsChange(?:OriginChanged|SizeChanged|User(?:Drag|Resize)|Zoom)|C(?:a(?:n(?:BeVisibleWithoutLoginAttribute|Collapse|DrawInCurrentPort|G(?:etWindowRegion|row)|MeasureTitle|SetupProxyDragImage|Zoom)|scade(?:On(?:MainScreen|ParentWindow(?:Screen)?)|StartAtParentWindowScreen))|enter(?:MainScreen|On(?:MainScreen|ParentWindow(?:Screen)?)|ParentWindow(?:Screen)?)|loseBox(?:Attribute|Rgn)|o(?:llapseBox(?:Attribute|Rgn)|mpositingAttribute|n(?:strain(?:AllowPartial|CalcOnly|M(?:ayResize|ove(?:Minimum|RegardlessOfFit))|StandardOptions|Use(?:SpecifiedBounds|TransitionWindow))|tentRgn)))|D(?:ef(?:HIView|ObjectClass|Proc(?:ID|Ptr|Type)|SupportsColorGrafPort|aultPosition|initionVersion(?:One|Two))|ialogDefProcResID|o(?:cument(?:DefProcResID|Proc)|esNotCycleAttribute)|ra(?:gRgn|wer(?:Clos(?:ed|ing)|Open(?:ing)?)))|Edge(?:Bottom|Default|Left|Right|Top)|F(?:adeTransitionEffect|loat(?:FullZoom(?:GrowProc|Proc)|GrowProc|HorizZoom(?:GrowProc|Proc)|Proc|Side(?:FullZoom(?:GrowProc|Proc)|GrowProc|HorizZoom(?:GrowProc|Proc)|Proc|VertZoom(?:GrowProc|Proc))|VertZoom(?:GrowProc|Proc))|rameworkScaledAttribute|ullZoom(?:Attribute|DocumentProc|GrowDocumentProc))|G(?:enieTransitionEffect|lobalPortRgn|ro(?:up(?:Attr(?:FixedLevel|HideOnCollapse|LayerTogether|MoveTogether|PositionFixed|S(?:elect(?:AsLayer|able)|haredActivation)|ZOrderFixed)|Contents(?:Re(?:curse|turnWindows)|Visible)|Level(?:Active|Inactive|Promoted))|w(?:DocumentProc|Rgn)))|H(?:as(?:RoundBottomBarCornersAttribute|TitleBar)|i(?:de(?:On(?:FullScreenAttribute|SuspendAttribute)|TransitionAction)|ghResolutionCapableAttribute)|oriz(?:Zoom(?:DocumentProc|GrowDocumentProc)|ontalZoomAttribute))|I(?:gnoreClicksAttribute|nWindowMenuAttribute|s(?:Alert|CollapsedState|Modal|Opaque))|L(?:atentVisible(?:AppHidden|Collapsed(?:Group|Owner)|F(?:loater|ullScreen)|Suspend)|iveResizeAttribute)|M(?:e(?:nuIncludeRotate|tal(?:Attribute|NoContentSeparatorAttribute))|o(?:dal(?:DialogProc|ity(?:AppModal|None|SystemModal|WindowModal))|v(?:able(?:AlertProc|Modal(?:DialogProc|GrowProc))|eTransitionAction))|sg(?:C(?:alculateShape|leanUp)|Dra(?:gHilite|w(?:Grow(?:Box|Outline)|InCurrentPort)?)|Get(?:Features|GrowImageRegion|Region)|HitTest|Initialize|M(?:easureTitle|odified)|S(?:etupProxyDragImage|tateChanged)))|No(?:A(?:ctivatesAttribute|ttributes)|ConstrainAttribute|Position|ShadowAttribute|TitleBarAttribute|UpdatesAttribute)|O(?:paque(?:ForEventsAttribute|Rgn)|verlayProc)|P(?:aintProcOptionsNone|lainDialogProc|ropertyPersistent)|Resiz(?:ableAttribute|eTransitionAction)|S(?:h(?:adowDialogProc|eet(?:Alert(?:DefProcResID|Proc)|DefProcResID|Proc|TransitionEffect)|owTransitionAction)|i(?:deTitlebarAttribute|mple(?:DefProcResID|FrameProc|Proc))|lideTransitionEffect|t(?:a(?:gger(?:MainScreen|ParentWindow(?:Screen)?)|ndard(?:DocumentAttributes|FloatingAttributes|HandlerAttribute)|teTitleChanged)|ructureRgn)|upports(?:DragHilite|GetGrowImageRegion|ModifiedBit))|T(?:exturedSquareCornersAttribute|itle(?:BarRgn|ProxyIconRgn|TextRgn)|oolbarButton(?:Attribute|Rgn))|U(?:nifiedTitleAndToolbarAttribute|pdateRgn|tility(?:DefProcResID|SideTitleDefProcResID))|Vert(?:Zoom(?:DocumentProc|GrowDocumentProc)|icalZoomAttribute)|WantsDisposeAtProcessDeath|Zoom(?:BoxRgn|TransitionEffect)|sLatin1(?:PalmVariant|StandardVariant)))|or(?:d(?:FinalSwashesO(?:ffSelector|nSelector)|InitialSwashesO(?:ffSelector|nSelector))|kgroupFolderIcon)|r(?:PermKCStatus|apKCItemAttr|ite(?:FailureErr|ProtectedErr|Reference)))|X(?:86(?:ISA|RTA)|Lib(?:Tag1|Version))|Y(?:UV(?:211PixelFormat|411PixelFormat|SPixelFormat|UPixelFormat)|V(?:U9PixelFormat|YU422PixelFormat))|Zoom(?:Accelerate|Decelerate|NoAcceleration)|administratorUser|e(?:rnel(?:A(?:lreadyFreeErr|sync(?:ReceiveLimitErr|SendLimitErr)|ttributeErr)|CanceledErr|DeletePermissionErr|Ex(?:ceptionErr|ecut(?:ePermissionErr|ionLevelErr))|I(?:DErr|n(?:UseErr|completeErr))|O(?:bjectExistsErr|ptionsErr)|PrivilegeErr|Re(?:adPermissionErr|turnValueErr)|T(?:erminatedErr|imeoutErr)|Un(?:recoverableErr|supportedErr)|WritePermissionErr)|y(?:32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|A(?:E(?:A(?:djustMarksProc|ngle|rcAngle|ttaching)|B(?:aseAddr|estType|gnd(?:Color|Pattern)|ounds|ufferSize)|C(?:ellList|la(?:ssID|useOffsets)|o(?:lor(?:Table)?|mp(?:Operator|areProc)|ntainer|untProc)|ur(?:rentPoint|ve(?:Height|Width)))|D(?:a(?:shStyle|ta)|e(?:f(?:aultType|initionRect)|s(?:cType|iredClass|tination))|o(?:AntiAlias|Dithered|Rotate|Scale|Translate)|ragging)|E(?:ditionFileLoc|lements|ndPoint|rrorObject|vent(?:Class|ID))|F(?:i(?:l(?:e(?:Type)?|l(?:Color|Pattern))|xLength)|lip(?:Horizontal|Vertical)|o(?:nt|rmula))|G(?:etErrDescProc|raphicObjects)|H(?:iliteRange|omograph(?:Accent|DicInfo|Weight))|I(?:D|mageQuality|n(?:dex|sertHere))|Key(?:Data|Form(?:s)?|word)|L(?:A(?:Homograph|Morpheme(?:Bundle|Path)?)|aunchedAs(?:LogInItem|ServiceItem)|e(?:ftSide|vel)|ineArrow|ogical(?:Operator|Terms))|M(?:ark(?:Proc|TokenProc)|o(?:rpheme(?:PartOfSpeechCode|TextRange)|veView))|N(?:ame|e(?:wElementLoc|xtBody)|oAutoRouting)|O(?:bject(?:1|2|Class)?|ff(?:Styles|set)|nStyles)|P(?:MTable|OSTHeaderData|aram(?:Flags|eters)|en(?:Color|Pattern|Width)|i(?:nRange|x(?:MapMinus|elDepth))|o(?:int(?:List|Size)?|sition)|ro(?:p(?:Data|Flags|ID|ert(?:ies|y))|tection))|R(?:angeSt(?:art|op)|e(?:corderCount|gionClass|nderAs|pl(?:acing|yHeaderData)|questedType|sult(?:Info)?)|o(?:t(?:Point|ation)|wList))|S(?:aveOptions|c(?:ale|riptTag)|e(?:archText|rverInstance)|howWhere|t(?:art(?:Angle|Point)|yles)|uiteID)|T(?:SM(?:DocumentRefcon|EventRe(?:cord|f)|GlyphInfoArray|ScriptTag|Text(?:F(?:MFont|ont)|PointSize))|arget|e(?:st|xt(?:Color|Font|Line(?:Ascent|Height)|PointSize|S(?:ervice(?:Encoding|MacEncoding)|tyles))?)|he(?:Data|Text)|r(?:ans(?:ferMode|lation)|yAsStructGraf))|U(?:niformStyles|pdate(?:On|Range)|s(?:erTerm|ing))|Version|W(?:hoseRangeSt(?:art|op)|indow|ritingCode)|XMLRe(?:plyData|questData))|S(?:Arg|P(?:ositionalArgs|reposition(?:A(?:bo(?:ut|ve)|gainst|partFrom|round|sideFrom|t)|B(?:e(?:low|neath|side|tween)|y)|F(?:or|rom)|Given|Has|In(?:steadOf|to)?|O(?:n(?:to)?|utOf|ver)|Since|T(?:hr(?:ough|u)|o)|Un(?:der|til)|With(?:out)?))|Returning|SubroutineName|UserRecordFields)|c(?:ceptTimeoutAttr|tualSenderAuditToken)|dd(?:itionalHTTPHeaders|ressAttr)|ll|pp(?:HandledCoercion|leEventAttributesAttr))|C(?:loseAllWindows|o(?:deMask|ntextualMenu(?:Attributes|CommandID|Modifiers|Name|Submenu)))|D(?:CM(?:Field(?:Attributes|DefaultData|FindMethods|Name|T(?:ag|ype))|MaxRecordSize)|i(?:rectObject|s(?:ableAuthenticationAttr|poseTokenProc))|own(?:Mask)?|riveNumber)|E(?:rror(?:Code|Number|String)|v(?:ent(?:ClassAttr|IDAttr|SourceAttr)|tDev))|GlobalPositionList|HighLevel(?:Class|ID)|I(?:CEditPreferenceDestination|conAndMask|nteractLevelAttr)|Key(?:Code|board)?|Local(?:PositionList|Where)|M(?:enuI(?:D|tem)|i(?:ni(?:1BitMask|4BitIcon|8BitIcon)|s(?:cellaneous|sedKeywordAttr))|odifiers)|NewBounds|O(?:SA(?:Dialect(?:Code|LangCode|Name|ScriptCode)|Source(?:End|Start))|ldFinderItems|ptionalKeywordAttr|riginal(?:AddressAttr|Bounds))|Pr(?:eDispatch|ocessSerialNumber)|R(?:PCMethod(?:Name|Param(?:Order)?)|e(?:directedDocumentList|ply(?:PortAttr|RequestedAttr)|turnIDAttr))|S(?:OAP(?:Action|MethodNameSpace(?:URI)?|S(?:MD(?:Namespace(?:URI)?|Type)|chemaVersion|tructureMetaData))|R(?:Recognizer|Speech(?:Result|Status))|cszResource|e(?:lect(?:Proc|ion)|nder(?:A(?:ppl(?:escriptEntitlementsAttr|ication(?:IdentifierEntitlementAttr|Sandboxed))|uditTokenAttr)|E(?:GIDAttr|UIDAttr)|GIDAttr|PIDAttr|UIDAttr))|mall(?:32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|IconAndMask)|ubjectAttr)|T(?:imeoutAttr|ransactionIDAttr)|U(?:p(?:Mask)?|ser(?:NameAttr|PasswordAttr))|W(?:he(?:n|re)|indow)|XMLDebuggingAttr))|fullPrivileges|i(?:Movie(?:FolderType|PlugInsFolderType|SoundEffectsFolderType)|o(?:AC(?:Access(?:BlankAccess(?:Bit|Mask)|Everyone(?:Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask))|Group(?:Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask))|Owner(?:Bit|Mask|Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask))|User(?:Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask)))|UserNo(?:MakeChanges(?:Bit|Mask)|SeeF(?:iles(?:Bit|Mask)|older(?:Bit|Mask))|tOwner(?:Bit|Mask)))|F(?:CB(?:FileLocked(?:Bit|Mask)|LargeFile(?:Bit|Mask)|Modified(?:Bit|Mask)|OwnClump(?:Bit|Mask)|Resource(?:Bit|Mask)|SharedWrite(?:Bit|Mask)|Write(?:Bit|Locked(?:Bit|Mask)|Mask))|lAttrib(?:CopyProt(?:Bit|Mask)|D(?:ataOpen(?:Bit|Mask)|ir(?:Bit|Mask))|FileOpen(?:Bit|Mask)|InShared(?:Bit|Mask)|Locked(?:Bit|Mask)|Mounted(?:Bit|Mask)|ResOpen(?:Bit|Mask)|SharePoint(?:Bit|Mask)))|VAtrb(?:DefaultVolume(?:Bit|Mask)|FilesOpen(?:Bit|Mask)|HardwareLocked(?:Bit|Mask)|SoftwareLocked(?:Bit|Mask))))|no(?:Group|User)|ownerPrivileges)|l(?:CloseMsg|D(?:o(?:HAutoscroll(?:Bit)?|VAutoscroll(?:Bit)?)|raw(?:Msg|ingModeOff(?:Bit)?))|ExtendDrag(?:Bit)?|HiliteMsg|InitMsg|No(?:Disjoint(?:Bit)?|Extend(?:Bit)?|NilHilite(?:Bit)?|Rect(?:Bit)?)|OnlyOne(?:Bit)?|UseSense(?:Bit)?|a(?:Dictionary(?:NotOpenedErr|TooManyErr|UnknownErr)|En(?:gineNotFoundErr|vironment(?:BusyErr|ExistErr|NotFoundErr))|FailAnalysisErr|InvalidPathErr|NoMoreMorphemeErr|Property(?:Err|IsReadOnlyErr|NotFoundErr|UnknownErr|ValueErr)|T(?:extOverFlowErr|ooSmallBufferErr)|ng(?:A(?:fri(?:caans|kaans)|lbanian|mharic|r(?:abic|menian)|ssamese|ymara|zerbaijan(?:Ar|Roman|i))|B(?:asque|e(?:lorussian|ngali)|reton|u(?:lgarian|rmese)|yelorussian)|C(?:atalan|h(?:ewa|inese)|roatian|zech)|D(?:anish|utch|zongkha)|E(?:nglish|s(?:peranto|tonian))|F(?:a(?:eroese|r(?:oese|si))|innish|lemish|rench)|G(?:al(?:ician|la)|e(?:orgian|rman)|ree(?:k(?:Ancient|Poly)?|nlandic)|u(?:arani|jarati))|H(?:ebrew|indi|ungarian)|I(?:celandic|n(?:donesian|uktitut)|rish(?:Gaelic(?:Script)?)?|talian)|Ja(?:panese|vaneseRom)|K(?:a(?:nnada|shmiri|zakh)|hmer|i(?:nyarwanda|rghiz)|orean|urdish)|L(?:a(?:o|pp(?:ish|onian)|t(?:in|vian))|ettish|ithuanian)|M(?:a(?:cedonian|l(?:a(?:gasy|y(?:Arabic|Roman|alam))|t(?:a|ese))|nxGaelic|rathi)|o(?:ldavian|ngolian(?:Cyr)?))|N(?:epali|orwegian|y(?:anja|norsk))|Or(?:iya|omo)|P(?:ashto|ersian|o(?:lish|rtug(?:ese|uese))|unjabi)|Quechua|R(?:omanian|u(?:anda|ndi|ssian))|S(?:a(?:amisk|mi|nskrit)|cottishGaelic|erbian|i(?:mpChinese|n(?:dhi|halese))|lov(?:ak|enian)|omali|panish|undaneseRom|w(?:ahili|edish))|T(?:a(?:galog|jiki|mil|tar)|elugu|hai|i(?:betan|grinya)|ongan|radChinese|urk(?:ish|men))|U(?:ighur|krainian|nspecified|rdu|zbek)|Vietnamese|Welsh|Y(?:iddish|ugoslavian))|pProtErr|rge(?:1BitMask|4BitData|8BitData)|stDskErr|unch(?:Allow24Bit|Continue|DontSwitch|InhibitDaemon|NoFileFlags|UseMinimum))|eft(?:OverChars|SingGuillemet)|imitReachedErr|o(?:c(?:alOnlyErr|kPortBits(?:Bad(?:PortErr|SurfaceErr)|SurfaceLostErr|W(?:indow(?:ClippedErr|MovedErr|ResizedErr)|rongGDeviceErr)))|ng(?:Da(?:te(?:Found)?|y)|Month|Week|Year)))|m(?:BarNFnd|CalcItemMsg|D(?:ownMask|rawMsg)|FulErr|PopUpMsg|SizeMsg|UpMask|a(?:c(?:Dev|ron)|p(?:C(?:hanged(?:Bit)?|ompact(?:Bit)?)|Read(?:Err|Only(?:Bit)?))|trixErr|x(?:Country|DateField|SizeToGrowTooSmall))|ct(?:AllItems|LastIDIndic)|dy|e(?:diaTypesDontMatch|m(?:A(?:ZErr|drErr)|BCErr|F(?:ragErr|ullErr)|LockedErr|P(?:CErr|urErr)|ROZ(?:Err(?:or)?|Warn)|SCErr|WZErr)|nu(?:I(?:nvalidErr|temNotFoundErr)|NotFoundErr|Pr(?:gErr|operty(?:Invalid(?:Err)?|NotFoundErr))|UsesSystemDefErr))|i(?:di(?:DupIDErr|InvalidCmdErr|ManagerAbsentOSErr|N(?:ameLenErr|o(?:C(?:lientErr|onErr)|PortErr))|TooMany(?:ConsErr|PortsErr)|VConnect(?:Err|Made|Rmvd)|WriteErr)|n(?:Country|LeadingZ|i(?:1BitMask|4BitData|8BitData)|ute(?:Field|Mask))|ssingRequiredParameterErr)|mInternalError|ntLdingZ|o(?:de(?:32BitCompatible|C(?:anBackground|ontrolPanel)|D(?:eskAccessory|isplayManagerAware|oesActivateOnFGSwitch)|Get(?:AppDiedMsg|FrontClicks)|HighLevelEventAware|L(?:aunchDontSwitch|iteral|ocalAndRemoteHLEvents)|MultiLaunch|N(?:eedSuspendResume|ormal)|OnlyBackground|Phonemes|Reserved|StationeryAware|T(?:ext|une)|UseTextEditServices)|nth(?:Field|Mask)|u(?:ntedFolderIconResource|se(?:Down|MovedMessage|Up))|v(?:ableDBoxProc|ieT(?:extNotFoundErr|oolboxUninitialized)))|pWorkFlag(?:CopyWorkBlock|Do(?:Completion|Work|ntBlock)|Get(?:IsRunning|ProcessorCount))|ultiplePublisherWrn|yd)|n(?:WIDTHHook|ame(?:FontTableTag|TypeErr)|bp(?:BuffOvr|ConfDiff|Duplicate|N(?:ISErr|o(?:Confirm|tFound)))|e(?:edClearScrapErr|gZcbFreeErr|twork(?:E(?:rr|vt)|Mask)|wLine(?:Bit|CharMask|Mask))|il(?:HandleErr|ScrapFlavorDataErr)|mTyp(?:Err|e)|o(?:AdrMkErr|BridgeErr|C(?:a(?:che(?:Bit|Mask)|lls)|o(?:decErr|nstraint))|D(?:MAErr|ata(?:Area|Handler)|e(?:fault(?:DataRef|UserErr)|viceForChannel)|riveErr|taMkErr)|ExportProcAvailableErr|G(?:lobalsErr|rowDocProc)|H(?:ardware(?:Err)?|elpForItem)|I(?:conDataAvailableErr|nformErr)|M(?:MUErr|PPErr|a(?:c(?:DskErr|hineNameErr)|rk|skFoundErr)|e(?:diaHandler|m(?:ForPictPlaybackErr|oryNodeFailedInitialize))|o(?:re(?:FolderDescErr|KeyColorsErr|RealTime)|vieFound))|NybErr|OutstandingHLE|P(?:a(?:steboardPromiseKeeperErr|thMappingErr)|ortErr|refAppErr)|Re(?:cordOfApp|lErr|quest|sponseErr)|S(?:crap(?:Err|PromiseKeeperErr)|e(?:curitySession|ndResp|ssionErr)|ou(?:ndTrackInMovieErr|rceTreeFoundErr)|u(?:chIconErr|itableDisplaysErr)|ynthFound)|T(?:humbnailFoundErr|oolboxNameErr|ranslationPathErr|ypeErr)|User(?:InteractionAllowed|NameErr|Re(?:cErr|fErr))|VideoTrackInMovieErr|n(?:DragOriginatorErr|GlyphID|MatchingEditState)|t(?:A(?:FileErr|QTVRMovieErr|RemountErr|llowedToSaveMovieErr|ppropriateForClassic)|BTree|E(?:nough(?:BufferSpace|D(?:ataErr|iskSpaceToGrab)|Hardware(?:Err)?|Memory(?:Err|ToGrab))|xact(?:MatrixErr|SizeErr))|HeldErr|I(?:mplementedMusicOSErr|nitErr)|L(?:eafAtomErr|o(?:ckedErr|ggedInErr))|OpenErr|PasteboardOwnerErr|RegisteredSectionErr|ThePublisherWrn|e(?:ChannelNotAllocatedOSErr|Icon)))|r(?:CallNotSupported|DataTruncatedErr|ExitedIteratorScope|I(?:nvalid(?:EntryIterationOp|NodeErr)|terationDone)|LockedErr|N(?:ameErr|ot(?:CreatedErr|EnoughMemoryErr|FoundErr|ModifiedErr|SlotDeviceErr))|OverrunErr|P(?:ath(?:BufferTooSmall|NotFound)|ower(?:Err|SwitchAbortErr)|ropertyAlreadyExists)|ResultCodeBase|T(?:ransactionAborted|ypeMismatchErr))|s(?:DrvErr|StackErr|vErr)|u(?:l(?:Dev|lEvent)|mberFor(?:matting(?:Bad(?:CurrencyPositionErr|FormatErr|NumberFormattingObjectErr|OptionsErr|TokenErr)|DelimiterMissingErr|EmptyFormatErr|LiteralMissingErr|NotA(?:DigitErr|NumberErr)|OverflowInDestinationErr|SpuriousCharErr|UnOrd(?:eredCurrencyRangeErr|redCurrencyRangeErr))|tmattingNotADigitErr)))|o(?:ffLinErr|gonek|k|p(?:WrErr|en(?:Err|FolderIconResource)|tionKey(?:Bit)?)|s(?:2FontTableTag|Evt(?:MessageMask)?|Mask)|ver(?:Dot|layDITL)|wnedFolderIconResource)|p(?:A(?:RADialIn|S(?:Da(?:teString|y(?:s)?)|Hours|It|M(?:e|inutes|onth)|P(?:arent|i|rint(?:Depth|Length))|Quote|Re(?:quiredImportItems|sult|turn)|S(?:econds|pace)|T(?:ab|ime(?:String)?|opLevelScript)|Week(?:day|s)|Year)|T(?:Machine|Type|Zone)|boutMacintosh|pp(?:Partition|l(?:eMenuItemsFolder|icationFile))|r(?:cAngle|ePrivilegesInherited))|B(?:ackground(?:Color|Pattern)|estType|ounds|uttonViewArrangement|y(?:CreationDateArrangement|KindArrangement|LabelArrangement|ModificationDateArrangement|NameArrangement|SizeArrangement))|C(?:a(?:llBackNumber|n(?:C(?:hangePassword|onnect)|DoProgramLinking)|pacity)|l(?:ass|ipboard)|o(?:lor(?:Table)?|m(?:ment|pletelyExpanded)|n(?:duit|t(?:ainer(?:Window)?|ent(?:Space|s)|rolPanelsFolder))|rnerCurve(?:Height|Width))|reationDate(?:Old)?)|D(?:CM(?:AccessMethod|C(?:lass|opyright)|L(?:isting|ocale)|Maintenance|Permission)|NS(?:Form)?|ashStyle|e(?:f(?:ault(?:ButtonViewIconSize|IconViewIconSize|ListViewIconSize|Type)|initionRect)|layBeforeSpringing|s(?:cription|k(?:AccessoryFile|top))|vice(?:Address|Type))|isk|ottedDecimal)|E(?:jectable|n(?:abled|dPoint|tireContents)|x(?:p(?:and(?:able|ed)|orted)|tensionsFolder))|F(?:TPKind|i(?:l(?:e(?:Creator|Share(?:On|StartingUp)|Type(?:Old)?)?|l(?:Color|Pattern))|nderPreferences)|o(?:lder(?:Old)?|nt(?:sFolder(?:PreAllegro)?)?|rmula)|reeSpace)|G(?:r(?:aphicObjects|id(?:Icons)?|oup(?:Privileges)?)|uestPrivileges)|H(?:as(?:CloseBox|ScriptingTerminology|TitleBar)|ost)|I(?:D|con(?:Bitmap|Size|ViewArrangement)|n(?:dex|fo(?:Panel|Window)|herits|sertionLoc|ternetLocation)|s(?:Collapsed|F(?:loating|rontProcess)|Locked(?:Old)?|Mod(?:al|ified)|Owner|P(?:opup|ulledOpen)|Resizable|S(?:criptable|elected|ta(?:rtup|tioneryPad))|Zoom(?:able|ed(?:Full)?))|temNumber)|Justification|K(?:e(?:epArranged(?:By)?|y(?:Kind|strokeKey))|ind)|L(?:a(?:bel(?:1|2|3|4|5|6|7|Index)|ngCode|rge(?:Button|stFreeBlock))|ength|i(?:neArrow|stViewIconSize)|ocal(?:AndRemoteEvents)?)|M(?:akeChanges|enuID|inAppPartition|o(?:difi(?:cationDate(?:Old)?|ers)|unted))|N(?:ame|e(?:twork|wElementLoc)|o(?:Arrangement|de))|O(?:bject|riginalItem|wner(?:Privileges)?)|P(?:a(?:rtitionSpaceUsed|th)|en(?:Color|Pattern|Width)|hysicalSize|ixelDepth|o(?:int(?:List|Size)|rt|sition)|r(?:e(?:ferences(?:Folder|Window)|viousView)|o(?:ductVersion|gramLinkingOn|perties|t(?:ection|ocol))))|R(?:e(?:st|verse)|otation)|S(?:CSI(?:Bus|LUN)|c(?:ale|heme|ript(?:Code|Tag)?)|e(?:eF(?:iles|olders)|lect(?:ed|ion))|h(?:ar(?:ableContainer|ing(?:Protection|Window)?)|o(?:rtCuts|uldCallBack|w(?:C(?:omment|reationDate)|D(?:ate|iskInfo)|FolderSize|Kind|Label|ModificationDate|Size|Version))|utdownFolder)|ize|mall(?:Button|Icon)|napToGridArrangement|o(?:cket|rtDirection|und)|pringOpenFolders|ta(?:ggerIcons|rt(?:Angle|Point|ingUp|up(?:Disk|ItemsFolder)))|uggestedAppPartition|ystemFolder)|T(?:e(?:mporaryFolder|xt(?:Color|Encoding|Font|ItemDelimiters|PointSize|Styles))|ra(?:ns(?:ferMode|lation)|sh))|U(?:RL|niformStyles|pdateOn|se(?:RelativeDate|ShortMenus|WideGrid|r(?:Name|Password|Selection)))|V(?:ersion|i(?:ew(?:Font(?:Size)?|Preferences)?|sible))|W(?:arnOnEmpty|indow)|a(?:ramErr|steDev|th(?:NotVerifiedErr|TooLongErr))|er(?:Thousand|mErr)|i(?:c(?:Item|ker(?:CantLive|ResourceError)|t(?:Info(?:IDErr|Ver(?:bErr|sionErr))|ureDataErr))|xMapTooDeepErr)|l(?:a(?:inDBox|tform(?:68k|AIXppc|I(?:A32NativeEntryPoint|RIXmips|nterpreted)|Linux(?:intel|ppc)|MacOSx86|NeXT(?:68k|Intel|ppc|sparc)|PowerPC(?:64NativeEntryPoint|NativeEntryPoint)?|SunOS(?:intel|sparc)|Win32|X86_64NativeEntryPoint))|easeCache(?:Bit|Mask))|m(?:BusyErr|Field|Mask|Re(?:cv(?:EndErr|StartErr)|plyTOErr)|Send(?:EndErr|StartErr))|o(?:pup(?:FixedWidth|MenuProc|Title(?:Bold|C(?:enterJust|ondense)|Extend|Italic|LeftJust|NoStyle|Outline|RightJust|Shadow|Underline)|Use(?:AddResMenu|WFont)|VariableWidth)|rt(?:ClosedErr|InUse|N(?:ameExistsErr|ot(?:Cf|Pwr)))|sErr)|r(?:InitErr|WrErr|eferencesFolderIconResource|i(?:nt(?:MonitorFolderIconResource|erStatusOpCodeNotSupportedErr)|vateFolderIconResource)|o(?:c(?:NotFound|essStateIncorrectErr)|gressProcAborted|pertyNotSupportedByNodeErr|tocolErr))|ushButProc)|q(?:Err|fcbNot(?:CreatedErr|FoundErr)|t(?:ActionNotHandledErr|NetworkAlreadyAllocatedErr|ParamErr|XML(?:ApplicationErr|ParseErr)|ml(?:Dll(?:EntryNotFoundErr|LoadErr)|Uninitialized)|s(?:AddressBusyErr|Bad(?:DataErr|S(?:electorErr|tateErr))|ConnectionFailedErr|T(?:imeoutErr|ooMuchDataErr)|Un(?:knownValueErr|supported(?:DataTypeErr|FeatureErr|RateErr)))|vr(?:LibraryLoadErr|Uninitialized))|ueueFull)|r(?:AliasType|ad(?:Ctrl|ioButProc)|c(?:DB(?:AsyncNotSupp|B(?:ad(?:AsyncPB|DDEV|Sess(?:ID|Num)|Type)|reak)|E(?:rror|xec)|N(?:oHandler|ull)|PackNotInited|Value|WrongVersion)|vrErr)|dVerify(?:Bit|Mask)?|e(?:ad(?:Err|QErr|Reference)|c(?:NotFnd|ordDataTooBigErr)|gisterComponent(?:A(?:fterExisting|liasesOnly)|Global|NoDuplicates)|q(?:Aborted|Failed|uiredFlagsDontMatch)|s(?:1Field|2Field|3Field|AttrErr|C(?:hanged(?:Bit)?|trl)|FNotFound|Locked(?:Bit)?|NotFound|P(?:r(?:eload(?:Bit)?|o(?:blem|tected(?:Bit)?))|urgeable(?:Bit)?)|Sys(?:Heap(?:Bit)?|RefBit)|ourceInMemory|umeFlag)|tryComponentRegistrationErr)|fNumErr|gn(?:OverflowErr|TooBigErr(?:or)?)|i(?:ght(?:ControlKey(?:Bit)?|OptionKey(?:Bit)?|S(?:hiftKey(?:Bit)?|ingGuillemet))|ngMark)|mvRe(?:fFailed|sFailed)|o(?:man(?:AppFond|Flags|SysFond)|utingNotFoundErr))|s(?:IQType|am(?:eFileErr|plesAlreadyInMediaErr)|c(?:TypeNotFoundErr|r(?:ap(?:Flavor(?:FlagsMismatchErr|NotFoundErr|SizeMismatchErr)|PromiseNotKeptErr)|ipt(?:CurLang|DefLang)|ollBarProc))|dm(?:InitErr|JTInitErr|P(?:RAMInitErr|riInitErr)|SRTInitErr)|e(?:NoDB|c(?:LeadingZ|ond(?:Field|Mask)|tNFErr)|ekErr|lectorNotSupportedByNodeErr|pNot(?:Consistent|IntlSep)|qGrabInfoNotAvailable|ss(?:ClosedErr|TableErr|ion(?:Has(?:GraphicAccess|TTY)|IsR(?:emote|oot)|KeepCurrentBootstrap))|ttingNotSupportedByNodeErr)|h(?:aredFolderIconResource|iftKey(?:Bit)?|ortDate|utDownAlert)|i(?:Bad(?:DeviceName|RefNum|SoundInDevice)|DeviceBusyErr|HardDriveTooSlow|In(?:it(?:S(?:DTblErr|PTblErr)|VBLQsErr)|putDeviceErr|valid(?:Compression|Sample(?:Rate|Size)))|No(?:BufferSpecified|SoundInHardware)|Unknown(?:InfoType|Quality)|VBRCompressionNotSupported|ze(?:Bit|of_sfnt(?:CMap(?:E(?:ncoding|xtendedSubHeader)|Header|SubHeader)|D(?:escriptorHeader|irectory)|Instance|Name(?:Header|Record)|Variation(?:Axis|Header))))|ktClosedErr|l(?:eepQType|otNumErr|pQType)|m(?:A(?:llScripts|mharic|r(?:abic|menian))|B(?:LFieldBad|ad(?:BoardId|RefId|Script|Verb|s(?:List|PtrErr))|engali|lkMoveErr|u(?:rmese|sErrTO)|yteLanesErr)|C(?:PUErr|RCFail|entralEuroRoman|h(?:ar(?:1byte|2byte|Ascii|B(?:idirect|opomofo)|ContextualLR|E(?:uro|xtAscii)|FIS(?:G(?:ana|reek)|Ideo|Kana|Russian)|GanaKana|H(?:angul|iragana|orizontal)|Ideographic|Jamo|Katakana|L(?:eft|ower)|NonContextualLR|Punct|Right|TwoByte(?:Greek|Russian)|Upper|Vertical)|inese)|kStatusErr|odeRevErr|urrentScript|yrillic)|D(?:evanagari|is(?:DrvrNamErr|abledSlot|posePErr))|E(?:astEurRoman|mptySlot|thiopic|xtArabic)|F(?:HBl(?:kDispErr|ockRdErr)|ISClass(?:Lvl(?:1|2)|User)|irstByte|o(?:nd(?:End|Start)|rmatErr))|G(?:e(?:ez|orgian|t(?:DrvrNamErr|PRErr))|reek|u(?:jarati|rmukhi))|Hebrew|I(?:deographic(?:Level(?:1|2)|User)|nit(?:StatVErr|TblVErr))|Ja(?:mo(?:Bog(?:Jaeum|Moeum)|Jaeum|Moeum)|panese)|K(?:CHRCache|an(?:a(?:HardOK|S(?:mall|oftOK))|nada)|ey(?:DisableKybd(?:Switch|s)|EnableKybds|ForceKeyScript(?:Bit|Mask)|Next(?:InputMethod|Kybd|Script)|Roman|S(?:cript|etDir(?:LeftRight|RightLeft)|wap(?:InputMethod|Kybd|Script)|ysScript)|Toggle(?:Direction|Inline))|hmer|lingon|orean)|La(?:o(?:tian)?|stByte)|M(?:a(?:layalam|sk(?:A(?:ll|scii(?:1|2)?)|Bopomofo2|Gana2|Hangul2|Jamo2|Kana(?:1|2)|Native))|iddleByte|ongolian)|N(?:ewPErr|ilsBlockErr|o(?:Board(?:Id|SRsrc)|Dir|GoodOpens|JmpTbl|MoresRsrcs|sInfoArray|tInstalled)|umberPartsTable)|O(?:ffsetErr|riya)|P(?:RAMInitErr|riInitErr|unct(?:Blank|Graphic|N(?:ormal|umber)|Repeat|Symbol))|R(?:Symbol|e(?:cNotFnd|gionCode|s(?:erved(?:Err|Slot)|rvErr)|visionErr)|oman|ussian)|S(?:DMInitErr|RT(?:InitErr|OvrFlErr)|elOOBErr|i(?:mpChinese|n(?:dhi|gleByte|halese))|l(?:avic|otOOBErr)|ys(?:Script|temScript))|T(?:amil|elugu|hai|ibetan|ra(?:dChinese|ns(?:Ascii(?:1|2)?|Bopomofo2|Case|Gana2|Hangul(?:2|Format)|Jamo2|Kana(?:1|2)|Lower|Native|Pre(?:DoubleByting|LowerCasing)|RuleBaseFormat|System|Upper)))|U(?:n(?:ExBusErr|TokenTable|i(?:codeScript|nterp))|prHalfCharSet)|Vietnamese|W(?:hiteSpaceList|ord(?:SelectTable|WrapTable))|all(?:1BitMask|4BitData|8BitData|DateBit)|c(?:ClassMask|DoubleMask|OrientationMask|R(?:eserved|ightMask)|TypeMask|UpperMask)|f(?:D(?:isableKeyScriptSync(?:Mask)?|ualCaret)|NameTagEnab|ShowIcon|UseAssocFontInfo)|s(?:GetDrvrErr|PointerNil|f(?:AutoInit|B0Digits|Context|Forms|IntellCP|Ligatures|N(?:atCase|oForceFont)|Reverse|S(?:ingByte|ynchUnstyledTE)|UnivExt)))|o(?:C(?:haracterMode|ommandDelimiter|urrent(?:A5|Voice))|Error(?:CallBack|s)|InputMode|NumberMode|OutputTo(?:AudioDevice|ExtAudioFile|FileWithCFURL)|P(?:honeme(?:CallBack|Options|Symbols)|itch(?:Base|Mod))|R(?:ate|e(?:centSync|fCon|set))|S(?:oundOutput|peechDoneCallBack|tatus|yn(?:cCallBack|th(?:Extension|Type)))|TextDoneCallBack|Vo(?:ice(?:Description|File)|lume)|WordCallBack|rts(?:After|Before|Equal)|u(?:ndSupportNotAvailableErr|rceNotFoundErr))|pdAdjErr|rcCopy|t(?:a(?:leEditState|rtupFolderIconResource|t(?:Text|usErr))|opIcon|r(?:UserBreak|eamingNodeNotReadyErr|ingOverflow))|u(?:p(?:Day|Month|Week|Year)|spendResumeMessage)|v(?:All(?:1BitData|4BitData|8BitData|AvailableData|LargeData|MiniData|SmallData)|Disabled|Large(?:1Bit|4Bit|8Bit)|Mini(?:1Bit|4Bit|8Bit)|Small(?:1Bit|4Bit|8Bit)|TempDisable)|y(?:nth(?:NotReady|OpenFailed|esizer(?:NotRespondingOSErr|OSErr))|stem(?:CurLang|DefLang|FolderIconResource)))|t(?:aDst(?:DocNeedsResourceFork|IsAppTranslation)|e(?:Bit(?:Clear|Set|Test)|C(?:aret|enter)|Draw|F(?:AutoScroll|I(?:dleWithEventLoopTimer|nlineInput(?:AutoScroll)?)|OutlineHilite|TextBuffering|Use(?:InlineInput|TextServices|WhiteBackground)|ind|lush(?:Default|Left|Right)|orceLeft|rom(?:Find|Recal))|Highlight|Just(?:Center|Left|Right)|ScrapSizeErr|Word(?:Drag|Select)|l(?:A(?:PattNotSupp|lreadyOpen|utoAnsNotOn)|Bad(?:APattErr|BearerType|C(?:AErr|odeResource)|D(?:N(?:DType|Err|Type)|isplayMode)|F(?:eatureID|unction|wdType)|H(?:TypeErr|andErr)|In(?:dex|t(?:Ext|ercomID))|LevelErr|P(?:a(?:geID|rkID)|ickupGroupID|roc(?:Err|ID))|Rate|S(?:WErr|ampleRate|elect|tateErr)|TermErr|VTypeErr)|C(?:A(?:Not(?:Acceptable|Deflectable|Rejectable)|Unavail)|BErr|onf(?:Err|LimitE(?:rr|xceeded)|NoLimit|Rej))|D(?:N(?:DTypeNotSupp|TypeNotSupp)|e(?:tAlreadyOn|viceNotFound)|isplayModeNotSupp)|F(?:eat(?:Active|Not(?:Avail|Su(?:b|pp)))|wdTypeNotSupp)|GenericError|HTypeNotSupp|In(?:dexNotSupp|itFailed|tExtNotSupp)|No(?:C(?:allbackRef|ommFolder)|Err|MemErr|OpenErr|SuchTool|Tools|tEnoughdspBW)|PBErr|St(?:ateNotSupp|illNeeded)|T(?:ermNotOpen|ransfer(?:Err|Rej))|UnknownErr|V(?:TypeNotSupp|alidateFailed))|xt(?:MenuProc|Parser(?:Bad(?:Par(?:amErr|serObjectErr)|T(?:ext(?:EncodingErr|LanguageErr)|okenValueErr))|No(?:MoreT(?:extErr|okensErr)|SuchTokenFoundErr)|ObjectNotFoundErr|ParamErr)))|h(?:eme(?:Bad(?:CursorIndexErr|TextColorErr)|HasNoAccentsErr|InvalidBrushErr|MonitorDepthNotSupportedErr|NoAppropriateBrushErr|Process(?:NotRegisteredErr|RegisteredErr)|ScriptFontNotFoundErr)|read(?:BadAppContextErr|NotFoundErr|ProtocolErr|TooManyReqsErr))|i(?:lde|me(?:Cycle(?:12|24|Zero)|NotIn(?:Media|Track|ViewErr)))|k0BadErr|ls_(?:ciphersuite_(?:AES_(?:128_GCM_SHA256|256_GCM_SHA384)|CHACHA20_POLY1305_SHA256|ECDHE_(?:ECDSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256))|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|group_(?:ats(?:_compatibility)?|compatibility|default|legacy))|protocol_version_(?:DTLSv1(?:0|2)|TLSv1(?:0|1|2|3)))|m(?:foErr|wdoErr)|o(?:g(?:Char(?:12HourBit|ZCycleBit)|Delta12HourBit|gle(?:B(?:ad(?:Char|Delta|Field|Num)|it)|Err(?:3|4|5)|O(?:K|utOfRange)|Un(?:defined|known)))|k(?:DecPoint|E(?:Minus|Plus|scape)|Le(?:ad(?:Placer|er)|ftQuote)|M(?:axSymbols|inusSign)|NonLeader|P(?:ercent|lusSign)|R(?:eserved|ightQuote)|Separator|Thousands|ZeroLead|en(?:1Quote|2(?:Equal|Quote)|A(?:l(?:pha|t(?:Num|Real))|mpersand|sterisk|tSign)|Ba(?:ckSlash|r)|C(?:a(?:pPi|r(?:at|et))|enterDot|o(?:lon(?:Equal)?|mma))|D(?:ivide|ollar)|E(?:llipsis|mpty|qual|rr|scape|xclam(?:Equal)?)|Fraction|Great(?:Equal(?:1|2))?|Hash|In(?:finity|t(?:egral|l(?:Currency)?))|L(?:e(?:ft(?:1Quote|2Quote|Bracket|C(?:omment|urly)|Enclose|Lit|Paren|SingGuillemet)|ss(?:Equal(?:1|2)|Great)?)|iteral)|Mi(?:cro|nus)|N(?:ewLine|il|o(?:BreakSpace|tEqual)|umeric)|O(?:K|verflow)|P(?:er(?:Thousand|cent|iod)|i|lus(?:Minus)?)|Question|R(?:e(?:alNum|serve(?:1|2))|ight(?:1Quote|2Quote|Bracket|C(?:omment|urly)|Enclose|Lit|Paren|SingGuillemet)|oot)|S(?:emicolon|igma|lash)|Tild(?:a|e)|Un(?:derline|known)|White))|oMany(?:Reqs|S(?:eps|kts)))|ra(?:ck(?:IDNotFound|NotInMovie)|shIconResource)|s(?:N(?:extSelectMode|ormalSelectMode)|PreviousSelectMode|m(?:AlreadyRegisteredErr|C(?:ant(?:ChangeForcedClassStateErr|OpenComponentErr)|omponent(?:AlreadyOpenErr|NoErr|Property(?:NotFoundErr|UnsupportedErr)))|D(?:efaultIsNotInputMethodErr|oc(?:NotActiveErr|Property(?:BufferTooSmallErr|NotFoundErr)|umentOpenErr))|In(?:putM(?:ethod(?:IsOldErr|NotFoundErr)|odeChangeFailedErr)|valid(?:Context|DocIDErr))|N(?:everRegisteredErr|o(?:Handler|MoreTokens|OpenTSErr|Stem|tAnAppErr))|ScriptHasNoIMErr|T(?:S(?:HasNoMenuErr|MDocBusyErr|NotOpenErr)|extServiceNotFoundErr)|U(?:n(?:knownErr|sup(?:ScriptLanguageErr|portedTypeErr))|seInputWindowErr)))|t(?:Disabled|Label(?:1|2|3|4|5|6|7)|None|O(?:ffline|pen)|Selected(?:Disabled|O(?:ffline|pen))?)|uneP(?:arseOSErr|layerFullOSErr)|woSideErr|ype(?:128BitFloatingPoint|32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|A(?:E(?:Homograph(?:Accent|DicInfo|Weight)|List|Morpheme(?:PartOfSpeechCode|TextRange)|Record|T(?:E|ext)|UT)|SStorage|TS(?:FontRef|U(?:FontID|Size))|bsoluteOrdinal|lias|pp(?:Parameters|l(?:Signature|e(?:Event|Script)|ication(?:BundleID|URL)))|rc|uditToken)|B(?:est|oo(?:kmarkData|lean)|yte(?:Count|Offset))|C(?:F(?:A(?:bsoluteTime|rrayRef|ttributedStringRef)|BooleanRef|DictionaryRef|Index|Mutable(?:A(?:rrayRef|ttributedStringRef)|DictionaryRef|StringRef)|NumberRef|Range|StringRef|TypeRef)|G(?:ContextRef|Display(?:ChangeFlags|ID)|Float(?:72DPIGlobal|ScreenPixel)?|ImageRef)|String|T(?:Font(?:DescriptorRef|Ref)|GlyphInfoRef)|e(?:ll|ntimeters)|har|l(?:assInfo|ickActivationResult)|o(?:l(?:lection|orTable|umn)|mp(?:Descriptor|onentInstance)|n(?:ceptualTime|trol(?:ActionUPP|FrameMetrics|PartCode|Ref)))|u(?:bic(?:Centimeter|Feet|Inches|Meters|Yards)|rrentContainer))|D(?:CMFi(?:eldAttributes|ndMethod)|a(?:shStyle|ta)|e(?:cimalStruct|grees(?:C|F|K))|ra(?:gRef|wingArea))|E(?:PS|lemInfo|n(?:codedString|umerat(?:ed|ion))|vent(?:HotKeyID|Info|Re(?:cord|f)|Target(?:Options|Ref)))|F(?:MFont(?:Family|S(?:ize|tyle))|S(?:Ref|VolumeRefNum)|alse|eet|i(?:leURL|nderWindow|xed(?:Point|Rectangle)?)|ontColor)|G(?:DHandle|IF|WorldPtr|allons|lyph(?:InfoArray|Selector)|r(?:a(?:fPtr|ms|phic(?:Line|Text))|oupedGraphic))|HI(?:Command|Menu|ObjectRef|Point(?:72DPIGlobal|ScreenPixel)?|Rect(?:72DPIGlobal|ScreenPixel)?|S(?:hapeRef|ize(?:72DPIGlobal|ScreenPixel)?)|Toolbar(?:Display(?:Mode|Size)|ItemRef|Ref)|ViewTrackingAreaRef|Window)|I(?:EEE(?:32BitFloatingPoint|64BitFloatingPoint)|SO8601DateTime|con(?:AndMask|Family)|n(?:ches|d(?:exDescriptor|icatorDragConstraint)|sertionLoc|tl(?:Text|WritingCode)))|JPEG|K(?:e(?:rnelProcessID|yword)|ilo(?:grams|meters))|L(?:A(?:Homograph|Morpheme(?:Bundle|Path)?)|iters|o(?:gicalDescriptor|ng(?:DateTime|Fixed(?:Point|Rectangle)?|Point|Rectangle)|wLevelEventRecord))|M(?:ach(?:Port|ineLoc)|e(?:nu(?:Command|Direction|EventOptions|ItemIndex|Ref|TrackingMode)|ters)|iles|o(?:dalClickResult|use(?:Button|TrackingRef|WheelAxis)))|Null|O(?:S(?:A(?:DialectInfo|ErrorRange|GenericStorage)|LTokenList|Status)|bject(?:BeingExamined|Specifier)|ffsetArray|unces|val)|P(?:String|a(?:ramInfo|steboardRef)|i(?:ct|x(?:MapMinus|elMap))|o(?:lygon|unds)|ro(?:cessSerialNumber|p(?:Info|erty))|tr)|Q(?:D(?:Point|R(?:e(?:ctangle|gion)|gnHandle))|uarts)|R(?:GB(?:16|96|Color)|angeDescriptor|e(?:ctangle|fCon|lative(?:Descriptor|Time)|plyPortAttr)|o(?:tation|undedRectangle|w))|S(?:Int(?:16|32|64)|R(?:Recognizer|SpeechResult)|c(?:r(?:ap(?:Ref|Styles)|ipt)|szResource)|ec(?:IdentityRef|tionH)|ignedByte(?:Count|Offset)|mall(?:32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|IconAndMask)|ound|quare(?:Feet|Kilometers|M(?:eters|iles)|Yards)|tyled(?:Text|UnicodeText)|uiteInfo)|T(?:IFF|able(?:tP(?:oint(?:Rec|erRec)|roximityRec))?|ext(?:Range(?:Array)?|Styles)?|hemeMenu(?:ItemType|State|Type)|oken|rue|ype)|U(?:Int(?:16|32|64)|TF(?:16ExternalRepresentation|8Text)|nicodeText|serRecordFields)|V(?:ersion|oidPtr)|W(?:hose(?:Descriptor|Range)|i(?:ldCard|ndow(?:DefPartCode|Modality|PartCode|Re(?:f|gionCode)|Transition(?:Action|Effect))))|Yards))|u(?:n(?:doDev|i(?:code(?:BufErr|C(?:h(?:arErr|ecksumErr)|ontextualErr)|DirectionErr|ElementErr|FallbacksErr|No(?:TableErr|tFoundErr)|PartConvertErr|T(?:ableFormatErr|extEncodingDataErr)|VariantErr)|mpErr|t(?:EmptyErr|TblFullErr))|known(?:FormatErr|InsertModeErr)|resolvedComponentDLLErr|supported(?:AuxiliaryImportData|ForPlatformErr|OSErr|ProcessorErr))|p(?:d(?:PixMemErr|ate(?:Dev|Evt|Mask))|p(?:C(?:allComponent(?:C(?:anDoProcInfo|loseProcInfo)|Get(?:MPWorkFunctionProcInfo|PublicResourceProcInfo)|OpenProcInfo|RegisterProcInfo|TargetProcInfo|UnregisterProcInfo|VersionProcInfo)|omponent(?:FunctionImplementedProcInfo|SetTargetProcInfo))|GetComponentVersionProcInfo))|rlDataH(?:FTP(?:Bad(?:NameListErr|PasswordErr|UserErr)|DataConnectionErr|FilenameErr|N(?:eedPasswordErr|o(?:DirectoryErr|NetDriverErr|PasswordErr))|P(?:ermissionsErr|rotocolErr)|QuotaErr|S(?:erver(?:DisconnectedErr|Err)|hutdownErr)|URLErr)|HTTP(?:NoNetDriverErr|ProtocolErr|RedirectErr|URLErr))|se(?:A(?:Talk|sync)|ExtClk|Free|MIDI|r(?:Break|CanceledErr|DataItemNotFound|Item|Kind|RejectErr)))|v(?:AxisOnly|LckdErr|Typ(?:Err|e)|a(?:lid(?:DateFields|InstancesExist)|riationFontTableTag)|er(?:A(?:frikaans|lternateGr|r(?:abi(?:a|c)|menia(?:n)?)|ustr(?:alia|ia(?:German)?))|B(?:e(?:l(?:arus|giumLux(?:Point)?)|ngali)|hutan|r(?:azil|eton|it(?:ain|tany))|ulgaria|yeloRussian)|C(?:a(?:nada(?:Comma|Point)|talonia)|hina|roatia|yprus|zech)|Denmark|E(?:astAsiaGeneric|ngCanada|rr|s(?:peranto|tonia))|F(?:a(?:eroeIsl|r(?:EastGeneric|oeIsl))|inland|lemish(?:Point)?|r(?:Belgium(?:Lux)?|Canada|Swiss|ance|enchUniversal))|G(?:e(?:nericFE|orgia(?:n)?|rman(?:Reformed|y))|r(?:Swiss|ee(?:ce(?:Alt|Poly)?|kAncient|nland))|ujarati)|Hungary|I(?:celand|n(?:dia(?:Hindi|Urdu)?|ternational)|r(?:an|eland(?:English)?|ishGaelicScript)|srael|tal(?:ianSwiss|y))|Japan|Korea|L(?:a(?:pland|tvia)|ithuania)|M(?:a(?:cedonia(?:n)?|gyar|lta|nxGaelic|rathi)|ultilingual)|N(?:e(?:pal|therlands(?:Comma)?)|orway|unavut|ynorsk)|P(?:akistan(?:Urdu)?|o(?:land|rtugal)|unjabi)|R(?:omania|u(?:mania|ssia))|S(?:ami|c(?:ottishGaelic|riptGeneric)|erbia(?:n)?|ingapore|lov(?:ak|enia(?:n)?)|p(?:LatinAmerica|ain)|weden)|T(?:aiwan|hailand|ibet(?:an)?|onga|urk(?:ey|ishModified))|U(?:S|kra(?:ine|nia)|nspecified|zbek)|Vietnam|W(?:ales|elsh)|Yugo(?:Croatian|slavia)|variant(?:Denmark|Norway|Portugal))|ideoOutputInUseErr|m(?:AddressNotInFileViewErr|B(?:adDriver|usyBackingFileErr)|FileViewAccessErr|Invalid(?:BackingFileIDErr|FileViewIDErr|OwningProcessErr)|KernelMMUInitErr|M(?:appingPrivilegesErr|emLckdErr|orePhysicalThanVirtualErr)|No(?:More(?:BackingFilesErr|FileViewsErr)|VectorErr)|OffErr)|o(?:iceNotFound|l(?:GoneErr|Mount(?:Changed(?:Bit|Mask)|ExtendedFlags(?:Bit|Mask)|FSReservedMask|Interact(?:Bit|Mask)|NoLoginMsgFlag(?:Bit|Mask)|SysReservedMask)|O(?:ffLinErr|nLinErr)|VMBusyErr)))|w(?:C(?:alcRgns|ontentColor)|D(?:ispose|raw(?:GIcon)?)|FrameColor|Grow|Hi(?:liteColor|t)|In(?:Co(?:llapseBox|ntent)|Drag|G(?:oAway|row)|ProxyIcon|Structure|ToolbarButton|Zoom(?:In|Out))|N(?:ew|oHit)|PrErr|T(?:extColor|itleBarColor)|ack(?:Bad(?:FileErr|MetaDataErr)|ForkNotFoundErr)|eekOfYear(?:Field|Mask)|fFileNotFound|indow(?:A(?:ppModalStateAlreadyExistsErr|ttribute(?:ImmutableErr|sConflictErr))|GroupInvalidErr|ManagerInternalErr|NoAppModalStateErr|WrongStateErr)|r(?:PermErr|Underrun|gVolTypErr|it(?:Err|eReference|ingPastEnd)|ongApplicationPlatform))|y(?:dm|ear(?:Field|Mask)|md)|z(?:eroCycle|oom(?:DocProc|NoGrow)))\\b", + "name": "support.constant.c" + }, { "match": "\\bkCFNumberFormatter(?:Currency(?:AccountingStyle|ISOCodeStyle|PluralStyle)|OrdinalStyle)\\b", "name": "support.constant.cf.10.11.c" @@ -57,6 +209,14 @@ "match": "\\bkCFISO8601DateFormatWith(?:ColonSeparatorInTime(?:Zone)?|Da(?:shSeparatorInDate|y)|Full(?:Date|Time)|InternetDateTime|Month|SpaceBetweenDateAndTime|Time(?:Zone)?|WeekOfYear|Year)\\b", "name": "support.constant.cf.10.12.c" }, + { + "match": "\\bkCFISO8601DateFormatWithFractionalSeconds\\b", + "name": "support.constant.cf.10.13.c" + }, + { + "match": "\\bkCFURLEnumeratorGenerateRelativePathURLs\\b", + "name": "support.constant.cf.10.15.c" + }, { "match": "\\bkCFFileSecurityClear(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)\\b", "name": "support.constant.cf.10.8.c" @@ -65,6 +225,10 @@ "match": "\\b(?:CF(?:ByteOrder(?:BigEndian|LittleEndian|Unknown)|NotificationSuspensionBehavior(?:Coalesce|D(?:eliverImmediately|rop)|Hold))|kCF(?:B(?:ookmarkResolutionWithout(?:MountingMask|UIMask)|undleExecutableArchitecture(?:I386|PPC(?:64)?|X86_64))|C(?:alendar(?:ComponentsWrap|Unit(?:Day|Era|Hour|M(?:inute|onth)|Quarter|Second|Week(?:Of(?:Month|Year)|day(?:Ordinal)?)|Year(?:ForWeekOfYear)?))|haracterSet(?:AlphaNumeric|C(?:apitalizedLetter|ontrol)|Dec(?:imalDigit|omposable)|Illegal|L(?:etter|owercaseLetter)|N(?:ewline|onBase)|Punctuation|Symbol|UppercaseLetter|Whitespace(?:AndNewline)?)|ompare(?:Anchored|Backwards|CaseInsensitive|DiacriticInsensitive|EqualTo|ForcedOrdering|GreaterThan|L(?:essThan|ocalized)|N(?:onliteral|umerically)|WidthInsensitive))|Dat(?:aSearch(?:Anchored|Backwards)|eFormatter(?:FullStyle|LongStyle|MediumStyle|NoStyle|ShortStyle))|FileDescriptor(?:ReadCallBack|WriteCallBack)|LocaleLanguageDirection(?:BottomToTop|LeftToRight|RightToLeft|TopToBottom|Unknown)|MessagePort(?:BecameInvalidError|IsInvalid|ReceiveTimeout|S(?:endTimeout|uccess)|TransportError)|N(?:otification(?:DeliverImmediately|PostToAllSessions)|umber(?:C(?:FIndexType|GFloatType|harType)|DoubleType|F(?:loat(?:32Type|64Type|Type)|ormatter(?:CurrencyStyle|DecimalStyle|NoStyle|P(?:a(?:d(?:After(?:Prefix|Suffix)|Before(?:Prefix|Suffix))|rseIntegersOnly)|ercentStyle)|Round(?:Ceiling|Down|Floor|Half(?:Down|Even|Up)|Up)|S(?:cientificStyle|pellOutStyle)))|IntType|Long(?:LongType|Type)|MaxType|NSIntegerType|S(?:Int(?:16Type|32Type|64Type|8Type)|hortType)))|PropertyList(?:BinaryFormat_v1_0|Immutable|MutableContainers(?:AndLeaves)?|OpenStepFormat|Read(?:CorruptError|StreamError|UnknownVersionError)|WriteStreamError|XMLFormat_v1_0)|RunLoop(?:A(?:fterWaiting|llActivities)|Before(?:Sources|Timers|Waiting)|E(?:ntry|xit)|Run(?:Finished|HandledSource|Stopped|TimedOut))|S(?:ocket(?:A(?:cceptCallBack|utomaticallyReenable(?:AcceptCallBack|DataCallBack|ReadCallBack|WriteCallBack))|C(?:loseOnInvalidate|onnectCallBack)|DataCallBack|Error|LeaveErrors|NoCallBack|ReadCallBack|Success|Timeout|WriteCallBack)|tr(?:eam(?:E(?:rrorDomain(?:Custom|MacOSStatus|POSIX)|vent(?:CanAcceptBytes|E(?:ndEncountered|rrorOccurred)|HasBytesAvailable|None|OpenCompleted))|Status(?:AtEnd|Closed|Error|NotOpen|Open(?:ing)?|Reading|Writing))|ing(?:Encoding(?:A(?:NSEL|SCII)|Big5(?:_(?:E|HKSCS_1999))?|CNS_11643_92_P(?:1|2|3)|DOS(?:Arabic|BalticRim|C(?:anadianFrench|hinese(?:Simplif|Trad)|yrillic)|Greek(?:1|2)?|Hebrew|Icelandic|Japanese|Korean|Latin(?:1|2|US)|Nordic|Portuguese|Russian|T(?:hai|urkish))|E(?:BCDIC_(?:CP037|US)|UC_(?:CN|JP|KR|TW))|GB(?:K_95|_(?:18030_2000|2312_80))|HZ_GB_2312|ISO(?:Latin(?:1(?:0)?|2|3|4|5|6|7|8|9|Arabic|Cyrillic|Greek|Hebrew|Thai)|_2022_(?:CN(?:_EXT)?|JP(?:_(?:1|2|3))?|KR))|JIS_(?:C6226_78|X02(?:0(?:1_76|8_(?:83|90))|12_90))|K(?:OI8_(?:R|U)|SC_5601_(?:87|92_Johab))|Mac(?:Ar(?:abic|menian)|B(?:engali|urmese)|C(?:e(?:ltic|ntralEurRoman)|hinese(?:Simp|Trad)|roatian|yrillic)|D(?:evanagari|ingbats)|E(?:thiopic|xtArabic)|Farsi|G(?:aelic|eorgian|reek|u(?:jarati|rmukhi))|H(?:FS|ebrew)|I(?:celandic|nuit)|Japanese|K(?:annada|hmer|orean)|Laotian|M(?:alayalam|ongolian)|Oriya|Roman(?:Latin1|ian)?|S(?:inhalese|ymbol)|T(?:amil|elugu|hai|ibetan|urkish)|Ukrainian|V(?:T100|ietnamese))|N(?:extStep(?:Japanese|Latin)|onLossyASCII)|ShiftJIS(?:_X0213(?:_(?:00|MenKuTen))?)?|U(?:TF(?:16(?:BE|LE)?|32(?:BE|LE)?|7(?:_IMAP)?|8)|nicode)|VISCII|Windows(?:Arabic|BalticRim|Cyrillic|Greek|Hebrew|KoreanJohab|Latin(?:1|2|5)|Vietnamese))|NormalizationForm(?:C|D|K(?:C|D))|Tokenizer(?:AttributeLa(?:nguage|tinTranscription)|Token(?:Has(?:DerivedSubTokensMask|HasNumbersMask|NonLettersMask|SubTokensMask)|IsCJWordMask|No(?:ne|rmal))|Unit(?:LineBreak|Paragraph|Sentence|Word(?:Boundary)?)))))|TimeZoneNameStyle(?:DaylightSaving|Generic|S(?:hort(?:DaylightSaving|Generic|Standard)|tandard))|U(?:RL(?:Bookmark(?:Creation(?:MinimalBookmarkMask|S(?:ecurityScopeAllowOnlyReadAccess|uitableForBookmarkFile)|WithSecurityScope)|ResolutionWith(?:SecurityScope|out(?:MountingMask|UIMask)))|Component(?:Fragment|Host|NetLocation|P(?:a(?:rameterString|ssword|th)|ort)|Query|ResourceSpecifier|Scheme|User(?:Info)?)|Enumerator(?:D(?:e(?:faultBehavior|scendRecursively)|irectoryPostOrderSuccess)|E(?:nd|rror)|GenerateFileReferenceURLs|IncludeDirectoriesP(?:ostOrder|reOrder)|S(?:kip(?:Invisibles|PackageContents)|uccess))|POSIXPathStyle|WindowsPathStyle)|serNotification(?:AlternateResponse|Ca(?:ncelResponse|utionAlertLevel)|DefaultResponse|No(?:DefaultButtonFlag|teAlertLevel)|OtherResponse|PlainAlertLevel|StopAlertLevel|UseRadioButtonsFlag))|XML(?:E(?:ntityType(?:Character|Par(?:ameter|sed(?:External|Internal))|Unparsed)|rror(?:E(?:lementlessDocument|ncodingConversionFailure)|Malformed(?:C(?:DSect|haracterReference|loseTag|omment)|D(?:TD|ocument)|Name|P(?:arsedCharacterData|rocessingInstruction)|StartTag)|NoData|Un(?:expectedEOF|knownEncoding)))|Node(?:CurrentVersion|Type(?:Attribute(?:ListDeclaration)?|C(?:DATASection|omment)|Document(?:Fragment|Type)?|E(?:lement(?:TypeDeclaration)?|ntity(?:Reference)?)|Notation|ProcessingInstruction|Text|Whitespace))|Parser(?:A(?:ddImpliedAttributes|llOptions)|NoOptions|Re(?:placePhysicalEntities|solveExternalEntities)|Skip(?:MetaData|Whitespace)|ValidateDocument)|StatusParse(?:InProgress|NotBegun|Successful))))\\b", "name": "support.constant.cf.c" }, + { + "match": "\\bDISPATCH_WALLTIME_NOW\\b", + "name": "support.constant.clib.10.14.c" + }, { "match": "\\b(?:FILESEC_(?:ACL(?:_(?:ALLOCSIZE|RAW))?|GR(?:OUP|PUUID)|MODE|OWNER|UUID)|P_(?:ALL|P(?:GID|ID)))\\b", "name": "support.constant.clib.c" @@ -86,23 +250,39 @@ "name": "support.constant.os.c" }, { - "match": "\\bkCGImageByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Mask)\\b", - "name": "support.constant.quartz.10.12.c" + "match": "\\bkCGImagePixelFormat(?:Mask|Packed|RGB(?:101010|5(?:55|65)|CIF10))\\b", + "name": "support.constant.quartz.10.14.c" }, { - "match": "\\b(?:CG(?:GlyphM(?:ax|in)|PDFDataFormat(?:JPEG(?:2000|Encoded)|Raw)|RectM(?:ax(?:XEdge|YEdge)|in(?:XEdge|YEdge)))|kCG(?:A(?:nnotatedSessionEventTap|ssistiveTechHighWindowLevelKey)|B(?:a(?:ck(?:ingStore(?:Buffered|Nonretained|Retained)|stopMenuLevelKey)|seWindowLevelKey)|itmap(?:AlphaInfoMask|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask)|Float(?:Components|InfoMask))|lendMode(?:C(?:lear|o(?:lor(?:Burn|Dodge)?|py))|D(?:arken|estination(?:Atop|In|O(?:ut|ver))|ifference)|Exclusion|H(?:ardLight|ue)|L(?:ighten|uminosity)|Multiply|Normal|Overlay|Plus(?:Darker|Lighter)|S(?:aturation|creen|o(?:ftLight|urce(?:Atop|In|Out)))|XOR))|C(?:aptureNo(?:Fill|Options)|o(?:lor(?:ConversionTransform(?:ApplySpace|FromSpace|ToSpace)|SpaceModel(?:CMYK|DeviceN|Indexed|Lab|Monochrome|Pattern|RGB|Unknown))|nfigure(?:For(?:AppOnly|Session)|Permanently))|ursorWindowLevelKey)|D(?:esktop(?:IconWindowLevelKey|WindowLevelKey)|isplay(?:AddFlag|BeginConfigurationFlag|D(?:esktopShapeChangedFlag|isabledFlag)|EnabledFlag|M(?:irrorFlag|ovedFlag)|RemoveFlag|S(?:etM(?:ainFlag|odeFlag)|tream(?:FrameStatus(?:Frame(?:Blank|Complete|Idle)|Stopped)|Update(?:DirtyRects|MovedRects|Re(?:ducedDirtyRects|freshedRects))))|UnMirrorFlag)|ockWindowLevelKey|raggingWindowLevelKey)|E(?:rror(?:CannotComplete|Failure|I(?:llegalArgument|nvalid(?:Con(?:nection|text)|Operation))|No(?:neAvailable|tImplemented)|RangeCheck|Success|TypeCheck)|vent(?:F(?:ilterMaskPermit(?:Local(?:KeyboardEvents|MouseEvents)|SystemDefinedEvents)|lag(?:Mask(?:Al(?:phaShift|ternate)|Co(?:mmand|ntrol)|Help|N(?:onCoalesced|umericPad)|S(?:econdaryFn|hift))|sChanged))|Key(?:Down|Up)|LeftMouse(?:D(?:own|ragged)|Up)|Mouse(?:Moved|Subtype(?:Default|TabletP(?:oint|roximity)))|Null|OtherMouse(?:D(?:own|ragged)|Up)|RightMouse(?:D(?:own|ragged)|Up)|S(?:crollWheel|ource(?:GroupID|State(?:CombinedSessionState|HIDSystemState|ID|Private)|U(?:nixProcessID|ser(?:Data|ID)))|uppressionState(?:RemoteMouseDrag|SuppressionInterval))|Ta(?:bletP(?:ointer|roximity)|p(?:DisabledBy(?:Timeout|UserInput)|Option(?:Default|ListenOnly))|rget(?:ProcessSerialNumber|UnixProcessID))))|F(?:loatingWindowLevelKey|ontPostScriptFormatType(?:1|3|42))|G(?:esturePhase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin|None)|radientDraws(?:AfterEndLocation|BeforeStartLocation))|H(?:IDEventTap|e(?:adInsertEventTap|lpWindowLevelKey))|I(?:mageAlpha(?:First|Last|None(?:Skip(?:First|Last))?|Only|Premultiplied(?:First|Last))|nterpolation(?:Default|High|Low|Medium|None))|KeyboardEvent(?:Autorepeat|Key(?:boardType|code))|Line(?:Cap(?:Butt|Round|Square)|Join(?:Bevel|Miter|Round))|M(?:a(?:inMenuWindowLevelKey|ximumWindowLevelKey)|inimumWindowLevelKey|o(?:dalPanelWindowLevelKey|mentumScrollPhase(?:Begin|Continue|End|None)|use(?:Button(?:Center|Left|Right)|Event(?:ButtonNumber|ClickState|Delta(?:X|Y)|InstantMouser|Number|Pressure|Subtype|WindowUnderMousePointer(?:ThatCanHandleThisEvent)?))))|N(?:ormalWindowLevelKey|umberOf(?:EventSuppressionStates|WindowLevelKeys))|OverlayWindowLevelKey|P(?:DF(?:ArtBox|BleedBox|CropBox|MediaBox|ObjectType(?:Array|Boolean|Dictionary|Integer|N(?:ame|ull)|Real|Str(?:eam|ing))|TrimBox)|at(?:h(?:E(?:OFill(?:Stroke)?|lement(?:Add(?:CurveToPoint|LineToPoint|QuadCurveToPoint)|CloseSubpath|MoveToPoint))|Fill(?:Stroke)?|Stroke)|ternTiling(?:ConstantSpacing(?:MinimalDistortion)?|NoDistortion))|opUpMenuWindowLevelKey)|RenderingIntent(?:AbsoluteColorimetric|Default|Perceptual|RelativeColorimetric|Saturation)|S(?:cr(?:een(?:SaverWindowLevelKey|UpdateOperation(?:Move|Re(?:ducedDirtyRectangleCount|fresh)))|oll(?:EventUnit(?:Line|Pixel)|Phase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin)|WheelEvent(?:DeltaAxis(?:1|2|3)|FixedPtDeltaAxis(?:1|2|3)|I(?:nstantMouser|sContinuous)|MomentumPhase|PointDeltaAxis(?:1|2|3)|Scroll(?:Count|Phase))))|essionEventTap|tatusWindowLevelKey)|T(?:a(?:blet(?:Event(?:DeviceID|Point(?:Buttons|Pressure|X|Y|Z)|Rotation|T(?:angentialPressure|ilt(?:X|Y))|Vendor(?:1|2|3))|ProximityEvent(?:CapabilityMask|DeviceID|EnterProximity|Pointer(?:ID|Type)|SystemTabletID|TabletID|Vendor(?:ID|Pointer(?:SerialNumber|Type)|UniqueID)))|ilAppendEventTap)|ext(?:Clip|Fill(?:Clip|Stroke(?:Clip)?)?|Invisible|Stroke(?:Clip)?)|ornOffMenuWindowLevelKey)|UtilityWindowLevelKey|Window(?:Image(?:B(?:estResolution|oundsIgnoreFraming)|Default|NominalResolution|OnlyShadows|ShouldBeOpaque)|List(?:ExcludeDesktopElements|Option(?:All|IncludingWindow|OnScreen(?:AboveWindow|BelowWindow|Only)))|Sharing(?:None|Read(?:Only|Write)))))\\b", + "match": "\\bCGPDFTagType(?:A(?:nnotation|rt)|B(?:ibliography|lockQuote)|C(?:aption|ode)|D(?:iv|ocument)|F(?:igure|orm(?:ula)?)|Header(?:1|2|3|4|5|6)?|Index|L(?:abel|i(?:nk|st(?:Body|Item)?))|No(?:nStructure|te)|P(?:ar(?:agraph|t)|rivate)|Quote|R(?:eference|uby(?:AnnotationText|BaseText|Punctuation)?)|S(?:ection|pan)|T(?:OC(?:I)?|able(?:Body|DataCell|Footer|Header(?:Cell)?|Row)?)|Warichu(?:Punctiation|Text)?)\\b", + "name": "support.constant.quartz.10.15.c" + }, + { + "match": "\\b(?:CG(?:GlyphM(?:ax|in)|PDFDataFormat(?:JPEG(?:2000|Encoded)|Raw)|RectM(?:ax(?:XEdge|YEdge)|in(?:XEdge|YEdge)))|kCG(?:A(?:nnotatedSessionEventTap|ssistiveTechHighWindowLevelKey)|B(?:a(?:ck(?:ingStore(?:Buffered|Nonretained|Retained)|stopMenuLevelKey)|seWindowLevelKey)|itmap(?:AlphaInfoMask|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask)|Float(?:Components|InfoMask))|lendMode(?:C(?:lear|o(?:lor(?:Burn|Dodge)?|py))|D(?:arken|estination(?:Atop|In|O(?:ut|ver))|ifference)|Exclusion|H(?:ardLight|ue)|L(?:ighten|uminosity)|Multiply|Normal|Overlay|Plus(?:Darker|Lighter)|S(?:aturation|creen|o(?:ftLight|urce(?:Atop|In|Out)))|XOR))|C(?:aptureNo(?:Fill|Options)|o(?:lor(?:ConversionTransform(?:ApplySpace|FromSpace|ToSpace)|SpaceModel(?:CMYK|DeviceN|Indexed|Lab|Monochrome|Pattern|RGB|Unknown|XYZ))|nfigure(?:For(?:AppOnly|Session)|Permanently))|ursorWindowLevelKey)|D(?:esktop(?:IconWindowLevelKey|WindowLevelKey)|isplay(?:AddFlag|BeginConfigurationFlag|D(?:esktopShapeChangedFlag|isabledFlag)|EnabledFlag|M(?:irrorFlag|ovedFlag)|RemoveFlag|S(?:etM(?:ainFlag|odeFlag)|tream(?:FrameStatus(?:Frame(?:Blank|Complete|Idle)|Stopped)|Update(?:DirtyRects|MovedRects|Re(?:ducedDirtyRects|freshedRects))))|UnMirrorFlag)|ockWindowLevelKey|raggingWindowLevelKey)|E(?:rror(?:CannotComplete|Failure|I(?:llegalArgument|nvalid(?:Con(?:nection|text)|Operation))|No(?:neAvailable|tImplemented)|RangeCheck|Success|TypeCheck)|vent(?:F(?:ilterMaskPermit(?:Local(?:KeyboardEvents|MouseEvents)|SystemDefinedEvents)|lag(?:Mask(?:Al(?:phaShift|ternate)|Co(?:mmand|ntrol)|Help|N(?:onCoalesced|umericPad)|S(?:econdaryFn|hift))|sChanged))|Key(?:Down|Up)|LeftMouse(?:D(?:own|ragged)|Up)|Mouse(?:Moved|Subtype(?:Default|TabletP(?:oint|roximity)))|Null|OtherMouse(?:D(?:own|ragged)|Up)|RightMouse(?:D(?:own|ragged)|Up)|S(?:crollWheel|ource(?:GroupID|State(?:CombinedSessionState|HIDSystemState|ID|Private)|U(?:nixProcessID|ser(?:Data|ID)))|uppressionState(?:RemoteMouseDrag|SuppressionInterval))|Ta(?:bletP(?:ointer|roximity)|p(?:DisabledBy(?:Timeout|UserInput)|Option(?:Default|ListenOnly))|rget(?:ProcessSerialNumber|UnixProcessID))|UnacceleratedPointerMovement(?:X|Y)))|F(?:loatingWindowLevelKey|ontPostScriptFormatType(?:1|3|42))|G(?:esturePhase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin|None)|radientDraws(?:AfterEndLocation|BeforeStartLocation))|H(?:IDEventTap|e(?:adInsertEventTap|lpWindowLevelKey))|I(?:mage(?:Alpha(?:First|Last|None(?:Skip(?:First|Last))?|Only|Premultiplied(?:First|Last))|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask))|nterpolation(?:Default|High|Low|Medium|None))|KeyboardEvent(?:Autorepeat|Key(?:boardType|code))|Line(?:Cap(?:Butt|Round|Square)|Join(?:Bevel|Miter|Round))|M(?:a(?:inMenuWindowLevelKey|ximumWindowLevelKey)|inimumWindowLevelKey|o(?:dalPanelWindowLevelKey|mentumScrollPhase(?:Begin|Continue|End|None)|use(?:Button(?:Center|Left|Right)|Event(?:ButtonNumber|ClickState|Delta(?:X|Y)|InstantMouser|Number|Pressure|Subtype|WindowUnderMousePointer(?:ThatCanHandleThisEvent)?))))|N(?:ormalWindowLevelKey|umberOf(?:EventSuppressionStates|WindowLevelKeys))|OverlayWindowLevelKey|P(?:DF(?:A(?:llows(?:Co(?:mmenting|ntent(?:Accessibility|Copying))|Document(?:Assembly|Changes)|FormFieldEntry|HighQualityPrinting|LowQualityPrinting)|rtBox)|BleedBox|CropBox|MediaBox|ObjectType(?:Array|Boolean|Dictionary|Integer|N(?:ame|ull)|Real|Str(?:eam|ing))|TrimBox)|at(?:h(?:E(?:OFill(?:Stroke)?|lement(?:Add(?:CurveToPoint|LineToPoint|QuadCurveToPoint)|CloseSubpath|MoveToPoint))|Fill(?:Stroke)?|Stroke)|ternTiling(?:ConstantSpacing(?:MinimalDistortion)?|NoDistortion))|opUpMenuWindowLevelKey)|RenderingIntent(?:AbsoluteColorimetric|Default|Perceptual|RelativeColorimetric|Saturation)|S(?:cr(?:een(?:SaverWindowLevelKey|UpdateOperation(?:Move|Re(?:ducedDirtyRectangleCount|fresh)))|oll(?:EventUnit(?:Line|Pixel)|Phase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin)|WheelEvent(?:DeltaAxis(?:1|2|3)|FixedPtDeltaAxis(?:1|2|3)|I(?:nstantMouser|sContinuous)|MomentumPhase|PointDeltaAxis(?:1|2|3)|Scroll(?:Count|Phase))))|essionEventTap|tatusWindowLevelKey)|T(?:a(?:blet(?:Event(?:DeviceID|Point(?:Buttons|Pressure|X|Y|Z)|Rotation|T(?:angentialPressure|ilt(?:X|Y))|Vendor(?:1|2|3))|ProximityEvent(?:CapabilityMask|DeviceID|EnterProximity|Pointer(?:ID|Type)|SystemTabletID|TabletID|Vendor(?:ID|Pointer(?:SerialNumber|Type)|UniqueID)))|ilAppendEventTap)|ext(?:Clip|Fill(?:Clip|Stroke(?:Clip)?)?|Invisible|Stroke(?:Clip)?)|ornOffMenuWindowLevelKey)|UtilityWindowLevelKey|Window(?:Image(?:B(?:estResolution|oundsIgnoreFraming)|Default|NominalResolution|OnlyShadows|ShouldBeOpaque)|List(?:ExcludeDesktopElements|Option(?:All|IncludingWindow|OnScreen(?:AboveWindow|BelowWindow|Only)))|Sharing(?:None|Read(?:Only|Write)))))\\b", "name": "support.constant.quartz.c" }, + { + "match": "\\bcl_device_id\\b", + "name": "support.type.10.10.c" + }, + { + "match": "\\b(?:JSTypedArrayType|SecKey(?:Algorithm|KeyExchangeParameter)|os_unfair_lock(?:_t)?)\\b", + "name": "support.type.10.12.c" + }, + { + "match": "\\b(?:A(?:E(?:A(?:ddressDesc|rray(?:Data(?:Pointer)?|Type))|BuildError(?:Code)?|Coerc(?:e(?:Desc(?:ProcPtr|UPP)|Ptr(?:ProcPtr|UPP))|ionHandlerUPP)|D(?:ataStorage(?:Type)?|esc(?:List|Ptr)?|isposeExternal(?:ProcPtr|UPP))|Event(?:Class|Handler(?:ProcPtr|UPP)|ID|Source)|Filter(?:ProcPtr|UPP)|I(?:dle(?:ProcPtr|UPP)|nteractAllowed)|Key(?:Desc|word)|Re(?:cord|moteProcessResolver(?:C(?:allback|ontext)|Ref)?|turnID)|S(?:end(?:Mode|Priority)|treamRef)|TransactionID)|FP(?:AlternateAddress|ServerSignature|TagData|VolMountInfo(?:Ptr)?|XVolMountInfo(?:Ptr)?)|HTOCType|IFFLoop|LMX(?:GlyphEntry|Header)|TS(?:Cu(?:bic(?:C(?:losePath(?:ProcPtr|UPP)|urveTo(?:ProcPtr|UPP))|LineTo(?:ProcPtr|UPP)|MoveTo(?:ProcPtr|UPP))|rveType)|F(?:SSpec|latData(?:Font(?:NameDataHeader|Spec(?:RawNameData(?:Header)?|iferType))|L(?:ayoutControlsDataHeader|ineInfo(?:Data|Header))|MainHeaderBlock|Style(?:List(?:FeatureData|Header|StyleDataHeader|VariationData)|RunDataHeader)|TextLayout(?:DataHeader|Header))|ont(?:A(?:pplierFunction|utoActivationSetting)|Cont(?:ainerRef|ext)|F(?:amily(?:ApplierFunction|Iterator(?:_)?|Ref)|ilter(?:Selector)?|ormat)|Iterator(?:_)?|Metrics|Notif(?:ication(?:InfoRef(?:_)?|Ref(?:_)?)|y(?:Action|Option))|Query(?:Callback|MessageID|SourceContext)|Ref|Size))|G(?:eneration|lyph(?:I(?:dealMetrics|nfoFlags)|Ref|ScreenMetrics|Vector))|Just(?:PriorityWidthDeltaOverrides|WidthDeltaEntryOverride)|L(?:ayoutRecord|ineLayoutOptions)|NotificationCallback|OptionFlags|Point|Quadratic(?:C(?:losePath(?:ProcPtr|UPP)|urve(?:ProcPtr|UPP))|Line(?:ProcPtr|UPP)|NewPath(?:ProcPtr|UPP))|StyleRenderingOptions|Trapezoid|U(?:Attribute(?:Info|Tag|ValuePtr)|Background(?:Color|Data(?:Type)?)|C(?:aret|ur(?:sorMovementType|vePath(?:s)?))|Direct(?:DataSelector|LayoutOperationOverride(?:ProcPtr|UPP))|F(?:latten(?:StyleRunOptions|edDataStreamFormat)|ont(?:F(?:allback(?:Method|s)|eature(?:Selector|Type))|ID|Variation(?:Axis|Value)))|Glyph(?:Info(?:Array)?|Selector)|HighlightMethod|L(?:ayoutOperation(?:CallbackStatus|OverrideSpecifier|Selector)|ine(?:Ref|Truncation))|RGBAlphaColor|Style(?:Comparison|LineCountType|RunInfo|SettingRef)?|T(?:ab(?:Type)?|ext(?:Layout|Measurement))|Un(?:FlattenStyleRunOptions|highlightData)|VerticalCharacterType))|U(?:3DMixer(?:AttenuationCurve|RenderingFlags)|ChannelInfo|D(?:ependentParameter|istanceAttenuationData)|EventListener(?:Block|Proc|Ref)|Graph|Host(?:Identifier|VersionIdentifier)|InputSamplesInOutputCallback(?:Struct)?|ListenerBase|MIDIOutputCallback(?:Struct)?|N(?:ode(?:Connection|Interaction|RenderCallback)?|umVersion)|P(?:arameter(?:EventType|Listener(?:Block|Proc|Ref)|MIDIMapping(?:Flags)?)|reset(?:Event)?)|Re(?:nderCallback(?:Struct)?|verbRoomType)|S(?:ampler(?:BankPresetData|InstrumentData)|cheduledAudioSliceFlags|patial(?:Mixer(?:AttenuationCurve|RenderingFlags)|izationAlgorithm)))|V(?:Audio(?:Integer|SessionErrorCode|UInteger)|L(?:CompareItems(?:ProcPtr|UPP)|DisposeItem(?:ProcPtr|UPP)|ItemSize(?:ProcPtr|UPP)|NodeType|Order|Tree(?:Ptr|Struct)|VisitStage|Walk(?:ProcPtr|UPP)))|X(?:CopyMultipleAttributeOptions|Error|MenuItemModifiers|Observer(?:Callback(?:WithInfo)?|Ref)|Priority|U(?:IElementRef|nderlineStyle)|Value(?:Ref|Type))|l(?:ert(?:Std(?:AlertParam(?:Ptr|Rec)|CFStringAlertParam(?:Ptr|Rec))|T(?:Hndl|Ptr|emplate|ype))|ias(?:Handle|InfoType|Ptr|Record))|n(?:chorPoint(?:Table)?|krTable)|pp(?:Parameters(?:Ptr)?|earance(?:PartCode|RegionCode)|l(?:eEvent(?:Ptr)?|icationSpecificChunk(?:Ptr)?))|reaID|sscEntry|u(?:dio(?:B(?:alanceFade(?:Type)?|uffer(?:List)?|ytePacketTranslation(?:Flags)?)|C(?:hannel(?:Bitmap|CoordinateIndex|Description|Flags|La(?:bel|yout(?:Tag)?))|lass(?:Description|ID)|o(?:dec(?:AppendInput(?:BufferListProc|DataProc)|GetProperty(?:InfoProc|Proc)|InitializeProc|MagicCookieInfo|Pr(?:imeInfo|o(?:duceOutput(?:BufferListProc|PacketsProc)|pertyID))|ResetProc|SetPropertyProc|UninitializeProc)?|mponent(?:Description|F(?:actoryFunction|lags)|Instan(?:ce|tiationOptions)|Method|PlugInInterface|ValidationResult)?|nverter(?:ComplexInputDataProc|InputDataProc|Pr(?:imeInfo|opertyID)|Ref)))|Device(?:I(?:D|O(?:Block|Proc(?:ID)?))|Property(?:ID|ListenerProc))|F(?:ile(?:Component(?:C(?:loseProc|ountUserDataProc|reateURLProc)|ExtensionIsThisFormatProc|FileDataIsThisFormatProc|Get(?:GlobalInfo(?:Proc|SizeProc)|Property(?:InfoProc|Proc)|UserData(?:Proc|SizeProc))|InitializeWithCallbacksProc|Op(?:en(?:URLProc|WithCallbacksProc)|timizeProc)|PropertyID|Re(?:ad(?:BytesProc|Packet(?:DataProc|sProc))|moveUserDataProc)|Set(?:PropertyProc|UserDataProc)|Write(?:BytesProc|PacketsProc))?|F(?:DFTable(?:Extended)?|lags)|ID|Marker(?:List)?|P(?:acketTableInfo|ermissions|ropertyID)|Region(?:Flags|List)?|Stream(?:ID|P(?:arseFlags|roperty(?:Flags|ID))|SeekFlags|_P(?:acketsProc|ropertyListenerProc))|Type(?:AndFormatID|ID)|_(?:GetSizeProc|ReadProc|S(?:MPTE_Time|etSizeProc)|WriteProc))|ormat(?:Flags|I(?:D|nfo)|ListItem|PropertyID)|ramePacketTranslation)|Hardware(?:IOProcStreamUsage|P(?:owerHint|roperty(?:ID|ListenerProc)))|IndependentPacketTranslation|LevelControlTransferFunction|O(?:bject(?:ID|Property(?:Address|Element|Listener(?:Block|Proc)|S(?:cope|elector)))|utputUnitSt(?:art(?:AtTimeParams|Proc)|opProc))|Pa(?:cket(?:DependencyInfoTranslation|R(?:angeByteCountTranslation|ollDistanceTranslation))|nning(?:Info|Mode))|Queue(?:Buffer(?:Ref)?|ChannelAssignment|InputCallback(?:Block)?|LevelMeterState|OutputCallback(?:Block)?|P(?:arameter(?:Event|ID|Value)|ro(?:cessingTap(?:Callback|Flags|Ref)|perty(?:ID|ListenerProc)))|Ref|TimelineRef)|RecordingChunk(?:Ptr)?|S(?:ampleType|e(?:rvices(?:PropertyID|SystemSoundCompletionProc)|ttingsFlags)|tream(?:BasicDescription|ID|P(?:acketDescription|ropertyListenerProc)|RangedDescription))|TimeStamp(?:Flags)?|Unit(?:Add(?:PropertyListenerProc|RenderNotifyProc)|Co(?:coaViewInfo|mplexRenderProc|nnection)|E(?:lement|vent(?:Type)?|xternalBuffer)|FrequencyResponseBin|GetP(?:arameterProc|roperty(?:InfoProc|Proc))|InitializeProc|M(?:IDIControlMapping|eterClipping)|NodeConnection|OtherPluginDesc|P(?:arameter(?:Event|HistoryInfo|I(?:D(?:Name)?|nfo)|NameInfo|Options|StringFromValue|Unit|Value(?:FromString|Name|Translation)?)?|r(?:esetMAS_Setting(?:Data|s)|o(?:cess(?:MultipleProc|Proc)|perty(?:ID|ListenerProc)?)))|Re(?:move(?:PropertyListener(?:Proc|WithUserDataProc)|RenderNotifyProc)|nder(?:ActionFlags|Proc)|setProc)|S(?:ampleType|c(?:heduleParametersProc|ope)|etP(?:arameterProc|ropertyProc))|UninitializeProc)?|Value(?:Range|Translation))|thorization(?:AsyncCallback|E(?:nvironment|xternalForm)|Flags|Item(?:Set)?|OpaqueRef|R(?:ef|ights)|String)))|B(?:T(?:HeaderRec|NodeDescriptor|reeKey(?:Limits)?)|asicWindowDescription|i(?:gEndian(?:Fixed|Long|OSType|Short|U(?:Int32|nsigned(?:Fixed|Long|Short)))|tMap(?:Handle|Ptr)?)|sln(?:Baseline(?:Class|Record)|Format(?:0Part|1Part|2Part|3Part|Union)|Table(?:Format|Ptr)?))|C(?:A(?:BarBeatTime|Clock(?:Beats|ListenerProc|Message|PropertyID|Ref|S(?:MPTEFormat|amples|econds|yncMode)|T(?:empo|ime(?:Format|base)?))|F(?:Audio(?:Description|FormatListItem)|ChunkHeader|DataChunk|F(?:ileHeader|ormatFlags)|In(?:foStrings|strumentChunk)|Marker(?:Chunk)?|Overview(?:Chunk|Sample)|P(?:acketTableHeader|eakChunk|ositionPeak)|Region(?:Chunk|Flags)?|String(?:ID|s)|UMIDChunk|_(?:SMPTE_Time|UUID_ChunkHeader))|MeterTrackEntry|T(?:empoMapEntry|ransform3D))|C(?:Tab(?:Handle|Ptr)|_(?:LONG(?:64)?|MD(?:2(?:_CTX|state_st)|4(?:_CTX|state_st)|5(?:_CTX|state_st))|SHA(?:1(?:_CTX|state_st)|256(?:_CTX|state_st)|512(?:_CTX|state_st))))|E_(?:CrlNumber|D(?:ataType|eltaCrl)|ExtendedKeyUsage|GeneralNameType)|F(?:H(?:TTP(?:AuthenticationRef|MessageRef)|ost(?:ClientC(?:allBack|ontext)|InfoType|Ref))|Net(?:Diagnostic(?:Ref|StatusValues)|Service(?:Browser(?:ClientCallBack|Flags|Ref)|ClientC(?:allBack|ontext)|Monitor(?:ClientCallBack|Ref|Type)|Re(?:f|gisterFlags)|sError)|workErrors)|ProxyAutoConfigurationResultCallback|StreamErrorHTTP(?:Authentication)?)|G(?:Image(?:AnimationStatus|Destination(?:Ref)?|Metadata(?:Errors|Ref|T(?:ag(?:Ref)?|ype))?|PropertyOrientation|Source(?:AnimationBlock|Ref|Status)?)|L(?:C(?:PContextPriorityRequest|ontext(?:Enable|Obj|Parameter))|Error|G(?:PURestartStatus|lobalOption)|OpenGLProfile|P(?:BufferObj|ixelFormat(?:Attribute|Obj))|Renderer(?:InfoObj|Property)|ShareGroup(?:Obj|Rec))|MutableImageMetadataRef|rafP(?:ort|tr))|M(?:2(?:Header|Profile(?:Ptr)?)|4Header|AdaptationMatrixType|B(?:itmap|ufferLocation)|C(?:MY(?:Color|KColor)|oncatProfileSet|urveType)|D(?:at(?:aType|eTime(?:Type)?)|evice(?:Class|Info|Profile(?:Array|Info)|Scope))|F(?:ixedXY(?:Color|ZColor)|l(?:atten(?:ProcPtr|UPP)|oatBitmap(?:Flags)?))|GrayColor|H(?:LSColor|SVColor|andleLocation)|IntentCRDVMSize|L(?:abColor|u(?:t(?:16Type|8Type)|vColor))|M(?:CreateTransformPropertyProc|Info|akeAndModel(?:Type)?|easurementType|ulti(?:Funct(?:CLUTType|Lut(?:A2BType|Type))|LocalizedUniCode(?:EntryRec|Type)|channel(?:5Color|6Color|7Color|8Color)))|Na(?:medColor(?:2(?:EntryType|Type)|Type)?|tiveDisplayInfo(?:Type)?)|P(?:S2CRDVMSizeType|a(?:rametricCurveType|thLocation)|rofile(?:IterateData|Location|SequenceDescType))|RGBColor|S(?:15Fixed16ArrayType|CertificateChainMode|DecoderRef|EncoderRef|Signe(?:dAttributes|rStatus)|creening(?:ChannelRec|Type)|ignatureType)|T(?:ag(?:ElemTable|Record)|ext(?:DescriptionType|Type))|U(?:16Fixed16ArrayType|Int(?:16ArrayType|32ArrayType|64ArrayType|8ArrayType)|crBgType|nicodeTextType)|Vi(?:deoCardGamma(?:Formula|T(?:able|ype))?|ewingConditionsType)|XYZ(?:Co(?:lor|mponent)|Type)|YxyColor)|QDProcs(?:Ptr)?|S(?:ComponentsThreadMode|DiskSpaceRecovery(?:Callback|Options)|Identity(?:AuthorityRef|Cl(?:ass|ientContext)|Flags|Query(?:ClientContext|Event|Flags|Re(?:ceiveEventCallback|f)|StringComparisonMethod)|Ref|StatusUpdatedCallback)|SM_(?:A(?:C(?:L_(?:AUTHORIZATION_TAG|EDIT_MODE|HANDLE|KEYCHAIN_PROMPT_SELECTOR|PR(?:EAUTH_TRACKING_STATE|OCESS_SUBJECT_SELECTOR)|SUBJECT_TYPE)|_HANDLE)|LGORITHMS|PPLE(?:CSPDL_DB_(?:CHANGE_PASSWORD_PARAMETERS(?:_PTR)?|IS_LOCKED_PARAMETERS(?:_PTR)?|SETTINGS_PARAMETERS(?:_PTR)?)|DL_OPEN_PARAMETERS(?:_PTR)?|_(?:CL_CSR_REQUEST|TP_(?:ACTION_(?:DATA|FLAGS)|C(?:ERT_REQUEST|RL_OPT(?:IONS|_FLAGS))|NAME_OID|S(?:MIME_OPTIONS|SL_OPTIONS))))|TT(?:ACH_FLAGS|RIBUTE_TYPE))|B(?:ER_TAG|ITMASK|OOL)|C(?:ALLOC|C_HANDLE|ERT(?:GROUP_TYPE(?:_PTR)?|_(?:BUNDLE_(?:ENCODING|TYPE)|ENCODING(?:_PTR)?|PARSE_FORMAT(?:_PTR)?|TYPE(?:_PTR)?))|L_(?:HANDLE|TEMPLATE_TYPE)|ONTEXT_(?:EVENT|TYPE)|RL(?:GROUP_TYPE(?:_PTR)?|_(?:ENCODING(?:_PTR)?|PARSE_FORMAT(?:_PTR)?|TYPE(?:_PTR)?))|SP(?:TYPE|_(?:FLAGS|HANDLE|READER_FLAGS)))|D(?:B_(?:A(?:CCESS_TYPE(?:_PTR)?|TTRIBUTE_(?:FORMAT(?:_PTR)?|NAME_FORMAT(?:_PTR)?))|CONJUNCTIVE(?:_PTR)?|HANDLE|INDEX(?:ED_DATA_LOCATION|_TYPE)|MODIFY_MODE|OPERATOR(?:_PTR)?|RE(?:CORDTYPE|TRIEVAL_MODES))|L(?:TYPE(?:_PTR)?|_(?:CUSTOM_ATTRIBUTES|FFS_ATTRIBUTES|HANDLE|LDAP_ATTRIBUTES|ODBC_ATTRIBUTES|PKCS11_ATTRIBUTE(?:_PTR)?)))|E(?:NCRYPT_MODE|VIDENCE_FORM)|FREE|H(?:ANDLE(?:_PTR)?|EADERVERSION)|INTPTR|K(?:EY(?:ATTR_FLAGS|BLOB_(?:FORMAT|TYPE)|CLASS|USE|_(?:HIERARCHY|TYPE))|R(?:SP_HANDLE|_POLICY_(?:FLAGS|TYPE)))|L(?:IST_(?:ELEMENT_(?:PTR|TYPE(?:_PTR)?)|TYPE(?:_PTR)?)|ONG_HANDLE(?:_PTR)?)|M(?:A(?:LLOC|NAGER_EVENT_TYPES)|ODULE_(?:EVENT(?:_PTR)?|HANDLE(?:_PTR)?))|NET_(?:ADDRESS_TYPE|PROTOCOL)|P(?:ADDING|KCS(?:5_PBKDF2_PRF|_OAEP_(?:MGF|PSOURCE))|R(?:IVILEGE(?:_SCOPE)?|OC_ADDR(?:_PTR)?)|VC_MODE)|QUERY_FLAGS|RE(?:ALLOC|TURN)|S(?:AMPLE_TYPE|C_FLAGS|ERVICE_(?:MASK|TYPE)|IZE|TRING)|T(?:IMESTRING|P_(?:A(?:CTION|PPLE_(?:CERT_STATUS|EVIDENCE_HEADER)|UTHORITY_REQUEST_TYPE(?:_PTR)?)|C(?:ERT(?:CHANGE_(?:ACTION|REASON|STATUS)|ISSUE_STATUS|NOTARIZE_STATUS|RECLAIM_STATUS|VERIFY_STATUS)|ONFIRM_STATUS(?:_PTR)?|RLISSUE_STATUS)|FORM_TYPE|HANDLE|S(?:ERVICES|TOP_ON)))|USEE_TAG|WORDID_TYPE|X509(?:EXT_DATA_FORMAT|_OPTION))|pecArray)|T(?:CharacterCollection|F(?:ont(?:Collection(?:CopyOptions|Ref|SortDescriptorsCallback)|Descriptor(?:MatchingState|Ref)|Format|Manager(?:AutoActivationSetting|Error|Scope)|O(?:ptions|rientation)|Priority|Ref|S(?:tylisticClass|ymbolicTraits)|Table(?:Options|Tag)|UIFontType)|rame(?:P(?:athFillRule|rogression)|Ref|setterRef))|GlyphInfoRef|Line(?:B(?:oundsOptions|reakMode)|Ref|TruncationType)|MutableFontCollectionRef|ParagraphStyle(?:Ref|S(?:etting|pecifier))|Ru(?:by(?:A(?:lignment|nnotationRef)|Overhang|Position)|n(?:Delegate(?:Callbacks|DeallocateCallback|Get(?:AscentCallback|DescentCallback|WidthCallback)|Ref)|Ref|Status))|T(?:ext(?:Alignment|TabRef)|ypesetterRef)|UnderlineStyle(?:Modifiers)?|WritingDirection|ab(?:Handle|Ptr))|V(?:AttachmentMode|BufferRef|DisplayLink(?:Output(?:Callback|Handler)|Ref)|FillExtendedPixelsCallBack(?:Data)?|ImageBufferRef|Op(?:enGL(?:Buffer(?:PoolRef|Ref)|Texture(?:CacheRef|Ref))|tionFlags)|P(?:ixelBuffer(?:LockFlags|Pool(?:FlushFlags|Ref)|Re(?:f|lease(?:BytesCallback|PlanarBytesCallback)))|lanar(?:ComponentInfo|PixelBufferInfo(?:_YCbCr(?:BiPlanar|Planar))?))|Return|SMPTETime(?:Flags|Type)?|Time(?:Flags|Stamp(?:Flags)?)?)|a(?:l(?:ibrat(?:e(?:Event(?:ProcPtr|UPP)|ProcPtr|UPP)|orInfo)|lingConventionType)|nCalibrate(?:ProcPtr|UPP)|retHook(?:ProcPtr|UPP)|tPositionRec)|ell|h(?:ar(?:ByteTable|s(?:Handle|Ptr)?)|unkHeader)|lickActivationResult|o(?:l(?:l(?:atorRef|ection(?:Exception(?:ProcPtr|UPP)|Flatten(?:ProcPtr|UPP)|Tag)?)|or(?:C(?:hangedUPP|omplement(?:ProcPtr|UPP))|S(?:earch(?:ProcPtr|UPP)|pec(?:Ptr)?|ync(?:AlphaInfo|CMM(?:Ref)?|Data(?:Depth|Layout)|M(?:D5|utableProfileRef)|Profile(?:Ref)?|Transform(?:Ref)?))|Table))|m(?:m(?:ent(?:Type|sChunk(?:Ptr)?)?|onChunk(?:Ptr)?)|ponent(?:AliasResource|Description|FunctionUPP|Instance(?:Record)?|MPWorkFunction(?:HeaderRecord(?:Ptr)?|ProcPtr|UPP)|P(?:arameters|latformInfo(?:Array)?)|R(?:e(?:cord|s(?:ource(?:Extension|Handle|Ptr)?|ult))|outine(?:ProcPtr|UPP)))?)|n(?:st(?:ATSUAttributeValuePtr|FS(?:EventStreamRef|SpecPtr)|HFSUniStr255Param|ScriptCodeRunPtr|Text(?:EncodingRunPtr|Ptr|ToUnicodeInfo)|Uni(?:CharArrayPtr|code(?:MappingPtr|ToTextInfo)))|t(?:ainerChunk|extualMenuInterfaceStruct|rol(?:Action(?:ProcPtr|UPP)|B(?:evel(?:Button(?:Behavior|Menu(?:Behavior|Placement))|Thickness)|utton(?:ContentInfo(?:Ptr)?|GraphicAlignment|Text(?:Alignment|Placement)))|C(?:lock(?:Flags|Type)|ontentType)|DisclosureTriangleOrientation|EditText(?:Selection(?:Ptr|Rec)|Validation(?:ProcPtr|UPP))|Fo(?:cusPart|ntStyle(?:Ptr|Rec))|Handle|I(?:D|mageContentInfo(?:Ptr)?)|K(?:ey(?:Filter(?:ProcPtr|Result|UPP)|ScriptBehavior)|ind)|P(?:artCode|opupArrow(?:Orientation|Size)|ushButtonIconAlignment)|R(?:ef|oundButtonSize)|S(?:ize|liderOrientation)|T(?:ab(?:Direction|Entry|InfoRec(?:V1)?|Size)|emplate(?:Handle|Ptr)?)|UserPane(?:Activate(?:ProcPtr|UPP)|Draw(?:ProcPtr|UPP)|Focus(?:ProcPtr|UPP)|HitTest(?:ProcPtr|UPP)|Idle(?:ProcPtr|UPP)|KeyDown(?:ProcPtr|UPP)|Tracking(?:ProcPtr|UPP))|Variant)))|reEndianFlipProc|untUserDataFDF)|tlCTab|ustomBadgeResource(?:Handle|Ptr)?)|D(?:A(?:ApprovalSessionRef|DiskRef|SessionRef)|C(?:M(?:AccessMethod(?:Feature|I(?:D|terator))|Dictionary(?:Header|I(?:D|terator)|Ref|StreamRef)|F(?:i(?:eld(?:Attributes|T(?:ag|ype))|ndMethod)|oundRecordIterator)|Object(?:I(?:D|terator)|Ref)|ProgressFilter(?:ProcPtr|UPP)|UniqueID)|SDictionaryRef)|ER(?:Byte|Item|Size)|I(?:TLMethod|nfo)|R(?:AudioTrackRef|BurnRef|CDTextBlockRef|DeviceRef|EraseRef|F(?:SObjectRef|ile(?:Fork(?:Index|Size(?:Info|Query))|Message|Pro(?:c|ductionInfo)|Ref|system(?:Mask|TrackRef))|olderRef)|LinkType|NotificationC(?:allback|enterRef)|RefCon(?:Callbacks|Re(?:leaseCallback|tainCallback))|T(?:rack(?:CallbackProc|Message|ProductionInfo|Ref)|ypeRef))|XInfo|at(?:a(?:Array|Browser(?:A(?:cce(?:ptDrag(?:ProcPtr|UPP)|ssibilityItemInfo(?:V(?:0|1))?)|ddDragItem(?:ProcPtr|UPP))|C(?:allbacks|ustomCallbacks)|Dra(?:gFlags|wItem(?:ProcPtr|UPP))|Edit(?:Command|Item(?:ProcPtr|UPP))|GetContextualMenu(?:ProcPtr|UPP)|HitTest(?:ProcPtr|UPP)|Item(?:AcceptDrag(?:ProcPtr|UPP)|Compare(?:ProcPtr|UPP)|D(?:ata(?:ProcPtr|Ref|UPP)|ragRgn(?:ProcPtr|UPP))|HelpContent(?:ProcPtr|UPP)|ID|Notification(?:ProcPtr|UPP|WithItem(?:ProcPtr|UPP))?|ProcPtr|ReceiveDrag(?:ProcPtr|UPP)|State|UPP)|ListView(?:ColumnDesc|HeaderDesc|PropertyFlags)|Metric|P(?:ostProcessDrag(?:ProcPtr|UPP)|roperty(?:Desc|Flags|ID|Part|Type))|Re(?:ceiveDrag(?:ProcPtr|UPP)|vealOptions)|S(?:e(?:lect(?:ContextualMenu(?:ProcPtr|UPP)|ion(?:AnchorDirection|Flags))|tOption)|ortOrder)|T(?:ableView(?:Column(?:Desc|I(?:D|ndex))|HiliteStyle|PropertyFlags|RowIndex)|racking(?:ProcPtr|Result|UPP))|ViewStyle)|Handle|Ptr)|e(?:Cache(?:Ptr|Record)|Delta|Form|Orders|TimeRec))|e(?:bug(?:AssertOutputHandler(?:ProcPtr|UPP)|ComponentCallback(?:ProcPtr|UPP)|ger(?:DisposeThread(?:ProcPtr|TPP|UPP)|NewThread(?:ProcPtr|TPP|UPP)|ThreadScheduler(?:ProcPtr|TPP|UPP)))|ferredTask(?:P(?:rocPtr|tr)|UPP)?|lim(?:Type|iterInfo)|s(?:cType|kHook(?:ProcPtr|UPP)))|ialog(?:Item(?:Index(?:ZeroBased)?|Type)|P(?:lacementSpec|tr)|Ref|T(?:Hndl|Ptr|emplate))|o(?:Get(?:FileTranslationListProcPtr|ScrapTranslationListProcPtr|TranslatedFilenameProcPtr)|Identify(?:FileProcPtr|ScrapProcPtr)|Translate(?:FileProcPtr|ScrapProcPtr)|cOpenMethod)|ra(?:g(?:A(?:ctions|ttributes)|Behaviors|Constraint|Drawing(?:ProcPtr|UPP)|GrayRgn(?:ProcPtr|UPP)|I(?:mageFlags|nput(?:ProcPtr|UPP)|temRef)|Re(?:ceiveHandler(?:ProcPtr|UPP)|f(?:erence)?|gionMessage)|SendData(?:ProcPtr|UPP)|Tracking(?:Handler(?:ProcPtr|UPP)|Message))|wHook(?:ProcPtr|UPP)))|E(?:OLHook(?:ProcPtr|UPP)|ditUnicodePostUpdate(?:ProcPtr|UPP)|v(?:Cmd|QEl(?:Ptr)?|ent(?:Attributes|C(?:lass(?:ID)?|omparator(?:ProcPtr|UPP))|H(?:andler(?:CallRef|ProcPtr|Ref|UPP)|otKey(?:ID|Ref))|Kind|Loop(?:IdleTimer(?:Message|ProcPtr|UPP)|Ref|Timer(?:ProcPtr|Ref|UPP))|M(?:ask|o(?:difiers|use(?:Button|WheelAxis)))|P(?:aram(?:Name|Type)|riority)|QueueRef|Re(?:cord|f)|T(?:argetRef|ime(?:out|rInterval)?|ype(?:Spec)?)))|x(?:ception(?:Handler(?:ProcPtr|TPP|UPP)?|Info(?:rmation(?:PowerPC)?)?|Kind)|t(?:AudioFile(?:PropertyID|Ref)|Com(?:monChunk(?:Ptr)?|ponentResource(?:Handle|Ptr)?)|ended(?:AudioFormatInfo|ControlEvent|F(?:ileInfo|olderInfo)|NoteOnEvent|TempoEvent))))|F(?:CFontDescriptorRef|I(?:LE|nfo)|KEY(?:ProcPtr|UPP)|M(?:F(?:ilter(?:Selector)?|ont(?:CallbackFilter(?:ProcPtr|UPP)|DirectoryFilter|Family(?:CallbackFilter(?:ProcPtr|UPP)|I(?:nstance(?:Iterator)?|terator))?|Iterator|S(?:ize|tyle))?)|Generation|Input)|N(?:Message|Subscription(?:ProcPtr|Ref|UPP))|P(?:RegIntel|UInformation(?:Intel64|PowerPC)?)|S(?:Al(?:ias(?:FilterProcPtr|Info(?:Bitmap|Ptr)?)|locationFlags)|Catalog(?:BulkParam(?:Ptr)?|Info(?:Bitmap|Ptr)?)|E(?:jectStatus|ventStream(?:C(?:allback|ontext|reateFlags)|Event(?:Flags|Id)|Ref))|F(?:ile(?:Operation(?:ClientContext|Ref|Sta(?:ge|tusProcPtr))|SecurityRef)|ork(?:CBInfoParam(?:Ptr)?|I(?:OParam(?:Ptr)?|nfo(?:Flags|Ptr)?)))|I(?:ORefNum|terator(?:Flags)?)|MountStatus|P(?:athFileOperationStatusProcPtr|ermissionInfo)|R(?:angeLockParam(?:Ptr)?|ef(?:ForkIOParam(?:Ptr)?|P(?:aram(?:Ptr)?|tr))?)|S(?:earchParams(?:Ptr)?|pec(?:ArrayPtr|Handle|Ptr)?)|UnmountStatus|Volume(?:Eject(?:ProcPtr|UPP)|Info(?:Bitmap|P(?:aram(?:Ptr)?|tr))?|Mount(?:ProcPtr|UPP)|Operation|RefNum|Unmount(?:ProcPtr|UPP)))|Vector|XInfo|amRec|ile(?:Info|T(?:ranslation(?:List(?:Handle|Ptr)?|Spec(?:Array(?:Handle|Ptr))?)|ype(?:Spec)?))|lavor(?:Flags|Type)|ndr(?:DirInfo|Extended(?:DirInfo|FileInfo)|FileInfo|OpaqueInfo)|o(?:lder(?:Class|Desc(?:Flags|Ptr)?|Info|Location|ManagerNotification(?:ProcPtr|UPP)|Routing(?:Ptr)?|Type)|nt(?:Assoc|Info|LanguageCode|NameCode|PlatformCode|Rec(?:Hdl|Ptr)?|S(?:criptCode|electionQDStyle(?:Ptr)?)|Variation)|rmat(?:Class|ResultType|Status|VersionChunk(?:Ptr)?)))|G(?:D(?:Handle|Ptr|evice)|L(?:b(?:itfield|oolean|yte)|c(?:har(?:ARB)?|lamp(?:d|f))|double|enum|f(?:ixed|loat)|ha(?:lf(?:ARB)?|ndleARB)|int(?:64(?:EXT)?|ptr(?:ARB)?)?|s(?:hort|izei(?:ptr(?:ARB)?)?|ync)|u(?:byte|int(?:64(?:EXT)?)?|short)|void)|NEFilterUPP|World(?:Flags|Ptr)|e(?:nericID|t(?:GrowImageRegionRec|MissingComponentResource(?:ProcPtr|UPP)|NextEventFilter(?:ProcPtr|UPP)|Property(?:FDF|InfoFDF)|ScrapData(?:ProcPtr|UPP)?|UserData(?:FDF|SizeFDF)|VolParmsInfoBuffer|WindowRegion(?:Ptr|Rec(?:Ptr)?)))|lyph(?:Collection|ID)|raf(?:P(?:ort|tr)|Verb))|H(?:FS(?:Catalog(?:F(?:ile|older)|Key|NodeID|Thread)|Extent(?:Descriptor|Key|Record)|Flavor|MasterDirectoryBlock|Plus(?:Attr(?:Data|Extents|ForkData|InlineData|Key|Record)|BSDInfo|Catalog(?:F(?:ile|older)|Key|Thread)|Extent(?:Descriptor|Key|Record)|ForkData|VolumeHeader)|UniStr255)|I(?:A(?:rchiveRef|xis(?:Position|Scale))|Binding(?:Kind)?|Co(?:mmand(?:Extended)?|ntentBorderMetrics|ordinateSpace)|DelegatePosition|ImageViewAutoTransformOptions|LayoutInfo|M(?:odalClickResult|utableShapeRef)|Object(?:ClassRef|Ref)|Po(?:int|sition(?:Kind|ing))|Rect|S(?:c(?:al(?:eKind|ing)|roll(?:BarTrackInfo|ViewAction))|egmentBehavior|hape(?:EnumerateProcPtr|Ref)|i(?:deBinding|ze))|T(?:heme(?:Animation(?:FrameInfo|TimeInfo)|B(?:ackgroundDrawInfo(?:Ptr)?|uttonDrawInfo(?:Ptr)?)|ChasingArrowsDrawInfo(?:Ptr)?|F(?:ocusRing|rame(?:DrawInfo(?:Ptr)?|Kind))|Gr(?:abberDrawInfo(?:Ptr)?|o(?:upBox(?:DrawInfo(?:Ptr)?|Kind)|wBox(?:DrawInfo(?:Ptr)?|Kind|Size)))|Header(?:DrawInfo(?:Ptr)?|Kind)|Menu(?:BarDrawInfo(?:Ptr)?|DrawInfo(?:Ptr|VersionZero(?:Ptr)?)?|ItemDrawInfo(?:Ptr)?|TitleDrawInfo(?:Ptr)?)|Orientation|P(?:lacardDrawInfo(?:Ptr)?|opupArrowDrawInfo(?:Ptr)?)|S(?:crollBarDelimitersDrawInfo(?:Ptr)?|e(?:gment(?:Adornment|DrawInfo(?:Ptr)?|Kind|Position|Size)|paratorDrawInfo(?:Ptr)?)|plitter(?:Adornment|DrawInfo(?:Ptr)?))|T(?:ab(?:Adornment|DrawInfo(?:VersionZero)?|Kind|P(?:ane(?:Adornment|DrawInfo(?:VersionZero)?)|osition)|Size)|ext(?:BoxOptions|HorizontalFlush|Info|Truncation|VerticalFlush)|ickMarkDrawInfo(?:Ptr)?|rackDrawInfo)|Window(?:DrawInfo(?:Ptr)?|WidgetDrawInfo(?:Ptr)?))|oolbar(?:Display(?:Mode|Size)|ItemRef|Ref)|ypeAndCreator)|View(?:Content(?:Info(?:Ptr)?|Type)|F(?:eatures|rameMetrics)|I(?:D|mageContent(?:Info|Type))|Kind|PartCode|Ref|TrackingArea(?:ID|Ref)|ZOrderOp)|Window(?:Availability|BackingLocation|Depth|Ref|S(?:caleMode|haringType)))|M(?:Cont(?:ent(?:ProvidedType|Request|Type)|rolContent(?:ProcPtr|UPP))|HelpContent(?:Ptr|Rec)?|Menu(?:ItemContent(?:ProcPtr|UPP)|TitleContent(?:ProcPtr|UPP))|StringResType|TagDisplaySide|WindowContent(?:ProcPtr|UPP)|enuBar(?:Header|Menu))|i(?:ghHook(?:ProcPtr|UPP)|liteMenuItemData(?:Ptr)?|tTestHook(?:ProcPtr|UPP))|o(?:mograph(?:Accent|DicInfoRec|Weight)|stCallback(?:Info|_Get(?:BeatAndTempo|MusicalTimeLocation|TransportState(?:2)?))))|I(?:BNibRef|C(?:A(?:ppSpec(?:Handle|List(?:Handle|Ptr)?|Ptr)?|ttr)|CharTable(?:Handle|Ptr)?|F(?:i(?:leSpec(?:Handle|Ptr)?|xedLength)|ontRecord(?:Handle|Ptr)?)|Instance|MapEntry(?:Flags|Handle|Ptr)?|P(?:erm|rofileID(?:Ptr)?)|Service(?:Entry(?:Flags|Handle|Ptr)?|s(?:Handle|Ptr)?))|O(?:A(?:ddressRange|lignment|ppleTimingID|syncC(?:allback(?:0|1|2)?|ompletionContent))|ByteCount(?:32|64)?|C(?:acheMode|o(?:lor(?:Component|Entry)|mpletion(?:ProcPtr|UPP)))|D(?:e(?:tailedTimingInformation(?:V(?:1|2))?|viceNumber)|isplay(?:ModeI(?:D|nformation)|ProductID|ScalerInformation|TimingRange(?:V(?:1|2))?|VendorID))|F(?:B(?:D(?:PLinkConfig|isplayModeDescription)|HDRMetaData(?:V1)?)|ixed(?:1616|Point32)?|ramebufferInformation)|G(?:Bounds|Point|Size)|HardwareCursor(?:Descriptor|Info)|I(?:ndex|temCount)|LogicalAddress|N(?:amedValue|otificationPort(?:Ref)?)|OptionBits|P(?:hysical(?:Address(?:32|64)?|Length(?:32|64)?|Range)|ixel(?:Aperture|Encoding|Information))|Return|S(?:e(?:lect|rvice(?:InterestC(?:allback|ontent(?:64)?)|MatchingCallback))|urface(?:Component(?:Name|Range|Type)|ID|LockOptions|PurgeabilityState|Ref|Subsampling))|TimingInformation|V(?:ersion|irtual(?:Address|Range)))|SAType|con(?:A(?:ction(?:ProcPtr|UPP)|lignmentType)|Family(?:Element|Handle|Ptr|Resource)|Getter(?:ProcPtr|UPP)|Ref|Se(?:lectorValue|rvicesUsageFlags)|TransformType)|n(?:d(?:exToUCString(?:ProcPtr|UPP)|icatorDragConstraint(?:Ptr)?)|strumentChunk(?:Ptr)?|t(?:erfaceTypeList|l(?:0(?:Hndl|Ptr|Rec)|1(?:Hndl|Ptr|Rec)|Text)))|t(?:emReference|l(?:1ExtRec|4(?:Handle|Ptr|Rec)|5Record|b(?:ExtRecord|Record)|cRecord)))|J(?:S(?:C(?:har|lass(?:Attributes|Definition|Ref)|ontext(?:GroupRef|Ref))|GlobalContextRef|Object(?:C(?:allAs(?:ConstructorCallback|FunctionCallback)|onvertToTypeCallback)|FinalizeCallback|GetProperty(?:Callback|NamesCallback)|InitializeCallback|Ref)|Property(?:Attributes|NameA(?:ccumulatorRef|rrayRef))|St(?:atic(?:Function|Value)|ringRef)|Type(?:dArrayBytesDeallocator)?|ValueRef)|apanesePartOfSpeech|ournalInfoBlock|ust(?:DirectionTable|P(?:C(?:Action(?:Subrecord|Type)?|ConditionalAddAction|D(?:ecompositionAction|uctilityAction)|GlyphRepeatAddAction|UnconditionalAddAction)|ostcompTable)|Table|WidthDelta(?:Entry|Group)|ificationFlags))|K(?:C(?:A(?:ttr(?:Type|ibute(?:List)?)|uthType)|C(?:allback(?:Info|ProcPtr|UPP)|ert(?:AddOptions|SearchOptions))|Event(?:Mask)?|Item(?:Attr|Class|Ref)|P(?:rotocolType|ublicKeyHash)|Ref|S(?:earchRef|tatus)|VerifyStopOn)|e(?:r(?:n(?:ArrayOffset|Entry|FormatSpecificHeader|IndexArrayHeader|Kerning(?:Pair|Value)|O(?:ffsetTable(?:Ptr)?|rderedList(?:Entry(?:Ptr)?|Header))|Pair|S(?:impleArrayHeader|tate(?:Entry|Header)|ubtable(?:Header(?:Ptr)?|Info))|Table(?:Format|Header(?:Handle|Ptr)?)?|Version0(?:Header|SubtableHeader))|x(?:A(?:nchorPointAction|rrayOffset)|Co(?:ntrolPoint(?:Action|Entry|Header)|ordinateAction)|FormatSpecificHeader|IndexArrayHeader|KerningPair|OrderedList(?:Entry(?:Ptr)?|Header)|S(?:impleArrayHeader|tate(?:Entry|Header)|ubtable(?:Coverage|Header(?:Ptr)?))|TableHeader(?:Handle|Ptr)?))|y(?:Map(?:ByteArray)?|boardLayout(?:Identifier|Kind|PropertyTag|Ref))))|L(?:A(?:ContextRef|EnvironmentRef|Homograph|Morpheme(?:Bundle|Path|Rec|sArray(?:Ptr)?)?|Property(?:Key|Type))|H(?:Element|Handle|Ptr|Table)|LCStyleInfo|S(?:A(?:cceptanceFlags|pplicationParameters)|HandlerOptions|ItemInfo(?:Flags|Record)|Launch(?:F(?:SRefSpec|lags)|URLSpec)|R(?:equestedInfo|olesMask)|SharedFileList(?:ChangedProcPtr|ItemRef|Re(?:f|solutionFlags)))|aunch(?:Flags|P(?:BPtr|aramBlockRec))|carCaret(?:ClassEntry|Table(?:Ptr)?)|ist(?:Bounds|ClickLoop(?:ProcPtr|UPP)|Def(?:ProcPtr|Spec(?:Ptr)?|Type|UPP)|Handle|Ptr|Re(?:c|f)|Search(?:ProcPtr|UPP))|o(?:cal(?:DateTime(?:Handle|Ptr)?|e(?:AndVariant|NameMask|Operation(?:Class|Variant)|PartMask|Ref))|ngDate(?:Cvt|Field|Rec|Time))|tag(?:StringRange|Table))|M(?:BarHook(?:ProcPtr|UPP)|C(?:Entry(?:Ptr)?|Table(?:Handle|Ptr)?)|D(?:EF(?:Draw(?:Data(?:Ptr)?|ItemsData(?:Ptr)?)|FindItemData(?:Ptr)?|HiliteItemData(?:Ptr)?)|ItemRef|Label(?:Domain|Ref)|Query(?:BatchingParams|Create(?:ResultFunction|ValueFunction)|OptionFlags|Ref|Sort(?:ComparatorFunction|OptionFlags))|S_HANDLE)|IDI(?:C(?:hannelMessage|lientRef|ompletionProc)|D(?:ataChunk(?:Ptr)?|eviceRef)|En(?:dpointRef|tityRef)|IOErrorNotification|MetaEvent|Not(?:eMessage|if(?:ication(?:MessageID)?|y(?:Block|Proc)))|Object(?:AddRemoveNotification|PropertyChangeNotification|Ref|Type)|P(?:acket(?:List)?|ortRef)|R(?:awData|ead(?:Block|Proc))|SysexSendRequest|TimeStamp|UniqueID)|P(?:A(?:ddressSpaceI(?:D|nfo)|reaID)|C(?:o(?:herenceID|nsoleID)|puID|riticalRegionI(?:D|nfo))|DebuggerLevel|E(?:G4ObjectID|vent(?:Flags|I(?:D|nfo))|xceptionKind)|IsFullyInitializedProc|NotificationI(?:D|nfo)|OpaqueID(?:Class)?|P(?:ageSizeClass|rocessID)|QueueI(?:D|nfo)|Remote(?:Context|Procedure)|Semaphore(?:Count|I(?:D|nfo))|T(?:ask(?:I(?:D|nfo(?:Version2)?)|Options|StateKind|Weight)|imerID))|a(?:c(?:Polygon|hine(?:Information(?:Intel64|PowerPC)?|Location))|gicCookieInfo|rker(?:Chunk(?:Ptr)?|IdType)?)|e(?:asureWindowTitleRec(?:Ptr)?|mory(?:ExceptionInformation|ReferenceKind)|nu(?:Attributes|Bar(?:Def(?:ProcPtr|UPP)|H(?:andle|eader)|Menu)|C(?:Rsrc(?:Handle|Ptr)?|ommand)|Def(?:Spec(?:Ptr)?|Type|UPP)|EventOptions|H(?:andle|ook(?:ProcPtr|UPP))|I(?:D|tem(?:Attributes|D(?:ata(?:Flags|Ptr|Rec)|rawing(?:ProcPtr|UPP))|I(?:D|ndex)))|Ref|T(?:itleDrawing(?:ProcPtr|UPP)|racking(?:Data(?:Ptr)?|Mode))))|ixe(?:dModeStateRecord|rDistanceParams)|o(?:dalFilter(?:ProcPtr|UPP|YD(?:ProcPtr|UPP))|r(?:pheme(?:PartOfSpeech|TextRange)|t(?:C(?:hain|ontextualSubtable)|FeatureEntry|InsertionSubtable|Ligature(?:ActionEntry|Subtable)|RearrangementSubtable|S(?:pecificSubtable|ubtable(?:MaskFlags)?|washSubtable)|Table)|x(?:C(?:hain|ontextualSubtable)|InsertionSubtable|LigatureSubtable|RearrangementSubtable|S(?:pecificSubtable|ubtable)|Table))|useTrackingResult)|usic(?:Device(?:Component|GroupID|InstrumentID|MIDIEventProc|NoteParams|S(?:t(?:artNoteProc|dNoteParams|opNoteProc)|ysExProc))|Event(?:Iterator|Type|UserData)|Player|Sequence(?:File(?:Flags|TypeID)|LoadFlags|Type|UserCallback)?|T(?:imeStamp|rack(?:LoopInfo)?)))|N(?:C(?:M(?:ConcatProfileS(?:et|pec)|DeviceProfileInfo)|olor(?:Changed(?:ProcPtr|UPP)|PickerInfo))|DR_record_t|Itl4(?:Handle|Ptr|Rec)|M(?:ProcPtr|Rec(?:Ptr)?|UPP)|PMColor(?:Ptr)?|WidthHook(?:ProcPtr|UPP)|X(?:ByteOrder|Coord|Event(?:Data|Ext(?:ension)?|Ptr|System(?:Device(?:List)?|Info(?:Data|Type)))?|KeyMapping|Mouse(?:Button|Scaling)|Point|S(?:ize|wapped(?:Double|Float))|TabletP(?:ointData(?:Ptr)?|roximityData(?:Ptr)?))|a(?:meTable|noseconds)|ote(?:InstanceID|ParamsControlValue)|u(?:llSt(?:Handle|Ptr|Rec)|m(?:FormatString(?:Rec)?|berParts(?:Ptr)?)))|O(?:S(?:A(?:Active(?:ProcPtr|UPP)|CreateAppleEvent(?:ProcPtr|UPP)|Error|ID|Send(?:ProcPtr|UPP)|syncReference(?:64)?|tomic_int64_aligned64_t)|FifoQueueHead|L(?:A(?:ccessor(?:ProcPtr|UPP)|djustMarks(?:ProcPtr|UPP))|Co(?:mpare(?:ProcPtr|UPP)|unt(?:ProcPtr|UPP))|DisposeToken(?:ProcPtr|UPP)|Get(?:ErrDesc(?:ProcPtr|UPP)|MarkToken(?:ProcPtr|UPP))|Mark(?:ProcPtr|UPP))|NotificationHeader(?:64)?|QueueHead)|ff(?:Pair|set(?:Array(?:Handle|Ptr)?|Table))|p(?:aque(?:A(?:E(?:DataStorageType|StreamRef)|TSU(?:FontFallbacks|Style|TextLayout)|UGraph|reaID|udio(?:Co(?:mponent|nverter)|FileStreamID|Queue(?:ProcessingTap|Timeline)?))|C(?:AClock|M(?:ProfileRef|WorldRef)|o(?:ll(?:atorRef|ection)|ntrolRef))|D(?:CM(?:FoundRecordIterator|Object(?:I(?:D|terator)|Ref))|ialogPtr|ragRef)|E(?:vent(?:H(?:andler(?:CallRef|Ref)|otKeyRef)|LoopRef|QueueRef|Ref|TargetRef)|xtAudioFile)|F(?:CFontDescriptorRef|NSubscriptionRef|S(?:Iterator|VolumeOperation))|GrafPtr|HI(?:ArchiveRef|Object(?:ClassRef|Ref)|ViewTrackingAreaRef)|I(?:BNibRef|CInstance|conRef)|JS(?:C(?:lass|ontext(?:Group)?)|PropertyNameA(?:ccumulator|rray)|String|Value)|KeyboardLayoutRef|L(?:A(?:ContextRef|EnvironmentRef)|SSharedFileList(?:ItemRef|Ref)|ocaleRef)|M(?:P(?:A(?:ddressSpaceID|reaID)|C(?:o(?:herenceID|nsoleID)|puID|riticalRegionID)|EventID|NotificationID|OpaqueID|ProcessID|QueueID|SemaphoreID|T(?:askID|imerID))|enuRef|usic(?:EventIterator|Player|Sequence|Track))|P(?:M(?:P(?:a(?:geFormat|per)|r(?:eset|int(?:Se(?:ssion|ttings)|er)))|Server)|asteboardRef|icker|olicySearchRef)|RgnHandle|S(?:RSpeechObject|crapRef|ec(?:AccessRef|CertificateRef|Identity(?:Ref|SearchRef)|KeyRef|TransformImplementation))|T(?:EC(?:ObjectRef|SnifferObjectRef)|SMDocumentID|XNObject|ext(?:BreakLocatorRef|ToUnicodeInfo)|hemeDrawingState|oolboxObjectClassRef|ranslationRef)|U(?:CTypeSelectRef|RLReference|nicodeToText(?:Info|RunInfo))|W(?:S(?:MethodInvocationRef|ProtocolHandlerRef)|indow(?:GroupRef|Ptr)))|bd(?:SideValues|Table(?:Format)?)|enCPicParams))|P(?:EF(?:ContainerHeader|ExportedSymbol(?:HashSlot|Key)?|Imported(?:Library|Symbol)|Loader(?:InfoHeader|RelocationHeader)|RelocChunk|S(?:ectionHeader|plitHashWord))|M(?:BorderType|ColorSpaceModel|D(?:ataFormat|estinationType|uplexMode)|La(?:nguageInfo|youtDirection)|O(?:bject|rientation)|P(?:PDDomain|a(?:ge(?:Format|ToPaperMappingType)|per(?:Margins|Type)?)|r(?:eset|int(?:DialogOptionFlags|Se(?:ssion|ttings)|er(?:State)?)))|QualityMode|Re(?:ct|solution)|S(?:calingAlignment|erver))|a(?:r(?:am(?:BlockRec|eterEvent)|mBlkPtr)|steboard(?:FlavorFlags|ItemID|PromiseKeeperProcPtr|Ref|S(?:tandardLocation|yncFlags))|t(?:Handle|Ptr|tern))|h(?:oneme(?:Descriptor|Info)|ysicalKeyboardLayoutType)|i(?:c(?:Handle|Ptr|ker(?:MenuItemInfo)?|ture)|x(?:Map(?:Handle|Ptr)?|Pat(?:Handle|Ptr)?))|lotIconRefFlags|oly(?:Handle|Ptr|gon)|r(?:interStatusOpcode|o(?:c(?:InfoType|ess(?:ApplicationTransformState|Info(?:ExtendedRec(?:Ptr)?|Rec(?:Ptr)?)))|gressTrackInfo|miseHFSFlavor|p(?:CharProperties|LookupS(?:egment|ingle)|Table|erty(?:Creator|Tag)))))|Q(?:D(?:Arc(?:ProcPtr|UPP)|Bits(?:ProcPtr|UPP)|Comment(?:ProcPtr|UPP)|Err|GetPic(?:ProcPtr|UPP)|JShieldCursor(?:ProcPtr|UPP)|Line(?:ProcPtr|UPP)|O(?:pcode(?:ProcPtr|UPP)|val(?:ProcPtr|UPP))|P(?:oly(?:ProcPtr|UPP)|rinterStatus(?:ProcPtr|UPP)|utPic(?:ProcPtr|UPP))|R(?:Rect(?:ProcPtr|UPP)|e(?:ct(?:ProcPtr|UPP)|gionParseDirection)|gn(?:ProcPtr|UPP))|StdGlyphs(?:ProcPtr|UPP)|T(?:ext(?:ProcPtr|UPP)|xMeas(?:ProcPtr|UPP)))|Elem(?:Ptr)?|Hdr(?:Ptr)?|L(?:Preview(?:PDFStyle|RequestRef)|ThumbnailRe(?:f|questRef))|Types)|R(?:DFlagsType|GBColor|OTA(?:GlyphEntry|Header)|TAType|e(?:ad(?:BytesFDF|Packet(?:DataFDF|sFDF))|drawBackground(?:ProcPtr|UPP)|gi(?:onToRects(?:ProcPtr|UPP)|ster(?:Information(?:Intel64|PowerPC)?|edComponent(?:InstanceRecord(?:Ptr)?|Record(?:Ptr)?)))|s(?:Attributes|Err(?:ProcPtr|UPP)|File(?:Attributes|RefNum)|ID|ource(?:Count|EndianFilterPtr|Index|Spec)))|gnHandle|outin(?:e(?:Descriptor(?:Handle|Ptr)?|FlagsType|Record(?:Handle|Ptr)?)|g(?:Flags|Resource(?:Entry|Handle|Ptr)))|srcChainLocation|uleBasedTrslRecord)|S(?:C(?:Bond(?:InterfaceRef|StatusRef)|DynamicStore(?:C(?:allBack|ontext)|Ref)|Network(?:Connection(?:C(?:allBack|ontext)|Flags|PPPStatus|Ref|Status)|InterfaceRef|ProtocolRef|Reachability(?:C(?:allBack|ontext)|Flags|Ref)|Se(?:rviceRef|tRef))|Preferences(?:C(?:allBack|ontext)|Notification|Ref)|VLANInterfaceRef)|FNTLookup(?:ArrayHeader|BinarySearchHeader|FormatSpecificHeader|Kind|Offset|S(?:egment(?:Header)?|ingle(?:Header)?)|T(?:able(?:Format|Handle|Ptr)?|rimmedArrayHeader)|V(?:alue|ectorHeader))|Int|K(?:Document(?:I(?:D|ndexState)|Ref)|Index(?:DocumentIteratorRef|Ref|Type)|S(?:earch(?:GroupRef|Options|Re(?:f|sults(?:FilterCallBack|Ref))|Type)|ummaryRef))|MPTETime(?:Flags|Type)?|R(?:CallBack(?:P(?:aram|rocPtr)|Struct|UPP)|Language(?:Model|Object)|P(?:ath|hrase)|Re(?:cogni(?:tion(?:Result|System)|zer)|jectionLevel)|Spee(?:ch(?:Object|Source)|dSetting)|Word)|SL(?:Authenticate|C(?:ipher(?:Suite|suiteGroup)|lientCertificateState|on(?:nection(?:Ref|Type)|text(?:Ref)?))|Protocol(?:Side)?|ReadFunc|Session(?:Option|State)|WriteFunc)|T(?:Class(?:Table)?|E(?:lement|ntry(?:Index|One|Two|Zero))|H(?:andle|eader)|Ptr|X(?:Class(?:Table)?|Entry(?:Index|One|Two|Zero)|Header|StateIndex))|c(?:hedule(?:dAudio(?:FileRegion(?:CompletionProc)?|Slice(?:CompletionProc)?)|rInfoRec(?:Ptr)?)|r(?:ap(?:Flavor(?:Flags|Info|Type)|PromiseKeeper(?:ProcPtr|UPP)|Ref|T(?:ranslationList(?:Handle|Ptr)?|ype(?:Spec)?))|ipt(?:CodeRun(?:Ptr)?|Language(?:Record|Support(?:Handle|Ptr)?)|TokenType|ingComponentSelector)|oll(?:BarTrackInfo|WindowOptions)|pST(?:Element|Table)))|e(?:c(?:A(?:CLRef|FPServerSignature|ccess(?:Control(?:CreateFlags|Ref)|OwnerType|Ref)|sn1(?:AlgId|Item|Oid|PubKeyInfo|Template(?:Chooser(?:Ptr)?|_struct)?)|uthenticationType)|C(?:S(?:DigestAlgorithm|Flags)|ertificateRef|ode(?:Ref|S(?:ignatureFlags|tatus))|redentialType)|External(?:Format|ItemType)|G(?:roupTransformRef|uestRef)|I(?:dentity(?:Ref|SearchRef)|tem(?:Attr|Class|ImportExport(?:Flags|KeyParameters)))|Key(?:GeneratePairBlock|ImportExport(?:Flags|Parameters)|OperationType|Ref|Sizes|Usage|chain(?:Attr(?:Type|ibute(?:Info|List|Ptr)?)|Callback(?:Info)?|Event(?:Mask)?|ItemRef|PromptSelector|Ref|S(?:e(?:archRef|ttings)|tatus)))|MessageBlock|P(?:a(?:dding|sswordRef)|olicy(?:Ref|SearchRef)|r(?:eferencesDomain|otocolType)|ublicKeyHash)|R(?:andomRef|equirement(?:Ref|Type))|StaticCodeRef|T(?:askRef|r(?:ansform(?:A(?:ctionBlock|ttribute(?:ActionBlock|Ref))|CreateFP|DataBlock|I(?:mplementationRef|nstanceBlock)|MetaAttributeType|Ref|StringOrAttributeRef)|ust(?:Callback|OptionFlags|Re(?:f|sultType)|Settings(?:Domain|KeyUsage|Result)|WithErrorCallback|edApplicationRef)))|uritySessionId)|lectorFunction(?:ProcPtr|UPP)|ssion(?:AttributeBits|CreationFlags)|t(?:PropertyFDF|UserDataFDF|upWindowProxyDragImageRec))|izeResourceRec(?:Handle|Ptr)?|l(?:eepQ(?:ProcPtr|Rec(?:Ptr)?|UPP)|iderTrackInfo)|ound(?:DataChunk(?:Ptr)?|ProcPtr|UPP)|peech(?:Channel(?:Record)?|Done(?:ProcPtr|UPP)|Error(?:CFProcPtr|Info|ProcPtr|UPP)|Phoneme(?:ProcPtr|UPP)|S(?:tatusInfo|ync(?:ProcPtr|UPP))|TextDone(?:ProcPtr|UPP)|VersionInfo|Word(?:CFProcPtr|ProcPtr|UPP)|XtndData)|t(?:Scrp(?:Handle|Ptr|Rec)|a(?:geList|ndard(?:DropLocation|IconListCellData(?:Ptr|Rec)))|ring(?:2DateStatus|ToDateStatus)|yle(?:Run|Table))|ys(?:PPtr|tem(?:SoundID|UI(?:Mode|Options))))|T(?:E(?:C(?:BufferContextRec|Conver(?:sionInfo|terContextRec)|Encoding(?:Pair(?:Rec|s(?:Handle|Ptr|Rec)?)|sList(?:Handle|Ptr|Rec))|In(?:fo(?:Handle|Ptr)?|ternetName(?:Rec|UsageMask|s(?:Handle|Ptr|Rec)))|Locale(?:ListToEncodingList(?:Ptr|Rec)|ToEncodingsList(?:Handle|Ptr|Rec))|ObjectRef|Plugin(?:C(?:lear(?:ContextInfoPtr|SnifferContextInfoPtr)|onvertTextEncodingPtr)|Disp(?:atchTable|oseEncoding(?:ConverterPtr|SnifferPtr))|FlushConversionPtr|Get(?:Count(?:Available(?:SniffersPtr|TextEncoding(?:PairsPtr|sPtr))|DestinationTextEncodingsPtr|MailEncodingsPtr|SubTextEncodingsPtr|WebEncodingsPtr)|PluginDispatchTablePtr|TextEncoding(?:FromInternetNamePtr|InternetNamePtr))|NewEncoding(?:ConverterPtr|SnifferPtr)|S(?:ig(?:nature)?|niffTextEncodingPtr|tateRec)|Version)|S(?:niffer(?:ContextRec|ObjectRef)|ubTextEncoding(?:Rec|s(?:Handle|Ptr|Rec)))|lickLoop(?:ProcPtr|UPP))|DoText(?:ProcPtr|UPP)|FindWord(?:ProcPtr|UPP)|Handle|IntHook|Ptr|Rec(?:alc(?:ProcPtr|UPP))?|Style(?:Handle|Ptr|Rec|Table))|ISInputSourceRef|MTask(?:Ptr)?|S(?:Code|M(?:Doc(?:AccessAttributes|ument(?:I(?:D|nterfaceType)|PropertyTag))|GlyphInfo(?:Array)?)|criptingSizeResource)|X(?:N(?:A(?:TSUI(?:Features|Variations)|ction(?:Key(?:Mapper(?:ProcPtr|UPP))?|NameMapper(?:ProcPtr|UPP))|ttributeData|utoScrollBehavior)|Background(?:Data|Type)?|C(?:arbonEventInfo|o(?:mmandEventSupportOptions|nt(?:extualMenuSetup(?:ProcPtr|UPP)|inuousFlags|rol(?:Data|Tag))|untOptions))|D(?:ataType|rawItems)|Errors|F(?:eatureBits|i(?:leType|nd(?:ProcPtr|UPP))|rame(?:ID|Options|Type))|HyperLinkState|LongRect|Ma(?:rgins|tch(?:Options|TextRecord))|O(?:bject(?:Refcon)?|ffset)|PermanentTextEncodingType|RectKey|Scroll(?:Bar(?:Orientation|State)|Info(?:ProcPtr|UPP)|Unit)|T(?:ab(?:Type)?|ype(?:Attributes|RunAttribute(?:Sizes|s)))|VersionValue)|TNTag)|a(?:ble(?:DirectoryRecord|tP(?:oint(?:Rec|erRec)|roximityRec))|sk(?:Proc|Storage(?:Index|Value)))|ext(?:BreakLocatorRef|Chunk(?:Ptr)?|Encoding(?:Base|Format|NameSelector|R(?:ec|un(?:Ptr)?)|Variant)?|Ptr|Range(?:Array(?:Handle|Ptr)?|Handle|Ptr)?|S(?:ervice(?:Class|Info(?:Ptr)?|List(?:Handle|Ptr)?|Property(?:Tag|Value))|tyle(?:Handle|Ptr)?)|ToUnicodeInfo|WidthHook(?:ProcPtr|UPP))|h(?:eme(?:ArrowOrientation|B(?:ackgroundKind|rush|utton(?:Adornment|Draw(?:Info(?:Ptr)?|ProcPtr|UPP)|Kind|Value))|C(?:heckBoxStyle|ursor)|Dra(?:gSoundKind|w(?:State|ingState))|Erase(?:ProcPtr|UPP)|FontID|GrowDirection|Iterator(?:ProcPtr|UPP)|Me(?:nu(?:BarState|ItemType|State|Type)|tric)|PopupArrowSize|S(?:crollBar(?:ArrowStyle|ThumbStyle)|oundKind)|T(?:ab(?:Direction|Style|TitleDraw(?:ProcPtr|UPP))|extColor|humbDirection|itleBarWidget|rack(?:Attributes|DrawInfo|EnableState|Kind|PressState))|Window(?:Attributes|Metrics(?:Ptr)?|Type))|read(?:Entry(?:ProcPtr|TPP|UPP)|ID|Options|S(?:cheduler(?:ProcPtr|TPP|UPP)|t(?:ate|yle)|witch(?:ProcPtr|TPP|UPP))|T(?:askRef|ermination(?:ProcPtr|TPP|UPP))))|imer(?:ProcPtr|UPP)|o(?:ggle(?:PB|Results)|ken(?:Block(?:Ptr)?|Re(?:c(?:Ptr)?|sults))|olboxObjectClassRef)|r(?:a(?:k(?:Table(?:Data|Entry)?|Value)|ns(?:itionWindowOptions|lation(?:Attributes|Flags|Ref(?:Num)?)))|ipleInt|uncCode)|ype(?:SelectRecord|sBlock(?:Ptr)?))|U(?:AZoomChangeFocusType|C(?:C(?:harProperty(?:Type|Value)|ollat(?:eOptions|ionValue))|Key(?:CharSeq|LayoutFeatureInfo|ModifiersToTableNum|Output|S(?:equenceDataIndex|tate(?:Entry(?:Range|Terminal)|Record(?:sIndex)?|Terminators))|ToCharTableIndex|board(?:Layout|TypeHeader))|T(?:SWalkDirection|extBreak(?:Options|Type)|ypeSelect(?:CompareResult|Options|Ref)))|Int|NDServerRef|RL(?:CallbackInfo|Event(?:Mask)?|Notify(?:ProcPtr|UPP)|OpenFlags|Reference|S(?:tate|ystemEvent(?:ProcPtr|UPP)))|TCDateTime(?:Handle|Ptr)?|n(?:i(?:CharArray(?:Handle|Offset|Ptr)|code(?:Map(?:Version|ping(?:Ptr)?)|ToText(?:Fallback(?:ProcPtr|UPP)|Info|RunInfo)))|tokenTable(?:Handle|Ptr)?)|ser(?:EventUPP|Item(?:ProcPtr|UPP)))|V(?:DGam(?:RecPtr|maRecord)|ector(?:128(?:Intel)?|Information(?:Intel64|PowerPC)?)|o(?:ice(?:Description|FileInfo|Spec(?:Ptr)?)|l(?:MountInfo(?:Header|Ptr)|ume(?:MountInfoHeader(?:Ptr)?|Type))))|W(?:CTab(?:Handle|Ptr)|S(?:ClientContext(?:CopyDescriptionCallBackProcPtr|Re(?:leaseCallBackProcPtr|tainCallBackProcPtr))?|MethodInvocationRef|ProtocolHandler(?:DeserializationProcPtr|Ref|SerializationProcPtr)|TypeID|tateData(?:Handle|Ptr)?)|i(?:d(?:eChar(?:Arr)?|thHook(?:ProcPtr|UPP))|n(?:CTab|dow(?:A(?:ctivationScope|ttributes)|C(?:lass|onstrainOptions)|D(?:ef(?:PartCode|Spec(?:Ptr)?|Type|UPP)|rawerState)|Group(?:Attributes|ContentOptions|Ref)|LatentVisibility|Modality|P(?:a(?:int(?:Proc(?:Options|Ptr)|UPP)|rtCode)|ositionMethod|tr)|Re(?:f|gionCode)|T(?:itleDrawing(?:ProcPtr|UPP)|ransition(?:Action|Effect)))))|ordBreak(?:ProcPtr|UPP)|rit(?:e(?:BytesFDF|PacketsFDF)|ingCode))|XLib(?:ContainerHeader|ExportedSymbol(?:HashSlot|Key)?)|ZoomAcceleration|a(?:cl_(?:entry_(?:id_t|t)|flag(?:_t|set_t)|perm(?:_t|set_(?:mask_t|t))|t(?:ag_t|ype_t)?)|ddr64_t|larm_(?:port_t|t(?:ype_t)?)|rcade_register_t|u(?:_(?:as(?:flgs_t|id_t)|c(?:lass_t|tlmode_t)|e(?:mod_t|v(?:class_map(?:_t)?|ent_t)|xpire_after(?:_t)?)|fstat_t|id_t|mask(?:_t)?|qctrl(?:_t)?|s(?:ession(?:_t)?|tat_t)|t(?:id(?:_(?:addr(?:_t)?|t))?|oken))|dit(?:_(?:fstat|stat|token_t)|info(?:_(?:addr(?:_t)?|t))?|pinfo(?:_(?:addr(?:_t)?|t))?)))|b(?:oo(?:lean_t|tstrap_t)|uild_(?:tool_version|version_command))|c(?:cntTokenRec(?:Handle|Ptr|ord)|lock_(?:attr_t|ctrl_(?:port_t|t)|flavor_t|id_t|re(?:ply_t|s_t)|serv_(?:port_t|t))|msghdr|oalition_t|pu_(?:subtype_t|t(?:hreadtype_t|ype_t))|ssm_(?:a(?:cl_(?:keychain_prompt_selector|process_subject_selector)|pple(?:cspdl_db_(?:change_password_parameters|is_locked_parameters|settings_parameters)|dl_open_parameters(?:_mask)?)|uthorizationgroup)|csp_operational_statistics|d(?:at(?:a|e)|b_schema_index_info|l_(?:db_handle|pkcs11_attributes))|func_name_addr|guid|k(?:ey_size|r_name)|list(?:_element)?|memory_funcs|name_list|parsed_c(?:ert|rl)|query_size_data|range|tp_result_set|version))|d(?:ata_in_code_entry|ec(?:form|imal)|y(?:l(?:d_(?:info_command|kernel_(?:image_info(?:_(?:array_t|t))?|process_info(?:_t)?))|i(?:b(?:_(?:command|module(?:_64)?|reference|table_of_contents))?|nker_command))|symtab_command))|e(?:mulation_vector_t|n(?:cryption_info_command(?:_64)?|try_point_command)|vsio(?:Keymapping|MouseScaling)|x(?:ception_(?:behavior_(?:array_t|t)|data_t(?:ype_t)?|flavor_array_t|handler_(?:array_t|t)|mask_(?:array_t|t)|port_(?:arra(?:ry_t|y_t)|t)|type_t)|tension_data_format))|f(?:e(?:nv_t|xcept_t)|pos_t|vm(?:file_command|lib(?:_command)?))|gpu_energy_data(?:_t)?|h(?:ash_info_bucket(?:_(?:array_t|t))?|ost_(?:basic_info(?:_(?:data_t|t))?|c(?:an_has_debugger_info(?:_(?:data_t|t))?|pu_load_info(?:_(?:data_t|t))?)|flavor_t|info(?:64_t|_(?:data_t|t))|load_info(?:_(?:data_t|t))?|name_(?:port_t|t)|p(?:r(?:eferred_user_arch(?:_(?:data_t|t))?|i(?:ority_info(?:_(?:data_t|t))?|v_t))|urgable_info_(?:data_t|t))|s(?:ched_info(?:_(?:data_t|t))?|ecurity_t)|t))|i(?:386_(?:exception_state_t|float_state_t|thread_state_t)|dent_command|maxdiv_t|nteger_t|o_(?:async_ref(?:64_t|_t)|buf_ptr_t|connect_t|enumerator_t|iterator_t|master_t|name_t|object_t|registry_entry_t|s(?:calar_inband(?:64_t|_t)|ervice_t|t(?:at_(?:entry|info(?:_t)?)|r(?:ing_(?:inband_t|t)|uct_inband_t)))|user_(?:reference_t|scalar_t))|pc_(?:info_(?:name(?:_(?:array_t|t))?|space(?:_(?:basic(?:_t)?|t))?|tree_name(?:_(?:array_t|t))?)|space_(?:inspect_t|port_t|t)|voucher_(?:attr_(?:control_t|manager_t)|t)))|k(?:auth_(?:ac(?:e(?:_(?:rights_t|t))?|l(?:_t)?)|cache_sizes|filesec(?:_t)?|identity_extlookup)|ern(?:_return_t|el_(?:boot_info_t|resource_sizes(?:_(?:data_t|t))?|version_t))|mod_(?:args_t|control_flavor_t|info(?:_(?:32_v1(?:_t)?|64_v1(?:_t)?|array_t|t))?|reference(?:_t)?|st(?:art_func_t|op_func_t)|t)|object_description_t)|l(?:a(?:belstr_t|unch_data_(?:dict_iterator_t|t(?:ype_t)?))|edger_(?:a(?:mount_t|rray_t)|item_t|port_(?:array_t|t)|t)|in(?:ger|ke(?:dit_data_command|r_option_command))|o(?:ad_command|ck(?:_set_(?:port_t|t)|group_info(?:_(?:array_t|t))?)))|m(?:a(?:ch_(?:core_(?:details|fileheader)|dead_name_notification_t|e(?:rror_(?:fn_t|t)|xception_(?:code_t|data_t(?:ype_t)?|subcode_t))|header(?:_64)?|m(?:emory_info(?:_(?:array_t|t))?|sg_(?:audit_trailer_t|b(?:ase_t|its_t|ody_t)|co(?:ntext_trailer_t|py_options_t)|descriptor_t(?:ype_t)?|empty_(?:rcv_t|send_t|t)|format_0_trailer_t|guard(?:_flags_t|ed_port_descriptor(?:32_t|64_t|_t))|header_t|id_t|ma(?:c_trailer_t|x_trailer_t)|o(?:ol_(?:descriptor(?:32_t|64_t|_t)|ports_descriptor(?:32_t|64_t|_t))|ption(?:_t|s_t))|p(?:ort_descriptor_t|riority_t)|return_t|s(?:e(?:curity_trailer_t|qno_trailer_t)|ize_t)|t(?:imeout_t|railer_(?:info_t|size_t|t(?:ype_t)?)|ype_(?:descriptor_t|n(?:ame_t|umber_t)|size_t))))|no_senders_notification_t|port_(?:array_t|context_t|de(?:l(?:eted_notification_t|ta_t)|stroyed_notification_t)|flavor_t|guard_exception_codes|info_(?:ext(?:_t)?|t)|limits(?:_t)?|ms(?:count_t|gcount_t)|name_(?:array_t|t)|options(?:_(?:ptr_t|t))?|qos(?:_t)?|right(?:_t|s_t)|s(?:eqno_t|rights_t|tatus(?:_t)?)|type_(?:array_t|t)|urefs_t)|send_(?:once_notification_t|possible_notification_t)|t(?:ask_basic_info(?:_(?:data_t|t))?|imespec(?:_t)?)|v(?:m_(?:address_t|info_region(?:_t)?|offset_t|read_entry(?:_t)?|size_t)|oucher_(?:attr_(?:co(?:mmand_t|nt(?:ent_(?:size_t|t)|rol_(?:flags_t|t)))|importance_refs|key_(?:array_t|t)|manager_t|r(?:aw_recipe_(?:array_(?:size_t|t)|size_t|t)|ecipe_(?:command_(?:array_t|t)|data(?:_t)?|size_t|t))|value_(?:flags_t|handle_(?:array_(?:size_t|t)|t)|reference_t))|name_(?:array_t|t)|selector_t|t))|zone_(?:info_(?:array_t|data|t)|name(?:_(?:array_t|t))?))|trix_(?:double(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))|float(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))))|context_t|em(?:_entry_name_port_t|ory_object_(?:a(?:rray_t|ttr_info(?:_(?:data_t|t))?)|behave_info(?:_(?:data_t|t))?|c(?:luster_size_t|o(?:ntrol_t|py_strategy_t))|default_t|f(?:ault_info_t|lavor_t)|info_(?:data_t|t)|name_t|offset_t|perf_info(?:_(?:data_t|t))?|return_t|size_t|t))|ig_(?:impl_routine_t|r(?:eply_error_t|outine_(?:arg_descriptor_t|descriptor(?:_t)?|t))|s(?:erver_routine_t|tub_routine_t|ubsystem(?:_t)?|ymtab(?:_t)?))|sg(?:_labels_t|hdr))|n(?:atural_t|ot(?:e_command|ify_port_t)|space_path_t|tsid_t)|os_(?:block_t|function_t|log_(?:s|t(?:ype_t)?)|trace_payload_(?:object_t|t)|unfair_lock_s)|p(?:a(?:cked_(?:char(?:16|2|32|4|64|8)|double(?:2|4|8)|float(?:16|2|4|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)|u(?:char(?:16|2|32|4|64|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)))|ge_address_array_t)|icker|o(?:inter_t|licy_(?:base(?:_(?:data_t|t)|s)|fifo_(?:base(?:_(?:data_t|t))?|info(?:_(?:data_t|t))?|limit(?:_(?:data_t|t))?)|info(?:_(?:data_t|t)|s)|limit(?:_(?:data_t|t)|s)|rr_(?:base(?:_(?:data_t|t))?|info(?:_(?:data_t|t))?|limit(?:_(?:data_t|t))?)|t(?:imeshare_(?:base(?:_(?:data_t|t))?|info(?:_(?:data_t|t))?|limit(?:_(?:data_t|t))?))?))|pnum_t|r(?:eb(?:ind_cksum_command|ound_dylib_command)|ocessor_(?:array_t|basic_info(?:_(?:data_t|t))?|cpu_load_info(?:_(?:data_t|t))?|flavor_t|info_(?:array_t|data_t|t)|port_(?:array_t|t)|set_(?:array_t|basic_info(?:_(?:data_t|t))?|control_(?:port_t|t)|flavor_t|info_(?:data_t|t)|load_info(?:_(?:data_t|t))?|name_(?:array_t|port_(?:array_t|t)|t)|port_t|t)|t)))|qos_class_t|r(?:e(?:g(?:64_t|isterSelectorType)|lop)|outine(?:_(?:arg_(?:descriptor(?:_t)?|offset|size|type)|descriptor(?:_t)?)|s_command(?:_64)?)|p(?:ath_command|c_(?:routine_(?:arg_descriptor(?:_t)?|descriptor(?:_t)?)|s(?:ignature|ubsystem(?:_t)?))))|s(?:a(?:_endpoints(?:_t)?|e_(?:associd_t|connid_t))|e(?:c(?:_(?:certificate(?:_t)?|identity(?:_t)?|object(?:_t)?|protocol_(?:challenge_(?:complete_t|t)|key_update_(?:complete_t|t)|metadata(?:_t)?|options(?:_t)?|pre_shared_key_selection_(?:complete_t|t)|verify_(?:complete_t|t))|trust(?:_t)?)|tion(?:_64)?|urity_token_t)|gment_command(?:_64)?|maphore_(?:port_t|t))|f(?:_hdtr|nt(?:CMap(?:E(?:ncoding|xtendedSubHeader)|Header|SubHeader)|D(?:escriptorHeader|irectory(?:Entry)?)|F(?:eature(?:Header|Name)|ont(?:Descriptor|FeatureSetting|RunFeature))|Instance|Name(?:Header|Record)|Variation(?:Axis|Header)))|i(?:md_(?:bool|char(?:1(?:6)?|2|3(?:2)?|4|64|8)|double(?:1|2|3|4|8)|float(?:1(?:6)?|2|3|4|8)|int(?:1(?:6)?|2|3|4|8)|long(?:1|2|3|4|8)|packed_(?:char(?:16|2|32|4|64|8)|double(?:2|4|8)|float(?:16|2|4|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)|u(?:char(?:16|2|32|4|64|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)))|short(?:1(?:6)?|2|3(?:2)?|4|8)|u(?:char(?:1(?:6)?|2|3(?:2)?|4|64|8)|int(?:1(?:6)?|2|3|4|8)|long(?:1|2|3|4|8)|short(?:1(?:6)?|2|3(?:2)?|4|8)))|nt(?:16|32|64|8))|leep_type_t|o(?:_np_extensions|ck(?:addr(?:_storage)?|proto)|urce_version_command)|u(?:b_(?:client_command|framework_command|library_command|umbrella_command)|id_cred_(?:path_t|t|uid_t))|y(?:m(?:seg_command|tab_(?:command|name_t))|nc_policy_t))|t(?:ask_(?:a(?:bsolutetime_info(?:_(?:data_t|t))?|ffinity_tag_info(?:_(?:data_t|t))?|rray_t)|basic_info(?:_(?:32(?:_(?:data_t|t))?|64(?:_(?:data_t|t))?|data_t|t))?|category_policy(?:_(?:data_t|t))?|dyld_info(?:_(?:data_t|t))?|e(?:vents_info(?:_(?:data_t|t))?|x(?:c_guard_behavior_t|tmod_info(?:_(?:data_t|t))?))|fla(?:gs_info(?:_(?:data_t|t))?|vor_t)|in(?:fo_(?:data_t|t)|spect_(?:basic_counts(?:_(?:data_t|t))?|flavor(?:_t)?|info_t|t))|kernelmemory_info(?:_(?:data_t|t))?|latency_qos(?:_t)?|name_t|p(?:o(?:licy_(?:flavor_t|t)|rt_(?:array_t|t)|wer_info(?:_(?:data_t|t|v2(?:_(?:data_t|t))?))?)|urgable_info_t)|qos_policy(?:_t)?|role(?:_t)?|s(?:pecial_port_t|uspension_token_t)|t(?:hr(?:ead_times_info(?:_(?:data_t|t))?|oughput_qos(?:_t)?)|race_memory_info(?:_(?:data_t|t))?)?|vm_info(?:_(?:data_t|t))?|wait_state_info(?:_(?:data_t|t))?|zone_info_(?:array_t|data|t))|hread_(?:a(?:ct_(?:array_t|port_(?:array_t|t)|t)|ffinity_policy(?:_(?:data_t|t))?|rray_t)|ba(?:ckground_policy(?:_(?:data_t|t))?|sic_info(?:_(?:data_t|t))?)|command|extended_(?:info(?:_(?:data_t|t))?|policy(?:_(?:data_t|t))?)|flavor_t|i(?:dentifier_info(?:_(?:data_t|t))?|n(?:fo_(?:data_t|t)|spect_t))|latency_qos_(?:policy(?:_(?:data_t|t))?|t)|p(?:o(?:licy_(?:flavor_t|t)|rt_(?:array_t|t))|recedence_policy(?:_(?:data_t|t))?)|sta(?:ndard_policy(?:_(?:data_t|t))?|te_(?:data_t|flavor_(?:array_t|t)|t))|t(?:hroughput_qos_(?:policy(?:_(?:data_t|t))?|t)|ime_constraint_policy(?:_(?:data_t|t))?)?)|ime_value(?:_t)?|l(?:s_(?:ciphersuite_(?:group_t|t)|protocol_version_t)|v_descriptor)|oken_t|wolevel_hint(?:s_command)?)|u(?:ext_object_t|int(?:16|32|64|8)|pl_t|ser_subsystem_t|uid_(?:command|string_t))|v(?:e(?:ctor_(?:char(?:16|2|3(?:2)?|4|8)|double(?:2|3|4|8)|float(?:16|2|3|4|8)|int(?:16|2|3|4|8)|long(?:1|2|3|4|8)|short(?:16|2|3(?:2)?|4|8)|u(?:char(?:16|2|3(?:2)?|4|8)|int(?:16|2|3|4|8)|long(?:1|2|3|4|8)|short(?:16|2|3(?:2)?|4|8)))|rsion_min_command)|fs_path_t|irtual_memory_guard_exception_codes|m(?:32_object_id_t|_(?:address_t|behavior_t|extmod_statistics(?:_(?:data_t|t))?|in(?:fo_(?:object(?:_(?:array_t|t))?|region(?:_(?:64(?:_t)?|t))?)|herit_t)|ma(?:chine_attribute_(?:t|val_t)|p_(?:address_t|offset_t|size_t|t))|named_entry_t|o(?:bject_(?:id_t|offset_t|size_t)|ffset_t)|p(?:age_info_(?:basic(?:_(?:data_t|t))?|data_t|flavor_t|t)|rot_t|urg(?:able_t|eable_(?:info(?:_t)?|stat(?:_t)?)))|re(?:ad_entry(?:_t)?|gion_(?:basic_info(?:_(?:64(?:_t)?|data_(?:64_t|t)|t))?|extended_info(?:_(?:data_t|t))?|flavor_t|info_(?:64_t|data_t|t)|recurse_info_(?:64_t|t)|submap_(?:info(?:_(?:64(?:_t)?|data_(?:64_t|t)|t))?|short_info_(?:64(?:_t)?|data_64_t))|top_info(?:_(?:data_t|t))?))|s(?:ize_t|tatistics(?:64(?:_(?:data_t|t))?|_(?:data_t|t))?|ync_t)|task_entry_t))|o(?:idPtr|ucher_mach_msg_state_(?:s|t)))|x(?:86_(?:avx(?:512_state(?:32_t|64_t|_t)?|_state(?:32_t|64_t|_t)?)|debug_state(?:32_t|64_t|_t)?|exception_state(?:32_t|64_t|_t)?|float_state(?:32_t|64_t|_t)?|pagein_state_t|state_hdr(?:_t)?|thread_(?:full_state64_t|state(?:32_t|64_t|_t)?))|pc_(?:activity_(?:handler_t|state_t|t)|connection_(?:handler_t|t)|endpoint_t|finalizer_t|handler_t|object_t|type_t))|zone_(?:btrecord(?:_(?:array_t|t))?|info(?:_(?:array_t|t))?|name(?:_(?:array_t|t))?))\\b", + "name": "support.type.c" + }, { "match": "\\b(?:CF(?:A(?:bsoluteTime|llocator(?:AllocateCallBack|Co(?:ntext|pyDescriptionCallBack)|DeallocateCallBack|PreferredSizeCallBack|Re(?:allocateCallBack|f|leaseCallBack|tainCallBack))|rray(?:ApplierFunction|C(?:allBacks|opyDescriptionCallBack)|EqualCallBack|Re(?:f|leaseCallBack|tainCallBack))|ttributedStringRef)|B(?:ag(?:ApplierFunction|C(?:allBacks|opyDescriptionCallBack)|EqualCallBack|HashCallBack|Re(?:f|leaseCallBack|tainCallBack))|i(?:naryHeap(?:ApplierFunction|C(?:allBacks|ompareContext)|Ref)|t(?:VectorRef)?)|ooleanRef|undleRef(?:Num)?|yteOrder)|C(?:alendar(?:Identifier|Ref|Unit)|haracterSet(?:PredefinedSet|Ref)|ompar(?:atorFunction|isonResult))|D(?:at(?:a(?:Ref|SearchFlags)|e(?:Formatter(?:Key|Ref|Style)|Ref))|ictionary(?:ApplierFunction|CopyDescriptionCallBack|EqualCallBack|HashCallBack|KeyCallBacks|Re(?:f|leaseCallBack|tainCallBack)|ValueCallBacks))|Error(?:Domain|Ref)|File(?:Descriptor(?:C(?:allBack|ontext)|NativeDescriptor|Ref)|Security(?:ClearOptions|Ref))|GregorianUnitFlags|HashCode|I(?:SO8601DateFormatOptions|ndex)|Locale(?:Identifier|Key|LanguageDirection|Ref)|M(?:achPort(?:C(?:allBack|ontext)|InvalidationCallBack|Ref)|essagePort(?:C(?:allBack|ontext)|InvalidationCallBack|Ref)|utable(?:A(?:rrayRef|ttributedStringRef)|B(?:agRef|itVectorRef)|CharacterSetRef|D(?:ataRef|ictionaryRef)|S(?:etRef|tringRef)))|N(?:otification(?:C(?:allback|enterRef)|Name|SuspensionBehavior)|u(?:llRef|mber(?:Formatter(?:Key|OptionFlags|PadPosition|R(?:ef|oundingMode)|Style)|Ref|Type)))|OptionFlags|P(?:lugIn(?:DynamicRegisterFunction|FactoryFunction|Instance(?:DeallocateInstanceDataFunction|GetInterfaceFunction|Ref)|Ref|UnloadFunction)|ropertyList(?:Format|MutabilityOptions|Ref))|R(?:ange|eadStream(?:ClientCallBack|Ref)|unLoop(?:Activity|Mode|Observer(?:C(?:allBack|ontext)|Ref)|R(?:ef|unResult)|Source(?:Context(?:1)?|Ref)|Timer(?:C(?:allBack|ontext)|Ref)))|S(?:et(?:ApplierFunction|C(?:allBacks|opyDescriptionCallBack)|EqualCallBack|HashCallBack|Re(?:f|leaseCallBack|tainCallBack))|ocket(?:C(?:allBack(?:Type)?|ontext)|Error|NativeHandle|Ref|Signature)|tr(?:eam(?:ClientContext|E(?:rror(?:Domain)?|ventType)|PropertyKey|Status)|ing(?:BuiltInEncodings|CompareFlags|Encoding(?:s)?|InlineBuffer|NormalizationForm|Ref|Tokenizer(?:Ref|TokenType)))|wappedFloat(?:32|64))|T(?:ime(?:Interval|Zone(?:NameStyle|Ref))|ree(?:ApplierFunction|Co(?:ntext|pyDescriptionCallBack)|Re(?:f|leaseCallBack|tainCallBack))|ype(?:ID|Ref))|U(?:RL(?:Bookmark(?:CreationOptions|FileCreationOptions|ResolutionOptions)|ComponentType|E(?:numerator(?:Options|Re(?:f|sult))|rror)|PathStyle|Ref)|UID(?:Bytes|Ref)|serNotification(?:CallBack|Ref))|WriteStream(?:ClientCallBack|Ref)|XML(?:Attribute(?:DeclarationInfo|ListDeclarationInfo)|Document(?:Info|TypeInfo)|E(?:lement(?:Info|TypeDeclarationInfo)|ntity(?:Info|ReferenceInfo|TypeCode)|xternalID)|No(?:de(?:Ref|TypeCode)|tationInfo)|P(?:arser(?:AddChildCallBack|C(?:allBacks|o(?:ntext|pyDescriptionCallBack)|reateXMLStructureCallBack)|EndXMLStructureCallBack|HandleErrorCallBack|Options|Re(?:f|leaseCallBack|solveExternalEntityCallBack|tainCallBack)|StatusCode)|rocessingInstructionInfo)|TreeRef))|FSRef)\\b", "name": "support.type.cf.c" }, { - "match": "\\b(?:FILE|accessx_descriptor|blk(?:cnt_t|size_t)|c(?:addr_t|lock(?:_t|id_t)|t_rune_t)|d(?:addr_t|ev_t|i(?:spatch_time_t|v_t)|ouble_t)|errno_t|f(?:bootstraptransfer(?:_t)?|c(?:hecklv(?:_t)?|odeblobs(?:_t)?)|d_(?:mask|set)|i(?:lesec_(?:property_t|t)|xpt_t)|lo(?:at_t|ck(?:timeout)?)|pos_t|s(?:blkcnt_t|filcnt_t|i(?:d(?:_t)?|gnatures(?:_t)?)|obj_id(?:_t)?|searchblock|tore(?:_t)?))|g(?:id_t|uid_t)|i(?:d(?:_t|type_t)|n(?:_(?:addr_t|port_t)|o(?:64_t|_t)|t(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t)))|jmp_buf|key_t|l(?:conv|div_t|ldiv_t|og2phys)|m(?:ach_port_t|ode_t)|nlink_t|off_t|p(?:id_t|roc_rlimit_control_wakeupmon|trdiff_t)|q(?:addr_t|uad_t)|r(?:advisory|egister_t|lim(?:_t|it)|size_t|u(?:ne_t|sage(?:_info_(?:current|t|v(?:0|1|2|3)))?))|s(?:e(?:archstate|gsz_t)|i(?:g(?:_(?:atomic_t|t)|action|event|info_t|jmp_buf|s(?:et_t|tack)|vec)|ze_t)|size_t|tack_t|useconds_t|wblk_t|yscall_arg_t)|t(?:ime(?:_t|spec|val)|m)|u(?:_(?:char|int(?:16_t|32_t|64_t|8_t)?|long|quad_t|short)|context_t|i(?:d_t|nt(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t)?)|s(?:e(?:conds_t|r_(?:addr_t|long_t|off_t|s(?:ize_t|size_t)|time_t|ulong_t))|hort)|uid_t)|va_list|wint_t)\\b", + "match": "\\b(?:accessx_descriptor|blk(?:cnt_t|size_t)|c(?:addr_t|lock(?:_t|i(?:d_t|nfo))|t_rune_t)|d(?:addr_t|ev_t|i(?:spatch_time_t|v_t)|ouble_t)|e(?:rrno_t|xception)|f(?:bootstraptransfer(?:_t)?|c(?:hecklv(?:_t)?|odeblobs(?:_t)?)|d_(?:mask|set)|i(?:lesec_(?:property_t|t)|xpt_t)|lo(?:at_t|ck(?:timeout)?)|punchhole(?:_t)?|s(?:blkcnt_t|filcnt_t|i(?:d(?:_t)?|gnatures(?:_t)?)|obj_id(?:_t)?|pecread(?:_t)?|searchblock|tore(?:_t)?)|trimactivefile(?:_t)?)|g(?:id_t|uid_t)|i(?:d(?:_t|type_t)|n(?:_(?:addr_t|port_t)|o(?:64_t|_t)|t(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t))|ovec|timerval)|jmp_buf|key_t|l(?:conv|div_t|ldiv_t|og2phys)|m(?:a(?:ch_port_t|x_align_t)|ode_t)|nlink_t|off_t|p(?:id_t|roc_rlimit_control_wakeupmon|trdiff_t)|q(?:addr_t|uad_t)|r(?:advisory|egister_t|lim(?:_t|it)|size_t|u(?:ne_t|sage(?:_info_(?:current|t|v(?:0|1|2|3|4)))?))|s(?:a_family_t|e(?:archstate|gsz_t)|i(?:g(?:_(?:atomic_t|t)|action|event|info_t|jmp_buf|s(?:et_t|tack)|vec)|md_(?:double(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))|float(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))|quat(?:d|f))|ze_t)|ocklen_t|size_t|tack_t|useconds_t|wblk_t|yscall_arg_t)|t(?:ime(?:_t|spec|val(?:64)?|zone)|m)|u(?:_(?:char|int(?:16_t|32_t|64_t|8_t)?|long|quad_t|short)|context_t|i(?:d_t|nt(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t)?)|s(?:e(?:conds_t|r_(?:addr_t|long_t|off_t|s(?:ize_t|size_t)|time_t|ulong_t))|hort)|uid_t)|va_list|w(?:char_t|int_t))\\b", "name": "support.type.clib.c" }, { - "match": "\\bdispatch_(?:autorelease_frequency_t|block_(?:flags_t|t)|data_(?:applier_t|s|t)|f(?:d_t|unction_t)|group_(?:s|t)|io_(?:close_flags_t|handler_t|interval_flags_t|s|t(?:ype_t)?)|o(?:bject_(?:s|t)|nce_t)|q(?:os_class_t|ueue_(?:attr_(?:s|t)|priority_t|s|t))|s(?:emaphore_(?:s|t)|ource_(?:m(?:ach_send_flags_t|emorypressure_flags_t)|proc_flags_t|s|t(?:imer_flags_t|ype_(?:s|t))?|vnode_flags_t)))\\b", + "match": "\\bdispatch_(?:autorelease_frequency_t|block_(?:flags_t|t)|channel_s|data_(?:s|t)|f(?:d_t|unction_t)|group_(?:s|t)|io_(?:close_flags_t|handler_t|interval_flags_t|s|t(?:ype_t)?)|mach_(?:msg_s|s)|o(?:bject_(?:s|t)|nce_t)|q(?:os_class_t|ueue_(?:attr_(?:s|t)|concurrent_t|global_t|main_t|priority_t|s(?:erial_t)?|t))|s(?:emaphore_(?:s|t)|ource_(?:m(?:ach_(?:recv_flags_t|send_flags_t)|emorypressure_flags_t)|proc_flags_t|s|t(?:imer_flags_t|ype_(?:s|t))?|vnode_flags_t))|workloop_t)\\b", "name": "support.type.dispatch.c" }, { @@ -110,16 +290,52 @@ "name": "support.type.mac-classic.c" }, { - "match": "\\b(?:pthread_(?:attr_t|cond(?:_t|attr_t)|key_t|mutex(?:_t|attr_t)|o(?:nce_t|verride_(?:s|t))|rwlock(?:_t|attr_t)|t)|sched_param)\\b", + "match": "\\bpthread_(?:attr_t|cond(?:_t|attr_t)|key_t|mutex(?:_t|attr_t)|once_t|rwlock(?:_t|attr_t)|t)\\b", "name": "support.type.pthread.c" }, { - "match": "\\bCGImageByteOrderInfo\\b", - "name": "support.type.quartz.10.12.c" + "match": "\\b(?:CG(?:AffineTransform|B(?:itmap(?:ContextReleaseDataCallback|Info)|lendMode|uttonCount)|C(?:aptureOptions|harCode|o(?:lor(?:ConversionInfo(?:Ref|TransformType)?|Re(?:f|nderingIntent)|Space(?:Model|Ref)?)?|n(?:figureOption|text(?:Ref)?)))|D(?:ata(?:Consumer(?:Callbacks|PutBytesCallback|Re(?:f|leaseInfoCallback))?|Provider(?:DirectCallbacks|GetByte(?:PointerCallback|s(?:AtPositionCallback|Callback))|Re(?:f|lease(?:BytePointerCallback|DataCallback|InfoCallback)|windCallback)|S(?:equentialCallbacks|kipForwardCallback))?)|eviceColor|i(?:rectDisplayID|splay(?:BlendFraction|C(?:hangeSummaryFlags|o(?:nfigRef|unt))|Err|Fade(?:Interval|ReservationToken)|Mode(?:Ref)?|Re(?:configurationCallBack|servationInterval)|Stream(?:Frame(?:AvailableHandler|Status)|Ref|Update(?:Re(?:ctType|f))?)?)))|E(?:rror|vent(?:Err|F(?:i(?:eld|lterMask)|lags)|M(?:ask|ouseSubtype)|Ref|S(?:ource(?:KeyboardType|Ref|StateID)|uppressionState)|T(?:ap(?:CallBack|Information|Location|Options|P(?:lacement|roxy))|imestamp|ype)))|F(?:loat|ont(?:Index|PostScriptFormat|Ref)?|unction(?:Callbacks|EvaluateCallback|Re(?:f|leaseInfoCallback))?)|G(?:ammaValue|esturePhase|lyph(?:DeprecatedEnum)?|radient(?:DrawingOptions|Ref)?)|I(?:mage(?:AlphaInfo|ByteOrderInfo|PixelFormatInfo|Ref)?|nterpolationQuality)|KeyCode|L(?:ayer(?:Ref)?|ine(?:Cap|Join))|M(?:o(?:mentumScrollPhase|useButton)|utablePathRef)|OpenGLDisplayMask|P(?:DF(?:A(?:ccessPermissions|rray(?:Ref)?)|Bo(?:olean|x)|ContentStream(?:Ref)?|D(?:ataFormat|ictionary(?:ApplierFunction|Ref)?|ocument(?:Ref)?)|Integer|O(?:bject(?:Ref|Type)?|perator(?:Callback|Table(?:Ref)?))|Page(?:Ref)?|Real|S(?:canner(?:Ref)?|tr(?:eam(?:Ref)?|ing(?:Ref)?))|Tag(?:Property|Type))|SConverter(?:Begin(?:DocumentCallback|PageCallback)|Callbacks|End(?:DocumentCallback|PageCallback)|MessageCallback|ProgressCallback|Re(?:f|leaseInfoCallback))?|at(?:h(?:Appl(?:ierFunction|yBlock)|DrawingMode|Element(?:Type)?|Ref)?|tern(?:Callbacks|DrawPatternCallback|Re(?:f|leaseInfoCallback)|Tiling)?)|oint)|Re(?:ct(?:Count|Edge)?|freshRate)|S(?:cr(?:een(?:RefreshCallback|Update(?:Move(?:Callback|Delta)|Operation))|oll(?:EventUnit|Phase))|hading(?:Ref)?|ize)|Text(?:DrawingMode|Encoding)|Vector|W(?:heelCount|indow(?:BackingType|I(?:D|mageOption)|L(?:evel(?:Key)?|istOption)|SharingType)))|IOSurfaceRef)\\b", + "name": "support.type.quartz.c" }, { - "match": "\\b(?:CG(?:AffineTransform|B(?:itmap(?:ContextReleaseDataCallback|Info)|lendMode|uttonCount)|C(?:aptureOptions|harCode|o(?:lor(?:ConversionInfo(?:Ref|TransformType)?|Re(?:f|nderingIntent)|Space(?:Model|Ref)?)?|n(?:figureOption|text(?:Ref)?)))|D(?:ata(?:Consumer(?:Callbacks|PutBytesCallback|Re(?:f|leaseInfoCallback))?|Provider(?:DirectCallbacks|GetByte(?:PointerCallback|s(?:AtPositionCallback|Callback))|Re(?:f|lease(?:BytePointerCallback|DataCallback|InfoCallback)|windCallback)|S(?:equentialCallbacks|kipForwardCallback))?)|eviceColor|i(?:rectDisplayID|splay(?:BlendFraction|C(?:hangeSummaryFlags|o(?:nfigRef|unt))|Err|Fade(?:Interval|ReservationToken)|Mode(?:Ref)?|Re(?:configurationCallBack|servationInterval)|Stream(?:Frame(?:AvailableHandler|Status)|Ref|Update(?:Re(?:ctType|f))?)?)))|E(?:rror|vent(?:Err|F(?:i(?:eld|lterMask)|lags)|M(?:ask|ouseSubtype)|Ref|S(?:ource(?:KeyboardType|Ref|StateID)|uppressionState)|T(?:ap(?:CallBack|Information|Location|Options|P(?:lacement|roxy))|imestamp|ype)))|F(?:loat|ont(?:Index|PostScriptFormat|Ref)?|unction(?:Callbacks|EvaluateCallback|Re(?:f|leaseInfoCallback))?)|G(?:ammaValue|esturePhase|lyph(?:DeprecatedEnum)?|radient(?:DrawingOptions|Ref)?)|I(?:mage(?:AlphaInfo|ByteOrderInfo|Ref)?|nterpolationQuality)|KeyCode|L(?:ayer(?:Ref)?|ine(?:Cap|Join))|M(?:o(?:mentumScrollPhase|useButton)|utablePathRef)|OpenGLDisplayMask|P(?:DF(?:Array(?:Ref)?|Bo(?:olean|x)|ContentStream(?:Ref)?|D(?:ataFormat|ictionary(?:ApplierFunction|Ref)?|ocument(?:Ref)?)|Integer|O(?:bject(?:Ref|Type)?|perator(?:Callback|Table(?:Ref)?))|Page(?:Ref)?|Real|S(?:canner(?:Ref)?|tr(?:eam(?:Ref)?|ing(?:Ref)?)))|SConverter(?:Begin(?:DocumentCallback|PageCallback)|Callbacks|End(?:DocumentCallback|PageCallback)|MessageCallback|ProgressCallback|Re(?:f|leaseInfoCallback))?|at(?:h(?:ApplierFunction|DrawingMode|Element(?:Type)?|Ref)?|tern(?:Callbacks|DrawPatternCallback|Re(?:f|leaseInfoCallback)|Tiling)?)|oint)|Re(?:ct(?:Count|Edge)?|freshRate)|S(?:cr(?:een(?:RefreshCallback|Update(?:Move(?:Callback|Delta)|Operation))|oll(?:EventUnit|Phase))|hading(?:Ref)?|ize)|Text(?:DrawingMode|Encoding)|Vector|W(?:heelCount|indow(?:BackingType|I(?:D|mageOption)|L(?:evel(?:Key)?|istOption)|SharingType)))|IOSurfaceRef)\\b", - "name": "support.type.quartz.c" + "match": "\\b(?:k(?:C(?:FHTTPVersion2_0|GImage(?:Destination(?:EmbedThumbnail|ImageMaxPixelSize)|MetadataShouldExcludeGPS|Property(?:8BIMVersion|APNG(?:DelayTime|LoopCount|UnclampedDelayTime)|GPSHPositioningError|MakerAppleDictionary))|T(?:FontOpenTypeFeature(?:Tag|Value)|RubyAnnotationAttributeName)|V(?:ImageBufferAlphaChannelIsOpaque|PixelFormatCo(?:mponentRange(?:_(?:FullRange|VideoRange|WideRange))?|ntains(?:RGB|YCbCr))))|Sec(?:AttrAccess(?:Control|ibleWhenPasscodeSetThisDeviceOnly)|UseOperationPrompt)|UTType(?:3DContent|A(?:VIMovie|pple(?:ProtectedMPEG4Video|Script)|ssemblyLanguageSource|udioInterchangeFileFormat)|B(?:inaryPropertyList|ookmark|zip2Archive)|C(?:alendarEvent|ommaSeparatedText)|DelimitedText|E(?:lectronicPublication|mailMessage)|Font|GNUZipArchive|InternetLocation|J(?:SON|ava(?:Archive|Class|Script))|Log|M(?:3UPlaylist|IDIAudio|PEG2(?:TransportStream|Video))|OSAScript(?:Bundle)?|P(?:HPScript|KCS12|erlScript|l(?:aylist|uginBundle)|r(?:esentation|opertyList)|ythonScript)|QuickLookGenerator|R(?:awImage|ubyScript)|S(?:c(?:alableVectorGraphics|ript)|hellScript|p(?:otlightImporter|readsheet)|ystemPreferencesPane)|T(?:abSeparatedText|oDoItem)|U(?:RLBookmarkData|TF8TabSeparatedText|nixExecutable)|W(?:aveformAudio|indowsExecutable)|X(?:509Certificate|MLPropertyList|PCService)|ZipArchive))|matrix_identity_(?:double(?:2x2|3x3|4x4)|float(?:2x2|3x3|4x4)))\\b", + "name": "support.variable.10.10.c" + }, + { + "match": "\\bk(?:A(?:XListItem(?:IndexTextAttribute|LevelTextAttribute|PrefixTextAttribute)|udioComponent(?:InstanceInvalidationNotification|RegistrationsChangedNotification))|C(?:FStreamPropertySocketExtendedBackgroundIdleMode|GImage(?:Property(?:ExifSubsecTimeOriginal|PNGCompressionFilter|TIFFTile(?:Length|Width))|SourceSubsampleFactor)|V(?:ImageBuffer(?:ColorPrimaries_(?:DCI_P3|ITU_R_2020|P3_D65)|TransferFunction_ITU_R_2020|YCbCrMatrix_(?:DCI_P3|ITU_R_2020|P3_D65))|MetalTextureCacheMaximumTextureAgeKey|PixelBuffer(?:MetalCompatibilityKey|OpenGLTextureCacheCompatibilityKey)))|MDItemHTMLContent|Sec(?:A(?:CLAuthorization(?:Integrity|PartitionID)|ttrSyncViewHint)|PolicyApplePayIssuerEncryption|TrustCertificateTransparency|UseAuthentication(?:Context|UI(?:Allow|Fail|Skip)?))|UTTypeSwiftSource)\\b", + "name": "support.variable.10.11.c" + }, + { + "match": "\\bk(?:C(?:FStreamNetworkServiceTypeCallSignaling|GImage(?:DestinationOptimizeColorForSharing|PropertyDNG(?:AsShot(?:Neutral|WhiteXY)|B(?:aseline(?:Exposure|Noise|Sharpness)|lackLevel)|C(?:a(?:librationIlluminant(?:1|2)|meraCalibration(?:1|2|Signature))|olorMatrix(?:1|2))|FixVignetteRadial|NoiseProfile|Pr(?:ivateData|ofileCalibrationSignature)|W(?:arp(?:Fisheye|Rectilinear)|hiteLevel)))|T(?:BackgroundColorAttributeName|FontDownloadedAttribute|HorizontalInVerticalFormsAttributeName|RubyAnnotationS(?:caleToFitAttributeName|izeFactorAttributeName)|TrackingAttributeName)|VImageBufferTransferFunction_SMPTE_ST_428_1)|IOSurfacePixelSizeCastingAllowed|Sec(?:Attr(?:AccessGroupToken|KeyTypeECSECPrimeRandom|TokenID(?:SecureEnclave)?)|Key(?:Algorithm(?:EC(?:D(?:HKeyExchange(?:Cofactor(?:X963SHA(?:1|2(?:24|56)|384|512))?|Standard(?:X963SHA(?:1|2(?:24|56)|384|512))?)|SASignature(?:DigestX962(?:SHA(?:1|2(?:24|56)|384|512))?|MessageX962SHA(?:1|2(?:24|56)|384|512)|RFC4754))|IESEncryption(?:CofactorX963SHA(?:1AESGCM|2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM)|StandardX963SHA(?:1AESGCM|2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM)))|RSA(?:Encryption(?:OAEPSHA(?:1(?:AESGCM)?|2(?:24(?:AESGCM)?|56(?:AESGCM)?)|384(?:AESGCM)?|512(?:AESGCM)?)|PKCS1|Raw)|Signature(?:DigestPKCS1v15(?:Raw|SHA(?:1|2(?:24|56)|384|512))|MessagePKCS1v15SHA(?:1|2(?:24|56)|384|512)|Raw)))|KeyExchangeParameter(?:RequestedSize|SharedInfo)))|UTTypeLivePhoto)\\b", + "name": "support.variable.10.12.c" + }, + { + "match": "\\bk(?:C(?:GImage(?:AuxiliaryData(?:Info(?:Data(?:Description)?|Metadata)|TypeD(?:epth|isparity))|Metadata(?:NamespaceIPTCExtension|PrefixIPTCExtension)|Property(?:AuxiliaryData(?:Type)?|BytesPerRow|FileContentsDictionary|Height|I(?:PTCExt(?:A(?:boutCvTerm(?:CvId|Id|Name|RefinedAbout)?|ddlModelInfo|rtwork(?:C(?:ircaDateCreated|o(?:nt(?:entDescription|ributionDescription)|pyright(?:Notice|Owner(?:ID|Name)))|reator(?:ID)?)|DateCreated|Licensor(?:ID|Name)|OrObject|PhysicalDescription|S(?:ource(?:Inv(?:URL|entoryNo))?|tylePeriod)|Title)|udio(?:Bitrate(?:Mode)?|ChannelCount))|C(?:ircaDateCreated|o(?:nt(?:ainerFormat(?:Identifier|Name)?|r(?:ibutor(?:Identifier|Name|Role)?|olledVocabularyTerm))|pyrightYear)|reator(?:Identifier|Name|Role)?)|D(?:ataOnScreen(?:Region(?:D|H|Text|Unit|W|X|Y)?)?|igital(?:ImageGUID|Source(?:FileType|Type))|opesheet(?:Link(?:Link(?:Qualifier)?)?)?)|E(?:mb(?:dEncRightsExpr|eddedEncodedRightsExpr(?:LangID|Type)?)|pisode(?:Identifier|N(?:ame|umber))?|vent|xternalMetadataLink)|FeedIdentifier|Genre(?:Cv(?:Id|Term(?:Id|Name|RefinedAbout)))?|Headline|IPTCLastEdited|L(?:inkedEnc(?:RightsExpr|odedRightsExpr(?:LangID|Type)?)|ocation(?:C(?:ity|ountry(?:Code|Name)|reated)|GPS(?:Altitude|L(?:atitude|ongitude))|Identifier|Location(?:Id|Name)|ProvinceState|S(?:hown|ublocation)|WorldRegion))|M(?:axAvail(?:Height|Width)|odelAge)|OrganisationInImage(?:Code|Name)|P(?:erson(?:Heard(?:Identifier|Name)?|InImage(?:C(?:haracteristic|vTerm(?:CvId|Id|Name|RefinedAbout))|Description|Id|Name|WDetails)?)|roductInImage(?:Description|GTIN|Name)?|ublicationEvent(?:Date|Identifier|Name)?)|R(?:ating(?:R(?:atingRegion|egion(?:C(?:ity|ountry(?:Code|Name))|GPS(?:Altitude|L(?:atitude|ongitude))|Identifier|Location(?:Id|Name)|ProvinceState|Sublocation|WorldRegion))|S(?:caleM(?:axValue|inValue)|ourceLink)|Value(?:LogoLink)?)?|e(?:gistry(?:EntryRole|I(?:D|temID)|OrganisationID)|leaseReady))|S(?:e(?:ason(?:Identifier|N(?:ame|umber))?|ries(?:Identifier|Name)?)|hownEvent(?:Identifier|Name)?|t(?:orylineIdentifier|reamReady|ylePeriod)|upplyChainSource(?:Identifier|Name)?)|T(?:emporalCoverage(?:From|To)?|ranscript(?:Link(?:Link(?:Qualifier)?)?)?)|Vi(?:deo(?:Bitrate(?:Mode)?|DisplayAspectRatio|EncodingProfile|S(?:hotType(?:Identifier|Name)?|treamsCount))|sualColor)|WorkflowTag(?:Cv(?:Id|Term(?:Id|Name|RefinedAbout)))?)|mage(?:Count|s))|NamedColorSpace|P(?:ixelFormat|rimaryImage)|ThumbnailImages|Width))|T(?:BaselineOffsetAttributeName|FontVariationAxisHiddenKey)|V(?:ImageBuffer(?:ContentLightLevelInfoKey|MasteringDisplayColorVolumeKey|TransferFunction_(?:ITU_R_2100_HLG|SMPTE_ST_2084_PQ|sRGB))|MetalTextureUsage))|IOSurface(?:Plane(?:BitsPerElement|Component(?:Bit(?:Depths|Offsets)|Names|Ranges|Types))|Subsampling)|Sec(?:A(?:CLAuthorizationChange(?:ACL|Owner)|ttrPersist(?:antReference|entReference))|KeyAlgorithm(?:ECIESEncryption(?:CofactorVariableIVX963SHA(?:2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM)|StandardVariableIVX963SHA(?:2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM))|RSASignature(?:DigestPSSSHA(?:1|2(?:24|56)|384|512)|MessagePSSSHA(?:1|2(?:24|56)|384|512)))))\\b", + "name": "support.variable.10.13.c" + }, + { + "match": "\\bkC(?:GImage(?:AuxiliaryDataTypePortraitEffectsMatte|Property(?:DNG(?:A(?:ctiveArea|n(?:alogBalance|tiAliasStrength)|sShot(?:ICCProfile|Pr(?:eProfileMatrix|ofileName)))|B(?:a(?:selineExposureOffset|yerGreenSplit)|estQualityScale|lackLevel(?:Delta(?:H|V)|RepeatDim))|C(?:FA(?:Layout|PlaneColor)|hromaBlurRadius|olorimetricReference|urrent(?:ICCProfile|PreProfileMatrix))|Default(?:BlackRender|Crop(?:Origin|Size)|Scale|UserCrop)|ExtraCameraProfiles|ForwardMatrix(?:1|2)|Linear(?:ResponseLimit|izationTable)|Ma(?:kerNoteSafety|skedAreas)|N(?:ewRawImageDigest|oiseReductionApplied)|O(?:pcodeList(?:1|2|3)|riginal(?:BestQualityFinalSize|Default(?:CropSize|FinalSize)|RawFile(?:D(?:ata|igest)|Name)))|Pr(?:eview(?:Application(?:Name|Version)|ColorSpace|DateTime|Settings(?:Digest|Name))|ofile(?:Copyright|EmbedPolicy|HueSatMap(?:D(?:ata(?:1|2)|ims)|Encoding)|LookTable(?:D(?:ata|ims)|Encoding)|Name|ToneCurve))|R(?:aw(?:DataUniqueID|ImageDigest|ToPreviewGain)|eductionMatrix(?:1|2)|owInterleaveFactor)|S(?:hadowScale|ubTileBlockSize))|PNG(?:Comment|Disclaimer|Source|Warning)))|TTypesetterOptionAllowUnboundedLayout|V(?:ImageBufferTransferFunction_Linear|PixelFormatContainsGrayscale))\\b", + "name": "support.variable.10.14.c" + }, + { + "match": "\\bk(?:C(?:FStreamProperty(?:Allow(?:ConstrainedNetworkAccess|ExpensiveNetworkAccess)|ConnectionIsExpensive)|GImage(?:A(?:nimation(?:DelayTime|LoopCount|StartIndex)|uxiliaryDataTypeSemanticSegmentation(?:HairMatte|SkinMatte|TeethMatte))|Property(?:APNG(?:CanvasPixel(?:Height|Width)|FrameInfoArray)|Exif(?:CompositeImage|OffsetTime(?:Digitized|Original)?|Source(?:ExposureTimesOfCompositeImage|ImageNumberOfCompositeImage))|GIF(?:CanvasPixel(?:Height|Width)|FrameInfoArray)|HEICS(?:CanvasPixel(?:Height|Width)|D(?:elayTime|ictionary)|FrameInfoArray|LoopCount|UnclampedDelayTime)))|TFontFeature(?:SampleTextKey|TooltipTextKey)|V(?:ImageBufferAlphaChannelMode(?:Key|_(?:PremultipliedAlpha|StraightAlpha))|MetalTextureStorageMode))|MIDIPropertyNameConfigurationDictionary|Sec(?:PropertyType(?:Array|Number)|UseDataProtectionKeychain))\\b", + "name": "support.variable.10.15.c" + }, + { + "match": "\\bkColorSyncExtendedRange\\b", + "name": "support.variable.10.16.c" + }, + { + "match": "\\bk(?:C(?:FStream(?:NetworkServiceType(?:AVStreaming|Responsive(?:AV|Data))|Property(?:ConnectionIsCellular|NoCellular))|GImage(?:Destination(?:DateTime|Me(?:rgeMetadata|tadata)|Orientation)|Metadata(?:EnumerateRecursively|Namespace(?:DublinCore|Exif(?:Aux)?|IPTCCore|Photoshop|TIFF|XMP(?:Basic|Rights))|Prefix(?:DublinCore|Exif(?:Aux)?|IPTCCore|Photoshop|TIFF|XMP(?:Basic|Rights))|ShouldExcludeXMP))|T(?:Baseline(?:Class(?:AttributeName|Hanging|Ideographic(?:Centered|High|Low)|Math|Roman)|InfoAttributeName|OriginalFont|Reference(?:Font|InfoAttributeName))|FontD(?:escriptorMatching(?:CurrentAssetSize|Descriptors|Error|Percentage|Result|SourceDescriptor|Total(?:AssetSize|DownloadedSize))|ownloadableAttribute)|WritingDirectionAttributeName)|VImageBufferColorPrimaries_P22)|Sec(?:O(?:AEP(?:EncodingParametersAttributeName|M(?:GF1DigestAlgorithmAttributeName|essageLengthAttributeName))|IDSRVName)|P(?:addingOAEPKey|olicyAppleTimeStamping|rivateKeyAttrs|ublicKeyAttrs)))\\b", + "name": "support.variable.10.8.c" + }, + { + "match": "\\b(?:XPC_ACTIVITY_(?:ALLOW_BATTERY|CHECK_IN|DELAY|GRACE_PERIOD|INTERVAL(?:_(?:1(?:5_MIN|_(?:DAY|HOUR|MIN))|30_MIN|4_HOURS|5_MIN|7_DAYS|8_HOURS))?|PRIORITY(?:_(?:MAINTENANCE|UTILITY))?|RE(?:PEATING|QUIRE_SCREEN_SLEEP))|k(?:AX(?:MarkedMisspelledTextAttribute|TrustedCheckOptionPrompt)|C(?:FStreamPropertySSLContext|GImage(?:Metadata(?:NamespaceExifEX|PrefixExifEX)|Property(?:Exif(?:ISOSpeed(?:Latitude(?:yyy|zzz))?|RecommendedExposureIndex|S(?:ensitivityType|tandardOutputSensitivity))|OpenEXR(?:AspectRatio|Dictionary))|SourceShouldCacheImmediately)|TLanguageAttributeName)|S(?:ec(?:Attr(?:Access(?:Group|ible(?:AfterFirstUnlock(?:ThisDeviceOnly)?|WhenUnlocked(?:ThisDeviceOnly)?)?)|KeyTypeEC|Synchronizable(?:Any)?)|Policy(?:Apple(?:PassbookSigning|Revocation)|RevocationFlags|TeamIdentifier)|Trust(?:E(?:valuationDate|xtendedValidation)|OrganizationName|Re(?:sultValue|vocation(?:Checked|ValidUntilDate))))|peech(?:AudioOutputFormatProperty|Output(?:ChannelMapProperty|ToFileDescriptorProperty)|SynthExtensionProperty)))|vm_kernel_page_(?:mask|s(?:hift|ize)))\\b", + "name": "support.variable.10.9.c" + }, + { + "match": "\\b(?:CATransform3DIdentity|KERNEL_(?:AUDIT_TOKEN|SECURITY_TOKEN)|NDR_record|UUID_NULL|bootstrap_port|gGuid(?:Apple(?:CSP(?:DL)?|DotMac(?:DL|TP)|FileDL|LDAPDL|SdCSPDL|X509(?:CL|TP))|Cssm)|k(?:A(?:ERemoteProcess(?:NameKey|ProcessIDKey|U(?:RLKey|serIDKey))|X(?:A(?:ttachmentTextAttribute|utocorrectedTextAttribute)|BackgroundColorTextAttribute|Fo(?:nt(?:FamilyKey|NameKey|SizeKey|TextAttribute)|reg(?:oundColorTextAttribute|roundColorTextAttribute))|LinkTextAttribute|MisspelledTextAttribute|NaturalLanguageTextAttribute|ReplacementStringTextAttribute|S(?:hadowTextAttribute|trikethrough(?:ColorTextAttribute|TextAttribute)|uperscriptTextAttribute)|Underline(?:ColorTextAttribute|TextAttribute)|V(?:alue(?:AXErrorType|C(?:FRangeType|G(?:PointType|RectType|SizeType))|IllegalType)|isibleNameKey))|u(?:dioStreamAnyRate|thorizationExternalFormLength))|C(?:F(?:DNSServiceFailureKey|ErrorDomain(?:C(?:FNetwork|GImageMetadata)|SystemConfiguration|WinSock)|FTPStatusCodeKey|GetAddrInfoFailureKey|HTTP(?:Authentication(?:AccountDomain|Password|Scheme(?:Basic|Digest|Kerberos|N(?:TLM|egotiate(?:2)?)|XMobileMeAuthToken)|Username)|Version1_(?:0|1))|NetworkProxies(?:Exc(?:eptionsList|ludeSimpleHostnames)|FTP(?:Enable|P(?:assive|ort|roxy))|Gopher(?:Enable|P(?:ort|roxy))|HTTP(?:Enable|P(?:ort|roxy)|S(?:Enable|P(?:ort|roxy)))|ProxyAuto(?:Config(?:Enable|JavaScript|URLString)|DiscoveryEnable)|RTSP(?:Enable|P(?:ort|roxy))|SOCKS(?:Enable|P(?:ort|roxy)))|Proxy(?:AutoConfiguration(?:HTTPResponseKey|JavaScriptKey|URLKey)|HostNameKey|P(?:asswordKey|ortNumberKey)|Type(?:AutoConfiguration(?:JavaScript|URL)|FTP|HTTP(?:S)?|Key|None|SOCKS)|UsernameKey)|S(?:OCKS(?:NegotiationMethodKey|StatusCodeKey|VersionKey)|tream(?:ErrorDomain(?:FTP|HTTP|Mach|Net(?:DB|Services)|SystemConfiguration|WinSock)|NetworkServiceType(?:Background|V(?:ideo|oice))?|Property(?:ProxyLocalBypass|S(?:SL(?:PeerTrust|Settings)|ocketRemote(?:Host|NetService)))|SSL(?:Certificates|IsServer|Level|PeerName|ValidatesCertificateChain)))|URLErrorFailingURL(?:ErrorKey|StringErrorKey))|GImage(?:Destination(?:BackgroundColor|LossyCompressionQuality)|Property(?:8BIM(?:Dictionary|LayerNames)|C(?:IFF(?:C(?:ameraSerialNumber|ontinuousDrive)|D(?:escription|ictionary)|F(?:irmware|lashExposureComp|ocusMode)|Image(?:FileName|Name|SerialNumber)|LensM(?:axMM|inMM|odel)|Me(?:asuredEV|teringMode)|OwnerName|Re(?:cordID|lease(?:Method|Timing))|S(?:elfTimingTime|hootingMode)|WhiteBalanceIndex)|olorModel(?:CMYK|Gray|Lab|RGB)?)|D(?:NG(?:BackwardVersion|CameraSerialNumber|Dictionary|L(?:ensInfo|ocalizedCameraModel)|UniqueCameraModel|Version)|PI(?:Height|Width)|epth)|Exif(?:A(?:pertureValue|ux(?:Dictionary|F(?:irmware|lashCompensation)|ImageNumber|Lens(?:I(?:D|nfo)|Model|SerialNumber)|OwnerName|SerialNumber))|B(?:odySerialNumber|rightnessValue)|C(?:FAPattern|ameraOwnerName|o(?:lorSpace|mp(?:onentsConfiguration|ressedBitsPerPixel)|ntrast)|ustomRendered)|D(?:ateTime(?:Digitized|Original)|eviceSettingDescription|i(?:ctionary|gitalZoomRatio))|Exposure(?:BiasValue|Index|Mode|Program|Time)|F(?:Number|ileSource|lash(?:Energy|PixVersion)?|ocal(?:Len(?:In35mmFilm|gth)|Plane(?:ResolutionUnit|XResolution|YResolution)))|Ga(?:inControl|mma)|I(?:SOSpeedRatings|mageUniqueID)|L(?:ens(?:M(?:ake|odel)|S(?:erialNumber|pecification))|ightSource)|M(?:a(?:kerNote|xApertureValue)|eteringMode)|OECF|Pixel(?:XDimension|YDimension)|RelatedSoundFile|S(?:aturation|cene(?:CaptureType|Type)|ensingMethod|h(?:arpness|utterSpeedValue)|p(?:atialFrequencyResponse|ectralSensitivity)|ub(?:ject(?:Area|Dist(?:Range|ance)|Location)|secTime(?:Digitized)?))|UserComment|Version|WhiteBalance)|FileSize|G(?:IF(?:D(?:elayTime|ictionary)|HasGlobalColorMap|ImageColorMap|LoopCount|UnclampedDelayTime)|PS(?:A(?:ltitude(?:Ref)?|reaInformation)|D(?:OP|ateStamp|est(?:Bearing(?:Ref)?|Distance(?:Ref)?|L(?:atitude(?:Ref)?|ongitude(?:Ref)?))|i(?:ctionary|fferental))|ImgDirection(?:Ref)?|L(?:atitude(?:Ref)?|ongitude(?:Ref)?)|M(?:apDatum|easureMode)|ProcessingMethod|S(?:atellites|peed(?:Ref)?|tatus)|T(?:imeStamp|rack(?:Ref)?)|Version))|HasAlpha|I(?:PTC(?:ActionAdvised|Byline(?:Title)?|C(?:a(?:ptionAbstract|tegory)|ity|o(?:nt(?:act(?:Info(?:Address|C(?:ity|ountry)|Emails|P(?:hones|ostalCode)|StateProvince|WebURLs))?|entLocation(?:Code|Name))|pyrightNotice|untryPrimaryLocation(?:Code|Name))|re(?:atorContactInfo|dit))|D(?:ateCreated|i(?:ctionary|gitalCreation(?:Date|Time)))|E(?:dit(?:Status|orialUpdate)|xpiration(?:Date|Time))|FixtureIdentifier|Headline|Image(?:Orientation|Type)|Keywords|LanguageIdentifier|O(?:bject(?:AttributeReference|Cycle|Name|TypeReference)|rigina(?:lTransmissionReference|tingProgram))|Pro(?:gramVersion|vinceState)|R(?:e(?:ference(?:Date|Number|Service)|lease(?:Date|Time))|ightsUsageTerms)|S(?:cene|ource|pecialInstructions|tarRating|u(?:b(?:Location|jectReference)|pplementalCategory))|TimeCreated|Urgency|WriterEditor)|s(?:Float|Indexed))|JFIF(?:D(?:ensityUnit|ictionary)|IsProgressive|Version|XDensity|YDensity)|Maker(?:Canon(?:AspectRatioInfo|C(?:ameraSerialNumber|ontinuousDrive)|Dictionary|F(?:irmware|lashExposureComp)|ImageSerialNumber|LensModel|OwnerName)|FujiDictionary|MinoltaDictionary|Nikon(?:C(?:ameraSerialNumber|olorMode)|Di(?:ctionary|gitalZoom)|F(?:lash(?:ExposureComp|Setting)|ocus(?:Distance|Mode))|I(?:SOSe(?:lection|tting)|mageAdjustment)|Lens(?:Adapter|Info|Type)|Quality|Sh(?:arpenMode|ootingMode|utterCount)|WhiteBalanceMode)|OlympusDictionary|PentaxDictionary)|Orientation|P(?:NG(?:Author|C(?:hromaticities|opyright|reationTime)|D(?:escription|ictionary)|Gamma|InterlaceType|ModificationTime|Software|Title|XPixelsPerMeter|YPixelsPerMeter|sRGBIntent)|ixel(?:Height|Width)|rofileName)|RawDictionary|TIFF(?:Artist|Co(?:mpression|pyright)|D(?:ateTime|ictionary|ocumentName)|HostComputer|ImageDescription|M(?:ake|odel)|Orientation|P(?:hotometricInterpretation|rimaryChromaticities)|ResolutionUnit|Software|TransferFunction|WhitePoint|XResolution|YResolution))|Source(?:CreateThumbnail(?:FromImage(?:Always|IfAbsent)|WithTransform)|Should(?:AllowFloat|Cache)|T(?:humbnailMaxPixelSize|ypeIdentifierHint)))|M(?:M(?:ApplyTransformProcName|CreateTransformPropertyProcName|Initialize(?:LinkProfileProcName|TransformProcName))|SEncoderDigestAlgorithmSHA(?:1|256))|SIdentity(?:ErrorDomain|GeneratePosixName)|T(?:F(?:o(?:nt(?:AttributeName|BaselineAdjustAttribute|C(?:ascadeListAttribute|haracterSetAttribute|o(?:llection(?:DisallowAutoActivationOption|IncludeDisabledFontsOption|RemoveDuplicatesOption)|pyrightNameKey))|D(?:es(?:criptionNameKey|igner(?:NameKey|URLNameKey))|isplayNameAttribute)|EnabledAttribute|F(?:amilyName(?:Attribute|Key)|eature(?:Se(?:lector(?:DefaultKey|IdentifierKey|NameKey|SettingKey)|ttingsAttribute)|Type(?:ExclusiveKey|IdentifierKey|NameKey|SelectorsKey)|sAttribute)|ixedAdvanceAttribute|ormatAttribute|ullNameKey)|L(?:anguagesAttribute|icense(?:NameKey|URLNameKey))|Ma(?:cintoshEncodingsAttribute|n(?:ager(?:BundleIdentifier|Error(?:Domain|Font(?:AssetNameKey|DescriptorsKey|URLsKey))|RegisteredFontsChangedNotification)|ufacturerNameKey)|trixAttribute)|NameAttribute|OrientationAttribute|P(?:ostScript(?:CIDNameKey|NameKey)|riorityAttribute)|Registration(?:ScopeAttribute|UserInfoAttribute)|S(?:ampleTextNameKey|izeAttribute|lantTrait|tyleName(?:Attribute|Key)|ubFamilyNameKey|ymbolicTrait)|Tra(?:demarkNameKey|itsAttribute)|U(?:RLAttribute|niqueNameKey)|V(?:ariationA(?:ttribute|xis(?:DefaultValueKey|IdentifierKey|M(?:aximumValueKey|inimumValueKey)|NameKey))|e(?:ndorURLNameKey|rsionNameKey))|W(?:eightTrait|idthTrait))|regroundColor(?:AttributeName|FromContextAttributeName))|rame(?:ClippingPathsAttributeName|P(?:ath(?:ClippingPathAttributeName|FillRuleAttributeName|WidthAttributeName)|rogressionAttributeName)))|GlyphInfoAttributeName|KernAttributeName|LigatureAttributeName|ParagraphStyleAttributeName|RunDelegateAttributeName|S(?:troke(?:ColorAttributeName|WidthAttributeName)|uperscriptAttributeName)|T(?:abColumnTerminatorsAttributeName|ypesetterOptionForcedEmbeddingLevel)|Underline(?:ColorAttributeName|StyleAttributeName)|VerticalFormsAttributeName)|V(?:Buffer(?:MovieTimeKey|NonPropagatedAttachmentsKey|PropagatedAttachmentsKey|Time(?:ScaleKey|ValueKey))|I(?:mageBuffer(?:C(?:GColorSpaceKey|hroma(?:Location(?:BottomFieldKey|TopFieldKey|_(?:Bottom(?:Left)?|Center|DV420|Left|Top(?:Left)?))|Subsampling(?:Key|_4(?:11|2(?:0|2))))|leanAperture(?:H(?:eightKey|orizontalOffsetKey)|Key|VerticalOffsetKey|WidthKey)|olorPrimaries(?:Key|_(?:EBU_3213|ITU_R_709_2|SMPTE_C)))|Display(?:DimensionsKey|HeightKey|WidthKey)|Field(?:CountKey|Detail(?:Key|SpatialFirstLine(?:Early|Late)|Temporal(?:BottomFirst|TopFirst)))|GammaLevelKey|ICCProfileKey|P(?:ixelAspectRatio(?:HorizontalSpacingKey|Key|VerticalSpacingKey)|referredCleanApertureKey)|TransferFunction(?:Key|_(?:ITU_R_709_2|SMPTE_240M_1995|UseGamma))|YCbCrMatrix(?:Key|_(?:ITU_R_(?:601_4|709_2)|SMPTE_240M_1995)))|ndefiniteTime)|Pixel(?:Buffer(?:BytesPerRowAlignmentKey|CG(?:BitmapContextCompatibilityKey|ImageCompatibilityKey)|ExtendedPixels(?:BottomKey|LeftKey|RightKey|TopKey)|HeightKey|IOSurface(?:CoreAnimationCompatibilityKey|OpenGL(?:ES(?:FBOCompatibilityKey|TextureCompatibilityKey)|FBOCompatibilityKey|TextureCompatibilityKey)|PropertiesKey)|MemoryAllocatorKey|OpenGL(?:CompatibilityKey|ES(?:CompatibilityKey|TextureCacheCompatibilityKey))|P(?:ixelFormatTypeKey|laneAlignmentKey|ool(?:AllocationThresholdKey|FreeBufferNotification|M(?:aximumBufferAgeKey|inimumBufferCountKey)))|WidthKey)|Format(?:B(?:itsPerBlock|l(?:ackBlock|ock(?:H(?:eight|orizontalAlignment)|VerticalAlignment|Width)))|C(?:G(?:Bitmap(?:ContextCompatibility|Info)|ImageCompatibility)|o(?:decType|n(?:stant|tainsAlpha)))|F(?:illExtendedPixelsCallback|ourCC)|HorizontalSubsampling|Name|OpenGL(?:Compatibility|ESCompatibility|Format|InternalFormat|Type)|Planes|QDCompatibility|VerticalSubsampling))|ZeroTime)|olorSync(?:A(?:CESCGLinearProfile|dobeRGB1998Profile)|B(?:estQuality|lackPointCompensation)|C(?:ameraDeviceClass|onver(?:sion(?:1DLut|3DLut|BPC|ChannelID|GridPoints|InpChan|Matrix|NDLut|OutChan|ParamCurve(?:0|1|2|3|4))|tQuality)|ustomProfiles)|D(?:CIP3Profile|evice(?:Class|De(?:faultProfileID|scription(?:s)?)|HostScope|ID|ModeDescription(?:s)?|Profile(?:I(?:D|s(?:Current|Default|Factory))|URL|sNotification)|RegisteredNotification|U(?:nregisteredNotification|serScope))|isplay(?:Device(?:Class|ProfilesNotification)|P3Profile)|raftQuality)|F(?:actoryProfiles|ixedPointRange)|Generic(?:CMYKProfile|Gray(?:Gamma22Profile|Profile)|LabProfile|RGBProfile|XYZProfile)|ITUR(?:2020Profile|709Profile)|NormalQuality|Pr(?:eferredCMM|interDeviceClass|ofile(?:C(?:lass|o(?:lorSpace|mputerDomain))|Description|H(?:eader|ostScope)|MD5Digest|PCS|RepositoryChangeNotification|U(?:RL|ser(?:Domain|Scope)))?)|R(?:OMMRGBProfile|e(?:gistrationUpdateWindowServer|nderingIntent(?:Absolute|Perceptual|Relative|Saturation|UseProfileHeader)?))|S(?:RGBProfile|cannerDeviceClass|ig(?:A(?:ToB(?:0Tag|1Tag|2Tag)|bstractClass)|B(?:ToA(?:0Tag|1Tag|2Tag)|lue(?:ColorantTag|TRCTag))|C(?:mykData|o(?:lorSpaceClass|pyrightTag))|D(?:eviceM(?:fgDescTag|odelDescTag)|isplayClass)|G(?:amutTag|r(?:ay(?:Data|TRCTag)|een(?:ColorantTag|TRCTag)))|InputClass|L(?:abData|inkClass)|Media(?:BlackPointTag|WhitePointTag)|NamedColor(?:2Tag|Class)|OutputClass|Pr(?:eview(?:0Tag|1Tag|2Tag)|ofile(?:DescriptionTag|SequenceDescTag))|R(?:ed(?:ColorantTag|TRCTag)|gbData)|TechnologyTag|ViewingCond(?:DescTag|itionsTag)|XYZData))|Transform(?:C(?:odeFragment(?:MD5|Type)|reator)|D(?:eviceTo(?:Device|PCS)|stSpace)|FullConversionData|GamutCheck|Info|P(?:CSTo(?:Device|PCS)|arametricConversionData)|S(?:implifiedConversionData|rcSpace)|Tag)))|D(?:ADiskDescription(?:Bus(?:NameKey|PathKey)|Device(?:GUIDKey|InternalKey|ModelKey|P(?:athKey|rotocolKey)|RevisionKey|TDMLockedKey|UnitKey|VendorKey)|Media(?:B(?:SD(?:M(?:ajorKey|inorKey)|NameKey|UnitKey)|lockSizeKey)|ContentKey|E(?:jectableKey|ncrypt(?:edKey|ionDetailKey))|IconKey|KindKey|LeafKey|NameKey|PathKey|RemovableKey|SizeKey|TypeKey|UUIDKey|W(?:holeKey|ritableKey))|Volume(?:KindKey|MountableKey|N(?:ameKey|etworkKey)|PathKey|TypeKey|UUIDKey))|R(?:A(?:bstractFile|ccessDate|llFilesystems|pplicationIdentifier|ttributeModificationDate|udio(?:FourChannelKey|PreEmphasisKey))|B(?:ackupDate|ibliographicFile|lock(?:Size(?:Key)?|TypeKey)|u(?:fferZone1DataKey|rn(?:AppendableKey|CompletionAction(?:Eject|Key|Mount)|DoubleLayerL0DataZoneBlocksKey|FailureAction(?:Eject|Key|None)|Key|OverwriteDiscKey|RequestedSpeedKey|St(?:atusChangedNotification|rategy(?:BDDAO|CD(?:SAO|TAO)|DVDDAO|IsRequiredKey|Key))|TestingKey|UnderrunProtectionKey|VerifyDiscKey)))|C(?:DText(?:ArrangerKey|C(?:FStringEncodingKey|haracterCodeKey|losedKey|o(?:mposerKey|pyrightAssertedFor(?:NamesKey|SpecialMessagesKey|TitlesKey)))|DiscIdentKey|Genre(?:CodeKey|Key)|Key|LanguageKey|MCNISRCKey|PerformerKey|S(?:izeKey|ongwriterKey|pecialMessageKey)|T(?:OC(?:2Key|Key)|itleKey))|o(?:ntentModificationDate|pyrightFile)|reationDate)|D(?:VD(?:CopyrightInfoKey|TimestampKey)|ata(?:FormKey|Preparer)|e(?:faultDate|vice(?:AppearedNotification|BurnSpeed(?:BD1x|CD1x|DVD1x|HDDVD1x|Max|sKey)|C(?:an(?:TestWrite(?:CDKey|DVDKey)|UnderrunProtect(?:CDKey|DVDKey)|Write(?:BD(?:Key|R(?:EKey|Key))|CD(?:Key|R(?:Key|WKey|awKey)|SAOKey|T(?:AOKey|extKey))|DVD(?:DAOKey|Key|PlusR(?:DoubleLayerKey|Key|W(?:DoubleLayerKey|Key))|R(?:AMKey|DualLayerKey|Key|W(?:DualLayerKey|Key)))|HDDVD(?:Key|R(?:AMKey|DualLayerKey|Key|W(?:DualLayerKey|Key)))|I(?:SRCKey|ndexPointsKey)|Key))|urrentWriteSpeedKey)|DisappearedNotification|FirmwareRevisionKey|I(?:ORegistryEntryPathKey|s(?:BusyKey|TrayOpenKey))|LoadingMechanismCan(?:EjectKey|InjectKey|OpenKey)|M(?:aximumWriteSpeedKey|edia(?:B(?:SDNameKey|locks(?:FreeKey|OverwritableKey|UsedKey))|Class(?:BD|CD|DVD|HDDVD|Key|Unknown)|DoubleLayerL0DataZoneBlocksKey|I(?:nfoKey|s(?:AppendableKey|BlankKey|ErasableKey|OverwritableKey|ReservedKey))|S(?:essionCountKey|tate(?:InTransition|Key|MediaPresent|None))|T(?:rackCountKey|ype(?:BDR(?:E|OM)?|CDR(?:OM|W)?|DVD(?:PlusR(?:DoubleLayer|W(?:DoubleLayer)?)?|R(?:AM|DualLayer|OM|W(?:DualLayer)?)?)|HDDVDR(?:AM|DualLayer|OM|W(?:DualLayer)?)?|Key|Unknown))))|P(?:hysicalInterconnect(?:ATAPI|Fi(?:breChannel|reWire)|Key|Location(?:External|Internal|Key|Unknown)|SCSI|USB)|roductNameKey)|S(?:tatusChangedNotification|upportLevel(?:AppleS(?:hipping|upported)|Key|None|Unsupported|VendorSupported))|Track(?:InfoKey|RefsKey)|VendorNameKey|Write(?:BufferSizeKey|CapabilitiesKey))))|E(?:ffectiveDate|r(?:ase(?:StatusChangedNotification|Type(?:Complete|Key|Quick))|rorStatus(?:AdditionalSenseStringKey|Error(?:InfoStringKey|Key|StringKey)|Key|Sense(?:CodeStringKey|Key)))|xpirationDate)|FreeBlocksKey|HFSPlus(?:CatalogNodeID|TextEncodingHint)?|I(?:SO(?:9660(?:Level(?:One|Two)|VersionNumber)?|Level|MacExtensions|RockRidgeExtensions)|n(?:dexPointsKey|visible))|Joliet|M(?:a(?:c(?:ExtendedFinderFlags|Fi(?:le(?:Creator|Type)|nder(?:Flags|HideExtension))|IconLocation|ScrollPosition|Window(?:Bounds|View))|xBurnSpeedKey)|ediaCatalogNumberKey)|NextWritableAddressKey|P(?:osix(?:FileMode|GID|UID)|reGap(?:IsRequiredKey|LengthKey)|ublisher)|Re(?:cordingDate|fConCFTypeCallbacks)|S(?:CMSCopyright(?:Free|Protected(?:Copy|Original))|e(?:rialCopyManagementStateKey|ssion(?:FormatKey|NumberKey))|tatus(?:Current(?:S(?:essionKey|peedKey)|TrackKey)|EraseTypeKey|P(?:ercentCompleteKey|rogress(?:Current(?:KPS|XFactor)|InfoKey))|State(?:Done|Erasing|F(?:ailed|inishing)|Key|None|Preparing|Session(?:Close|Open)|Track(?:Close|Open|Write)|Verifying)|Total(?:SessionsKey|TracksKey))|u(?:bchannelDataForm(?:Key|None|Pack|Raw)|ppressMacSpecificFiles)|y(?:nchronousBehaviorKey|stemIdentifier))|Track(?:I(?:SRCKey|sEmptyKey)|LengthKey|ModeKey|NumberKey|Packet(?:SizeKey|Type(?:Fixed|Key|Variable))|StartAddressKey|Type(?:Closed|In(?:complete|visible)|Key|Reserved))|UDF(?:ApplicationIdentifierSuffix|ExtendedFilePermissions|InterchangeLevel|Max(?:InterchangeLevel|VolumeSequenceNumber)|PrimaryVolumeDescriptorNumber|RealTimeFile|V(?:ersion1(?:02|50)|olumeSe(?:quenceNumber|t(?:I(?:dentifier|mplementationUse)|Timestamp)))|WriteVersion)?|V(?:erificationType(?:Checksum|Key|None|ProduceAgain|ReceiveData)|olume(?:C(?:heckedDate|reationDate)|E(?:ffectiveDate|xpirationDate)|ModificationDate|Set))))|F(?:CFont(?:CGColorAttribute|Fa(?:ceAttribute|milyAttribute)|NameAttribute|SizeAttribute|VisibleNameAttribute)|ontPanel(?:A(?:TSUFontIDKey|ttribute(?:SizesKey|TagsKey|ValuesKey|sKey))|BackgroundColorAttributeName|Feature(?:SelectorsKey|TypesKey)|MouseTrackingState|Variation(?:AxesKey|ValuesKey)))|HI(?:Delegate(?:AfterKey|BeforeKey)|Object(?:CustomData(?:C(?:DEFProcIDKey|lassIDKey)|DelegateGroupParametersKey|Parameter(?:NamesKey|TypesKey|ValuesKey)|SuperClassIDKey)|InitParam(?:Description|Event(?:Name|Type)|UserName))|T(?:extViewClassID|oolboxVersionNumber)|View(?:MenuContentID|Window(?:C(?:loseBoxID|o(?:llapseBoxID|ntentID))|GrowBoxID|T(?:itleID|oolbar(?:ButtonID|ID))|ZoomBoxID)))|IO(?:MasterPortDefault|Surface(?:AllocSize|BytesPer(?:Element|Row)|CacheMode|Element(?:Height|Width)|Height|Offset|P(?:ixelFormat|lane(?:B(?:ase|ytesPer(?:Element|Row))|Element(?:Height|Width)|Height|Info|Offset|Size|Width))|Width))|JSClassDefinitionEmpty|LSQuarantine(?:Agent(?:BundleIdentifierKey|NameKey)|DataURLKey|OriginURLKey|T(?:imeStampKey|ype(?:CalendarEventAttachment|EmailAttachment|InstantMessageAttachment|Key|Other(?:Attachment|Download)|WebDownload)))|M(?:D(?:Attribute(?:AllValues|DisplayValues|MultiValued|Name|ReadOnlyValues|Type)|ExporterAvaliable|Item(?:A(?:cquisitionM(?:ake|odel)|l(?:bum|titude)|p(?:erture|pl(?:eLoop(?:Descriptors|s(?:KeyFilterType|LoopMode|RootKey))|icationCategories))|ttributeChangeDate|u(?:di(?:ences|o(?:BitRate|ChannelCount|EncodingApplication|SampleRate|TrackNumber))|thor(?:Addresses|EmailAddresses|s)))|BitsPerSample|C(?:FBundleIdentifier|ameraOwner|ity|o(?:decs|lorSpace|m(?:ment|poser)|nt(?:actKeywords|ent(?:CreationDate|ModificationDate|Type(?:Tree)?)|ributors)|pyright|untry|verage)|reator)|D(?:ateAdded|e(?:liveryType|scription)|i(?:rector|splayName)|ownloadedDate|u(?:eDate|rationSeconds))|E(?:XIF(?:GPSVersion|Version)|ditors|mailAddresses|ncodingApplications|x(?:ecutable(?:Architectures|Platform)|posure(?:Mode|Program|TimeS(?:econds|tring))))|F(?:Number|S(?:C(?:ontentChangeDate|reationDate)|HasCustomIcon|I(?:nvisible|s(?:ExtensionHidden|Stationery))|Label|N(?:ame|odeCount)|Owner(?:GroupID|UserID)|Size)|inderComment|lashOnOff|o(?:calLength(?:35mm)?|nts))|G(?:PS(?:AreaInformation|D(?:OP|ateStamp|est(?:Bearing|Distance|L(?:atitude|ongitude))|ifferental)|M(?:apDatum|easureMode)|ProcessingMethod|Status|Track)|enre)|H(?:asAlphaChannel|eadline)|I(?:SOSpeed|dentifier|mageDirection|n(?:formation|st(?:antMessageAddresses|ructions))|s(?:ApplicationManaged|GeneralMIDISequence|LikelyJunk))|K(?:ey(?:Signature|words)|ind)|L(?:a(?:nguages|stUsedDate|titude|yerNames)|ensModel|ongitude|yricist)|M(?:axAperture|e(?:diaTypes|teringMode)|usical(?:Genre|Instrument(?:Category|Name)))|N(?:amedLocation|umberOfPages)|Or(?:ganizations|i(?:entation|ginal(?:Format|Source)))|P(?:a(?:ge(?:Height|Width)|rticipants|th)|erformers|honeNumbers|ixel(?:Count|Height|Width)|ro(?:ducer|fileName|jects)|ublishers)|R(?:e(?:c(?:ipient(?:Addresses|EmailAddresses|s)|ording(?:Date|Year))|dEyeOnOff|solution(?:HeightDPI|WidthDPI))|ights)|S(?:ecurityMethod|peed|t(?:a(?:rRating|teOrProvince)|reamable)|ubject)|T(?:e(?:mpo|xtContent)|heme|i(?:me(?:Signature|stamp)|tle)|otalBitRate)|URL|V(?:ersion|ideoBitRate)|Wh(?:ereFroms|iteBalance))|Label(?:AddedNotification|BundleURL|C(?:hangedNotification|ontentChangeDate)|DisplayName|I(?:con(?:Data|UUID)|sMutuallyExclusiveSetMember)|Kind(?:IsMutuallyExclusiveSetKey|VisibilityKey)?|RemovedNotification|SetsFinderColor|UUID|Visibility)|P(?:rivateVisibility|ublicVisibility)|Query(?:Did(?:FinishNotification|UpdateNotification)|ProgressNotification|ResultContentRelevance|Scope(?:AllIndexed|Computer(?:Indexed)?|Home|Network(?:Indexed)?)|Update(?:AddedItems|ChangedItems|RemovedItems)))|IDI(?:ObjectType_ExternalMask|Property(?:AdvanceScheduleTimeMuSec|C(?:anRoute|onnectionUniqueID)|D(?:eviceID|isplayName|river(?:DeviceEditorApp|Owner|Version))|I(?:mage|s(?:Broadcast|DrumMachine|E(?:ffectUnit|mbeddedEntity)|Mixer|Sampler))|M(?:a(?:nufacturer|x(?:ReceiveChannels|SysExSpeed|TransmitChannels))|odel)|Name|Offline|P(?:anDisruptsStereo|rivate)|Receive(?:Channels|s(?:BankSelect(?:LSB|MSB)|Clock|MTC|Notes|ProgramChanges))|S(?:ingleRealtimeEntity|upports(?:GeneralMIDI|MMC|ShowControl))|Transmit(?:Channels|s(?:BankSelect(?:LSB|MSB)|Clock|MTC|Notes|ProgramChanges))|UniqueID)))|QL(?:PreviewPropertyTextEncodingNameKey|ThumbnailOption(?:IconModeKey|ScaleFactorKey))|S(?:C(?:BondStatusDevice(?:AggregationStatus|Collecting|Distributing)|Comp(?:AnyRegex|Global|HostNames|Interface|Network|S(?:ervice|ystem)|Users)|DynamicStore(?:Domain(?:File|P(?:lugin|refs)|S(?:etup|tate))|Prop(?:Net(?:Interfaces|Primary(?:Interface|Service)|ServiceIDs)|Setup(?:CurrentSet|LastUpdated))|UseSessionKeys)|Ent(?:Net(?:6to4|AirPort|D(?:HCP|NS)|Ethernet|FireWire|I(?:P(?:Sec|v(?:4|6))|nterface)|L(?:2TP|ink)|Modem|P(?:PP(?:Serial|oE)?|roxies)|SMB)|UsersConsoleUser)|Network(?:Interface(?:IPv4|Type(?:6to4|B(?:luetooth|ond)|Ethernet|FireWire|I(?:EEE80211|P(?:Sec|v4)|rDA)|L2TP|Modem|PPP|Serial|VLAN|WWAN))|ProtocolType(?:DNS|IPv(?:4|6)|Proxies|SMB))|Pr(?:ef(?:CurrentSet|NetworkServices|S(?:ets|ystem))|op(?:InterfaceName|MACAddress|Net(?:6to4Relay|DNS(?:DomainName|Options|S(?:e(?:arch(?:Domains|Order)|rver(?:Addresses|Port|Timeout))|ortList|upplementalMatch(?:Domains|Orders)))|EthernetM(?:TU|edia(?:Options|SubType))|I(?:P(?:Sec(?:AuthenticationMethod|ConnectTime|Local(?:Certificate|Identifier(?:Type)?)|RemoteAddress|S(?:haredSecret(?:Encryption)?|tatus)|XAuth(?:Enabled|Name|Password(?:Encryption)?))|v(?:4(?:Addresses|BroadcastAddresses|ConfigMethod|D(?:HCPClientID|estAddresses)|Router|SubnetMasks)|6(?:Addresses|ConfigMethod|DestAddresses|Flags|PrefixLength|Router)))|nterface(?:DeviceName|Hardware|SubType|Type|s))|L(?:2TP(?:IPSecSharedSecret(?:Encryption)?|Transport)|ink(?:Active|Detaching)|ocalHostName)|Modem(?:AccessPointName|Connect(?:Speed|ion(?:Personality|Script))|D(?:ataCompression|evice(?:ContextID|Model|Vendor)|ialMode)|ErrorCorrection|Hold(?:CallWaitingAudibleAlert|DisconnectOnAnswer|Enabled|Reminder(?:Time)?)|Note|PulseDial|Spe(?:aker|ed))|OverridePrimary|P(?:PP(?:A(?:CSPEnabled|uth(?:Name|P(?:assword(?:Encryption)?|ro(?:mpt|tocol))))|C(?:CP(?:Enabled|MPPE(?:128Enabled|40Enabled))|o(?:mm(?:AlternateRemoteAddress|ConnectDelay|DisplayTerminalWindow|Re(?:dial(?:Count|Enabled|Interval)|moteAddress)|TerminalScript|UseTerminalScript)|nnectTime))|D(?:eviceLastCause|i(?:alOnDemand|sconnect(?:On(?:FastUserSwitch|Idle(?:Timer)?|Logout|Sleep)|Time)))|I(?:PCP(?:CompressionVJ|UsePeerDNS)|dleReminder(?:Timer)?)|L(?:CP(?:Compression(?:ACField|PField)|Echo(?:Enabled|Failure|Interval)|M(?:RU|TU)|ReceiveACCM|TransmitACCM)|astCause|ogfile)|OverridePrimary|RetryConnectTime|S(?:essionTimer|tatus)|UseSessionTimer|VerboseLogging)|roxies(?:Exc(?:eptionsList|ludeSimpleHostnames)|FTP(?:Enable|P(?:assive|ort|roxy))|Gopher(?:Enable|P(?:ort|roxy))|HTTP(?:Enable|P(?:ort|roxy)|S(?:Enable|P(?:ort|roxy)))|ProxyAuto(?:Config(?:Enable|JavaScript|URLString)|DiscoveryEnable)|RTSP(?:Enable|P(?:ort|roxy))|SOCKS(?:Enable|P(?:ort|roxy))))|S(?:MB(?:NetBIOSN(?:ame|odeType)|W(?:INSAddresses|orkgroup))|erviceOrder))|SystemComputerName(?:Encoding)?|UserDefinedName|Version))|Resv(?:Inactive|Link)|ValNet(?:I(?:P(?:Sec(?:AuthenticationMethod(?:Certificate|Hybrid|SharedSecret)|LocalIdentifierTypeKeyID|SharedSecretEncryptionKeychain|XAuthPasswordEncryption(?:Keychain|Prompt))|v(?:4ConfigMethod(?:Automatic|BOOTP|DHCP|INFORM|LinkLocal|Manual|PPP)|6ConfigMethod(?:6to4|Automatic|LinkLocal|Manual|RouterAdvertisement)))|nterface(?:SubType(?:L2TP|PPP(?:Serial|oE))|Type(?:6to4|Ethernet|FireWire|IPSec|PPP)))|L2TP(?:IPSecSharedSecretEncryptionKeychain|TransportIP(?:Sec)?)|ModemDialMode(?:IgnoreDialTone|Manual|WaitForDialTone)|PPPAuthP(?:asswordEncryption(?:Keychain|Token)|ro(?:mpt(?:After|Before)|tocol(?:CHAP|EAP|MSCHAP(?:1|2)|PAP)))|SMBNetBIOSNodeType(?:Broadcast|Hybrid|Mixed|Peer)))|K(?:EndTermChars|M(?:aximumTerms|inTermLength)|ProximityIndexing|S(?:t(?:artTermChars|opWords)|ubstitutions)|TermChars)|ec(?:A(?:CLAuthorization(?:Any|De(?:crypt|lete|rive)|E(?:ncrypt|xport(?:Clear|Wrapped))|GenKey|Import(?:Clear|Wrapped)|Keychain(?:Create|Delete|Item(?:Delete|Insert|Modify|Read))|Login|MAC|Sign)|ttr(?:A(?:cc(?:ess|ount)|pplication(?:Label|Tag)|uthenticationType(?:D(?:PA|efault)|HT(?:MLForm|TP(?:Basic|Digest))|MSN|NTLM|RPA)?)|C(?:an(?:De(?:crypt|rive)|Encrypt|Sign|Unwrap|Verify|Wrap)|ertificate(?:Encoding|Type)|omment|reat(?:ionDate|or))|Description|EffectiveKeySize|Generic|Is(?:Extractable|Invisible|Negative|Permanent|Sensitive|suer)|Key(?:Class(?:P(?:rivate|ublic)|Symmetric)?|SizeInBits|Type(?:3DES|AES|CAST|D(?:ES|SA)|ECDSA|R(?:C(?:2|4)|SA))?)|Label|ModificationDate|P(?:RF(?:HmacAlgSHA(?:1|2(?:24|56)|384|512))?|ath|ort|rotocol(?:A(?:FP|ppleTalk)|DAAP|EPPC|FTP(?:Account|Proxy|S)?|HTTP(?:Proxy|S(?:Proxy)?)?|I(?:MAP(?:S)?|PP|RC(?:S)?)|LDAP(?:S)?|NNTP(?:S)?|POP3(?:S)?|RTSP(?:Proxy)?|S(?:M(?:B|TP)|OCKS|SH)|Telnet(?:S)?)?|ublicKeyHash)|Rounds|S(?:alt|e(?:curityDomain|r(?:ialNumber|v(?:er|ice)))|ubject(?:KeyID)?)|Type))|Base(?:32Encoding|64Encoding)|C(?:FError(?:Architecture|GuestAttributes|InfoPlist|Pat(?:h|tern)|Re(?:quirementSyntax|source(?:A(?:dded|ltered)|Missing|S(?:eal|ideband))))|lass(?:Certificate|GenericPassword|I(?:dentity|nternetPassword)|Key)?|o(?:de(?:Attribute(?:Architecture|BundleVersion|Subarchitecture|UniversalFileOffset)|Info(?:C(?:MS|dHashes|ertificates|hangedFiles)|D(?:esignatedRequirement|igestAlgorithm(?:s)?)|Entitlements(?:Dict)?|F(?:lags|ormat)|I(?:dentifier|mplicitDesignatedRequirement)|MainExecutable|P(?:List|latformIdentifier)|R(?:equirement(?:Data|s)|untimeVersion)|S(?:ource|tatus)|T(?:eamIdentifier|ime(?:stamp)?|rust)|Unique))|mpressionRatio))|D(?:ecodeTypeAttribute|igest(?:HMAC(?:KeyAttribute|MD5|SHA(?:1|2))|LengthAttribute|MD(?:2|4|5)|SHA(?:1|2)|TypeAttribute))|Enc(?:ode(?:LineLengthAttribute|TypeAttribute)|rypt(?:Key|ionMode))|GuestAttribute(?:A(?:rchitecture|udit)|Canonical|DynamicCode(?:InfoPlist)?|Hash|MachPort|Pid|Subarchitecture)|I(?:VKey|dentityDomain(?:Default|KerberosKDC)|mport(?:Export(?:Access|Keychain|Passphrase)|Item(?:CertChain|Identity|KeyID|Label|Trust))|nputIs(?:AttributeName|Digest|PlainText|Raw))|KeyAttributeName|LineLength(?:64|76)|M(?:atch(?:CaseInsensitive|DiacriticInsensitive|EmailAddressIfPresent|I(?:ssuers|temList)|Limit(?:All|One)?|Policy|S(?:earchList|ubject(?:Contains|EndsWith|StartsWith|WholeString))|TrustedOnly|ValidOnDate|WidthInsensitive)|ode(?:C(?:BCKey|FBKey)|ECBKey|NoneKey|OFBKey))|OID(?:A(?:DC_CERT_POLICY|PPLE_(?:CERT_POLICY|E(?:KU_(?:CODE_SIGNING(?:_DEV)?|ICHAT_(?:ENCRYPTION|SIGNING)|RESOURCE_SIGNING|SYSTEM_IDENTITY)|XTENSION(?:_(?:A(?:AI_INTERMEDIATE|DC_(?:APPLE_SIGNING|DEV_SIGNING)|PPLE(?:ID_INTERMEDIATE|_SIGNING))|CODE_SIGNING|I(?:NTERMEDIATE_MARKER|TMS_INTERMEDIATE)|WWDR_INTERMEDIATE))?))|uthority(?:InfoAccess|KeyIdentifier))|B(?:asicConstraints|iometricInfo)|C(?:SSMKeyStruct|ert(?:Issuer|ificatePolicies)|lientAuth|o(?:llectiveSt(?:ateProvinceName|reetAddress)|mmonName|untryName)|rl(?:DistributionPoints|Number|Reason))|D(?:OTMAC_CERT_(?:E(?:MAIL_(?:ENCRYPT|SIGN)|XTENSION)|IDENTITY|POLICY)|e(?:ltaCrlIndicator|scription))|E(?:KU_IPSec|mail(?:Address|Protection)|xtended(?:KeyUsage(?:Any)?|UseCodeSigning))|GivenName|HoldInstructionCode|I(?:nvalidityDate|ssu(?:erAltName|ingDistributionPoint(?:s)?))|K(?:ERBv5_PKINIT_KP_(?:CLIENT_AUTH|KDC)|eyUsage)|LocalityName|M(?:S_NTPrincipalName|icrosoftSGC)|N(?:ameConstraints|etscape(?:Cert(?:Sequence|Type)|SGC))|O(?:CSPSigning|rganization(?:Name|alUnitName))|P(?:olicy(?:Constraints|Mappings)|rivateKeyUsagePeriod)|QC_Statements|S(?:er(?:ialNumber|verAuth)|t(?:ateProvinceName|reetAddress)|u(?:bject(?:AltName|DirectoryAttributes|EmailAddress|InfoAccess|KeyIdentifier|Picture|SignatureBitmap)|rname))|Ti(?:meStamping|tle)|UseExemptions|X509V(?:1(?:Certificate(?:IssuerUniqueId|SubjectUniqueId)|IssuerName(?:CStruct|LDAP|Std)?|S(?:erialNumber|ignature(?:Algorithm(?:Parameters|TBS)?|CStruct|Struct)?|ubject(?:Name(?:CStruct|LDAP|Std)?|PublicKey(?:Algorithm(?:Parameters)?|CStruct)?))|V(?:alidityNot(?:After|Before)|ersion))|3(?:Certificate(?:CStruct|Extension(?:C(?:Struct|ritical)|Id|Struct|Type|Value|s(?:CStruct|Struct))|NumberOfExtensions)?|SignedCertificate(?:CStruct)?)))|P(?:adding(?:Key|NoneKey|PKCS(?:1Key|5Key|7Key))|olicy(?:Apple(?:CodeSigning|EAP|I(?:DValidation|Psec)|PKINIT(?:Client|Server)|S(?:MIME|SL)|X509Basic)|Client|KU_(?:CRLSign|D(?:ataEncipherment|ecipherOnly|igitalSignature)|EncipherOnly|Key(?:Agreement|CertSign|Encipherment)|NonRepudiation)|MacAppStoreReceipt|Name|Oid)|roperty(?:Key(?:L(?:abel|ocalizedLabel)|Type|Value)|Type(?:Dat(?:a|e)|Error|S(?:ection|tring|uccess)|Title|URL|Warning)))|R(?:andomDefault|eturn(?:Attributes|Data|PersistentRef|Ref))|S(?:haredPassword|ignatureAttributeName)|Transform(?:A(?:bort(?:AttributeName|OriginatorKey)|ction(?:Attribute(?:Notification|Validation)|CanExecute|ExternalizeExtraData|Finalize|InternalizeExtraData|ProcessData|StartingExecution))|DebugAttributeName|ErrorDomain|InputAttributeName|OutputAttributeName|PreviousErrorKey|TransformName)|Use(?:ItemList|Keychain)|Value(?:Data|PersistentRef|Ref)|ZLibEncoding)|peech(?:Audio(?:GraphProperty|UnitProperty)|C(?:haracterModeProperty|ommand(?:DelimiterProperty|Prefix|Suffix)|urrentVoiceProperty)|Dictionary(?:Abbreviations|Entry(?:Phonemes|Spelling)|LocaleIdentifier|ModificationDate|Pronunciations)|Error(?:C(?:FCallBack|allback(?:CharacterOffset|SpokenString)|ount)|Newest(?:CharacterOffset)?|Oldest(?:CharacterOffset)?|sProperty)|InputModeProperty|Mode(?:Literal|Normal|Phoneme|T(?:ext|une))|N(?:o(?:EndingProsody|SpeechInterrupt)|umberModeProperty)|OutputTo(?:AudioDeviceProperty|ExtAudioFileProperty|FileURLProperty)|P(?:honeme(?:CallBack|Info(?:Example|Hilite(?:End|Start)|Opcode|Symbol)|OptionsProperty|SymbolsProperty)|itch(?:BaseProperty|ModProperty)|reflightThenPause)|R(?:ateProperty|e(?:centSyncProperty|fConProperty|setProperty))|S(?:peechDoneCallBack|tatus(?:NumberOfCharactersLeft|Output(?:Busy|Paused)|P(?:honemeCode|roperty))|yn(?:cCallBack|thesizerInfo(?:Identifier|Manufacturer|Property|Version)))|TextDoneCallBack|Vo(?:ice(?:Creator|ID)|lumeProperty)|WordCFCallBack))|T(?:IS(?:Category(?:InkInputSource|KeyboardInputSource|PaletteInputSource)|Notify(?:EnabledKeyboardInputSourcesChanged|SelectedKeyboardInputSourceChanged)|Property(?:BundleID|I(?:con(?:ImageURL|Ref)|nput(?:ModeID|Source(?:Category|I(?:D|s(?:ASCIICapable|Enable(?:Capable|d)|Select(?:Capable|ed)))|Languages|Type)))|LocalizedName|UnicodeKeyLayoutData)|Type(?:CharacterPalette|Ink|Keyboard(?:InputM(?:ethod(?:ModeEnabled|WithoutModes)|ode)|Layout|Viewer)))|XN(?:Action(?:Align(?:Center|Left|Right)|C(?:hange(?:Color|Font(?:Feature|Variation)?|GlyphVariation|S(?:ize|tyle)|TextPosition)|lear|ountOf(?:AllChanges|StyleChanges|TextChanges)|ut)|Drop|Move|Paste|Typing|UndoLast)|D(?:ataOption(?:CharacterEncodingKey|DocumentTypeKey)|ocumentAttribute(?:AuthorKey|C(?:o(?:m(?:mentKey|panyNameKey)|pyrightKey)|reationTimeKey)|EditorKey|KeywordsKey|ModificationTimeKey|SubjectKey|TitleKey))|MLTEDocumentType|PlainTextDocumentType|QuickTimeDocumentType|RTFDocumentType))|UT(?:ExportedTypeDeclarationsKey|ImportedTypeDeclarationsKey|T(?:agClass(?:FilenameExtension|MIMEType|NSPboardType|OSType)|ype(?:A(?:lias(?:File|Record)|ppl(?:e(?:ICNS|ProtectedMPEG4Audio)|ication(?:Bundle|File)?)|rchive|udio(?:visualContent)?)|B(?:MP|undle)|C(?:Header|PlusPlus(?:Header|Source)|Source|o(?:mpositeContent|n(?:formsToKey|t(?:act|ent))))|D(?:ata(?:base)?|escriptionKey|i(?:rectory|skImage))|Executable|F(?:ileURL|latRTFD|older|ramework)|GIF|HTML|I(?:CO|conFileKey|dentifierKey|mage|nkText|tem)|J(?:PEG(?:2000)?|avaSource)|M(?:P(?:3|EG(?:4(?:Audio)?)?)|essage|o(?:untPoint|vie))|ObjectiveC(?:PlusPlusSource|Source)|P(?:DF|ICT|NG|ackage|lainText)|QuickTime(?:Image|Movie)|R(?:TF(?:D)?|e(?:ferenceURLKey|solvable))|S(?:ourceCode|ymLink)|T(?:IFF|XNTextAndMultimediaData|agSpecificationKey|ext)|U(?:RL|TF(?:16(?:ExternalPlainText|PlainText)|8PlainText))|V(?:Card|ersionKey|ideo|olume)|WebArchive|XML))))|mach_task_self_|oid(?:A(?:d(?:CAIssuer|OCSP)|n(?:sip(?:384r1|521r1)|y(?:ExtendedKeyUsage|Policy))|uthority(?:InfoAccess|KeyIdentifier))|BasicConstraints|C(?:ertificatePolicies|o(?:mmonName|untryName)|rlDistributionPoints)|Description|E(?:cP(?:rime(?:192v1|256v1)|ubKey)|mailAddress|ntrustVersInfo|xtendedKeyUsage(?:C(?:lientAuth|odeSigning)|EmailProtection|IPSec|MicrosoftSGC|NetscapeSGC|OCSPSigning|ServerAuth|TimeStamping)?)|F(?:ee|riendlyName)|Google(?:EmbeddedSignedCertificateTimestamp|OCSPSignedCertificateTimestamp)|I(?:nhibitAnyPolicy|ssuerAltName)|KeyUsage|Local(?:KeyId|ityName)|M(?:SNTPrincipalName|d(?:2(?:Rsa)?|4(?:Rsa)?|5(?:Fee|Rsa)?))|N(?:ameConstraints|etscapeCertType)|Organization(?:Name|alUnitName)|P(?:olicy(?:Constraints|Mappings)|rivateKeyUsagePeriod)|Qt(?:Cps|UNotice)|Rsa|S(?:ha(?:1(?:Dsa(?:CommonOIW|OIW)?|Ecdsa|Fee|Rsa(?:OIW)?)?|2(?:24(?:Ecdsa|Rsa)?|56(?:Ecdsa|Rsa)?)|384(?:Ecdsa|Rsa)?|512(?:Ecdsa|Rsa)?)|tateOrProvinceName|ubject(?:AltName|InfoAccess|KeyIdentifier)))|v(?:m_page_(?:mask|s(?:hift|ize))|printf_stderr_func))\\b", + "name": "support.variable.c" }, { "match": "\\bkCF(?:Islamic(?:TabularCalendar|UmmAlQuraCalendar)|URL(?:AddedToDirectoryDateKey|DocumentIdentifierKey|GenerationIdentifierKey|QuarantinePropertiesKey))\\b", @@ -133,6 +349,10 @@ "match": "\\bkCFURL(?:CanonicalPathKey|Volume(?:Is(?:EncryptedKey|RootFileSystemKey)|Supports(?:CompressionKey|ExclusiveRenamingKey|FileCloningKey|SwapRenamingKey)))\\b", "name": "support.variable.cf.10.12.c" }, + { + "match": "\\bkCF(?:ErrorLocalizedFailureKey|URLVolume(?:AvailableCapacityFor(?:ImportantUsageKey|OpportunisticUsageKey)|Supports(?:AccessPermissionsKey|ImmutableFilesKey)))\\b", + "name": "support.variable.cf.10.13.c" + }, { "match": "\\bkCFURL(?:IsExcludedFromBackupKey|PathKey)\\b", "name": "support.variable.cf.10.8.c" @@ -142,7 +362,7 @@ "name": "support.variable.cf.10.9.c" }, { - "match": "\\bkCF(?:A(?:bsoluteTimeIntervalSince19(?:04|70)|llocator(?:Default|Malloc(?:Zone)?|Null|SystemDefault|UseContext))|B(?:oolean(?:False|True)|u(?:ddhistCalendar|ndle(?:DevelopmentRegionKey|ExecutableKey|I(?:dentifierKey|nfoDictionaryVersionKey)|LocalizationsKey|NameKey|VersionKey)))|C(?:hineseCalendar|o(?:pyString(?:BagCallBacks|DictionaryKeyCallBacks|SetCallBacks)|reFoundationVersionNumber))|DateFormatter(?:AMSymbol|Calendar(?:Name)?|D(?:efault(?:Date|Format)|oesRelativeDateFormattingKey)|EraSymbols|GregorianStartDate|IsLenient|LongEraSymbols|MonthSymbols|PMSymbol|QuarterSymbols|S(?:hort(?:MonthSymbols|QuarterSymbols|Standalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols)|WeekdaySymbols)|tandalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols))|T(?:imeZone|woDigitStartDate)|VeryShort(?:MonthSymbols|Standalone(?:MonthSymbols|WeekdaySymbols)|WeekdaySymbols)|WeekdaySymbols)|Error(?:D(?:escriptionKey|omain(?:Cocoa|Mach|OSStatus|POSIX))|FilePathKey|Localized(?:DescriptionKey|FailureReasonKey|RecoverySuggestionKey)|U(?:RLKey|nderlyingErrorKey))|GregorianCalendar|HebrewCalendar|I(?:SO8601Calendar|ndianCalendar|slamicC(?:alendar|ivilCalendar))|JapaneseCalendar|Locale(?:AlternateQuotation(?:BeginDelimiterKey|EndDelimiterKey)|C(?:alendar(?:Identifier)?|o(?:llat(?:ionIdentifier|orIdentifier)|untryCode)|urren(?:cy(?:Code|Symbol)|tLocaleDidChangeNotification))|DecimalSeparator|ExemplarCharacterSet|GroupingSeparator|Identifier|LanguageCode|MeasurementSystem|Quotation(?:BeginDelimiterKey|EndDelimiterKey)|ScriptCode|UsesMetricSystem|VariantCode)|N(?:otFound|u(?:ll|mber(?:Formatter(?:AlwaysShowDecimalSeparator|Currency(?:Code|DecimalSeparator|GroupingSeparator|Symbol)|De(?:cimalSeparator|faultFormat)|ExponentSymbol|FormatWidth|GroupingS(?:eparator|ize)|I(?:n(?:finitySymbol|ternationalCurrencySymbol)|sLenient)|M(?:ax(?:FractionDigits|IntegerDigits|SignificantDigits)|in(?:FractionDigits|IntegerDigits|SignificantDigits|usSign)|ultiplier)|N(?:aNSymbol|egative(?:Prefix|Suffix))|P(?:adding(?:Character|Position)|er(?:MillSymbol|centSymbol)|lusSign|ositive(?:Prefix|Suffix))|Rounding(?:Increment|Mode)|SecondaryGroupingSize|Use(?:GroupingSeparator|SignificantDigits)|ZeroSymbol)|N(?:aN|egativeInfinity)|PositiveInfinity)))|P(?:ersianCalendar|lugIn(?:DynamicRegist(?:erFunctionKey|rationKey)|FactoriesKey|TypesKey|UnloadFunctionKey)|references(?:Any(?:Application|Host|User)|Current(?:Application|Host|User)))|R(?:epublicOfChinaCalendar|unLoop(?:CommonModes|DefaultMode))|S(?:ocket(?:CommandKey|ErrorKey|NameKey|Re(?:gisterCommand|sultKey|trieveCommand)|ValueKey)|tr(?:eamProperty(?:AppendToFile|DataWritten|FileCurrentOffset|Socket(?:NativeHandle|Remote(?:HostName|PortNumber)))|ing(?:BinaryHeapCallBacks|Transform(?:FullwidthHalfwidth|HiraganaKatakana|Latin(?:Arabic|Cyrillic|Greek|H(?:angul|ebrew|iragana)|Katakana|Thai)|MandarinLatin|Strip(?:CombiningMarks|Diacritics)|To(?:Latin|UnicodeName|XMLHex)))))|T(?:imeZoneSystemTimeZoneDidChangeNotification|ype(?:ArrayCallBacks|BagCallBacks|Dictionary(?:KeyCallBacks|ValueCallBacks)|SetCallBacks))|U(?:RL(?:AttributeModificationDateKey|C(?:ontent(?:AccessDateKey|ModificationDateKey)|reationDateKey)|File(?:AllocatedSizeKey|Protection(?:Complete(?:Un(?:lessOpen|tilFirstUserAuthentication))?|Key|None)|Resource(?:IdentifierKey|Type(?:BlockSpecial|CharacterSpecial|Directory|Key|NamedPipe|Regular|S(?:ocket|ymbolicLink)|Unknown))|S(?:ecurityKey|izeKey))|HasHiddenExtensionKey|Is(?:AliasFileKey|DirectoryKey|ExecutableKey|HiddenKey|MountTriggerKey|PackageKey|Re(?:adableKey|gularFileKey)|Sy(?:mbolicLinkKey|stemImmutableKey)|U(?:biquitousItemKey|serImmutableKey)|VolumeKey|WritableKey)|KeysOfUnsetValuesKey|L(?:abelNumberKey|inkCountKey|ocalized(?:LabelKey|NameKey|TypeDescriptionKey))|NameKey|P(?:arentDirectoryURLKey|referredIOBlockSizeKey)|T(?:otalFile(?:AllocatedSizeKey|SizeKey)|ypeIdentifierKey)|UbiquitousItem(?:HasUnresolvedConflictsKey|Is(?:DownloadingKey|Upload(?:edKey|ingKey)))|Volume(?:AvailableCapacityKey|CreationDateKey|I(?:dentifierKey|s(?:AutomountedKey|BrowsableKey|EjectableKey|InternalKey|JournalingKey|LocalKey|Re(?:adOnlyKey|movableKey)))|Localized(?:FormatDescriptionKey|NameKey)|MaximumFileSizeKey|NameKey|ResourceCountKey|Supports(?:AdvisoryFileLockingKey|Case(?:PreservedNamesKey|SensitiveNamesKey)|ExtendedSecurityKey|HardLinksKey|JournalingKey|PersistentIDsKey|R(?:enamingKey|ootDirectoryDatesKey)|S(?:parseFilesKey|ymbolicLinksKey)|VolumeSizesKey|ZeroRunsKey)|TotalCapacityKey|U(?:RL(?:ForRemountingKey|Key)|UIDStringKey)))|serNotification(?:Al(?:ert(?:HeaderKey|MessageKey)|ternateButtonTitleKey)|CheckBoxTitlesKey|DefaultButtonTitleKey|IconURLKey|LocalizationURLKey|OtherButtonTitleKey|P(?:opUp(?:SelectionKey|TitlesKey)|rogressIndicatorValueKey)|SoundURLKey|TextField(?:TitlesKey|ValuesKey)))|XMLTreeError(?:Description|L(?:ineNumber|ocation)|StatusCode))\\b", + "match": "\\bkCF(?:A(?:bsoluteTimeIntervalSince19(?:04|70)|llocator(?:Default|Malloc(?:Zone)?|Null|SystemDefault|UseContext))|B(?:oolean(?:False|True)|u(?:ddhistCalendar|ndle(?:DevelopmentRegionKey|ExecutableKey|I(?:dentifierKey|nfoDictionaryVersionKey)|LocalizationsKey|NameKey|VersionKey)))|C(?:hineseCalendar|o(?:pyString(?:BagCallBacks|DictionaryKeyCallBacks|SetCallBacks)|reFoundationVersionNumber))|DateFormatter(?:AMSymbol|Calendar(?:Name)?|D(?:efault(?:Date|Format)|oesRelativeDateFormattingKey)|EraSymbols|GregorianStartDate|IsLenient|LongEraSymbols|MonthSymbols|PMSymbol|QuarterSymbols|S(?:hort(?:MonthSymbols|QuarterSymbols|Standalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols)|WeekdaySymbols)|tandalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols))|T(?:imeZone|woDigitStartDate)|VeryShort(?:MonthSymbols|Standalone(?:MonthSymbols|WeekdaySymbols)|WeekdaySymbols)|WeekdaySymbols)|Error(?:D(?:escriptionKey|omain(?:Cocoa|Mach|OSStatus|POSIX))|FilePathKey|Localized(?:DescriptionKey|FailureReasonKey|RecoverySuggestionKey)|U(?:RLKey|nderlyingErrorKey))|GregorianCalendar|HebrewCalendar|I(?:SO8601Calendar|ndianCalendar|slamicC(?:alendar|ivilCalendar))|JapaneseCalendar|Locale(?:AlternateQuotation(?:BeginDelimiterKey|EndDelimiterKey)|C(?:alendar(?:Identifier)?|o(?:llat(?:ionIdentifier|orIdentifier)|untryCode)|urren(?:cy(?:Code|Symbol)|tLocaleDidChangeNotification))|DecimalSeparator|ExemplarCharacterSet|GroupingSeparator|Identifier|LanguageCode|MeasurementSystem|Quotation(?:BeginDelimiterKey|EndDelimiterKey)|ScriptCode|UsesMetricSystem|VariantCode)|N(?:otFound|u(?:ll|mber(?:Formatter(?:AlwaysShowDecimalSeparator|Currency(?:Code|DecimalSeparator|GroupingSeparator|Symbol)|De(?:cimalSeparator|faultFormat)|ExponentSymbol|FormatWidth|GroupingS(?:eparator|ize)|I(?:n(?:finitySymbol|ternationalCurrencySymbol)|sLenient)|M(?:ax(?:FractionDigits|IntegerDigits|SignificantDigits)|in(?:FractionDigits|IntegerDigits|SignificantDigits|usSign)|ultiplier)|N(?:aNSymbol|egative(?:Prefix|Suffix))|P(?:adding(?:Character|Position)|er(?:MillSymbol|centSymbol)|lusSign|ositive(?:Prefix|Suffix))|Rounding(?:Increment|Mode)|SecondaryGroupingSize|Use(?:GroupingSeparator|SignificantDigits)|ZeroSymbol)|N(?:aN|egativeInfinity)|PositiveInfinity)))|P(?:ersianCalendar|lugIn(?:DynamicRegist(?:erFunctionKey|rationKey)|FactoriesKey|TypesKey|UnloadFunctionKey)|references(?:Any(?:Application|Host|User)|Current(?:Application|Host|User)))|R(?:epublicOfChinaCalendar|unLoop(?:CommonModes|DefaultMode))|S(?:ocket(?:CommandKey|ErrorKey|NameKey|Re(?:gisterCommand|sultKey|trieveCommand)|ValueKey)|tr(?:eam(?:ErrorDomainS(?:OCKS|SL)|Property(?:AppendToFile|DataWritten|FileCurrentOffset|S(?:OCKS(?:P(?:assword|roxy(?:Host|Port)?)|User|Version)|houldCloseNativeSocket|ocket(?:NativeHandle|Remote(?:HostName|PortNumber)|SecurityLevel)))|SocketS(?:OCKSVersion(?:4|5)|ecurityLevel(?:N(?:egotiatedSSL|one)|TLSv1)))|ing(?:BinaryHeapCallBacks|Transform(?:FullwidthHalfwidth|HiraganaKatakana|Latin(?:Arabic|Cyrillic|Greek|H(?:angul|ebrew|iragana)|Katakana|Thai)|MandarinLatin|Strip(?:CombiningMarks|Diacritics)|To(?:Latin|UnicodeName|XMLHex)))))|T(?:imeZoneSystemTimeZoneDidChangeNotification|ype(?:ArrayCallBacks|BagCallBacks|Dictionary(?:KeyCallBacks|ValueCallBacks)|SetCallBacks))|U(?:RL(?:AttributeModificationDateKey|C(?:ontent(?:AccessDateKey|ModificationDateKey)|reationDateKey)|File(?:AllocatedSizeKey|Protection(?:Complete(?:Un(?:lessOpen|tilFirstUserAuthentication))?|Key|None)|Resource(?:IdentifierKey|Type(?:BlockSpecial|CharacterSpecial|Directory|Key|NamedPipe|Regular|S(?:ocket|ymbolicLink)|Unknown))|S(?:ecurityKey|izeKey))|HasHiddenExtensionKey|Is(?:AliasFileKey|DirectoryKey|ExecutableKey|HiddenKey|MountTriggerKey|PackageKey|Re(?:adableKey|gularFileKey)|Sy(?:mbolicLinkKey|stemImmutableKey)|U(?:biquitousItemKey|serImmutableKey)|VolumeKey|WritableKey)|KeysOfUnsetValuesKey|L(?:abelNumberKey|inkCountKey|ocalized(?:LabelKey|NameKey|TypeDescriptionKey))|NameKey|P(?:arentDirectoryURLKey|referredIOBlockSizeKey)|T(?:otalFile(?:AllocatedSizeKey|SizeKey)|ypeIdentifierKey)|UbiquitousItem(?:HasUnresolvedConflictsKey|Is(?:DownloadingKey|Upload(?:edKey|ingKey)))|Volume(?:AvailableCapacityKey|CreationDateKey|I(?:dentifierKey|s(?:AutomountedKey|BrowsableKey|EjectableKey|InternalKey|JournalingKey|LocalKey|Re(?:adOnlyKey|movableKey)))|Localized(?:FormatDescriptionKey|NameKey)|MaximumFileSizeKey|NameKey|ResourceCountKey|Supports(?:AdvisoryFileLockingKey|Case(?:PreservedNamesKey|SensitiveNamesKey)|ExtendedSecurityKey|HardLinksKey|JournalingKey|PersistentIDsKey|R(?:enamingKey|ootDirectoryDatesKey)|S(?:parseFilesKey|ymbolicLinksKey)|VolumeSizesKey|ZeroRunsKey)|TotalCapacityKey|U(?:RL(?:ForRemountingKey|Key)|UIDStringKey)))|serNotification(?:Al(?:ert(?:HeaderKey|MessageKey|TopMostKey)|ternateButtonTitleKey)|CheckBoxTitlesKey|DefaultButtonTitleKey|IconURLKey|KeyboardTypesKey|LocalizationURLKey|OtherButtonTitleKey|P(?:opUp(?:SelectionKey|TitlesKey)|rogressIndicatorValueKey)|SoundURLKey|TextField(?:TitlesKey|ValuesKey)))|XMLTreeError(?:Description|L(?:ineNumber|ocation)|StatusCode))\\b", "name": "support.variable.cf.c" }, { @@ -157,6 +377,22 @@ "match": "\\bkCGColor(?:ConversionBlackPointCompensation|Space(?:Extended(?:Gray|Linear(?:Gray|SRGB)|SRGB)|Linear(?:Gray|SRGB)))\\b", "name": "support.variable.quartz.10.12.c" }, + { + "match": "\\bkCG(?:Color(?:ConversionTRCSize|SpaceGenericLab)|PDF(?:ContextAccessPermissions|Outline(?:Children|Destination(?:Rect)?|Title)))\\b", + "name": "support.variable.quartz.10.13.c" + }, + { + "match": "\\bkCGColorSpace(?:DisplayP3_HLG|ExtendedLinear(?:DisplayP3|ITUR_2020)|ITUR_2020_HLG)\\b", + "name": "support.variable.quartz.10.14.c" + }, + { + "match": "\\bkCGPDFTagProperty(?:A(?:ctualText|lternativeText)|LanguageText|TitleText)\\b", + "name": "support.variable.quartz.10.15.c" + }, + { + "match": "\\bkCGColorSpace(?:DisplayP3_PQ|ITUR_2020_PQ)\\b", + "name": "support.variable.quartz.10.16.c" + }, { "match": "\\bkCGDisplayS(?:howDuplicateLowResolutionModes|tream(?:ColorSpace|DestinationRect|MinimumFrameTime|PreserveAspectRatio|QueueDepth|S(?:howCursor|ourceRect)|YCbCrMatrix(?:_(?:ITU_R_(?:601_4|709_2)|SMPTE_240M_1995))?))\\b", "name": "support.variable.quartz.10.8.c" @@ -169,6 +405,17 @@ "repository": { "functions": { "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.10.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AudioFileReadPackets|LS(?:C(?:anRefAcceptItem|opy(?:ApplicationForMIMEType|DisplayNameForRef|Item(?:Attribute(?:s)?|InfoForRef)|KindStringFor(?:MIMEType|Ref|TypeInfo)))|FindApplicationForInfo|GetApplicationFor(?:I(?:nfo|tem)|URL)|Open(?:Application|F(?:SRef|romRefSpec)|ItemsWithRole|URLsWithRole)|RegisterFSRef|S(?:et(?:ExtensionHiddenForRef|ItemAttribute)|haredFileListI(?:nsertItemFSRef|temResolve)))|launch_(?:data_(?:a(?:lloc|rray_(?:get_(?:count|index)|set_index))|copy|dict_(?:get_count|i(?:nsert|terate)|lookup|remove)|free|get_(?:bool|errno|fd|integer|machport|opaque(?:_size)?|real|string|type)|new_(?:bool|fd|integer|machport|opaque|real|string)|set_(?:bool|fd|integer|machport|opaque|real|string))|get_fd|msg))\\b)" + }, { "captures": { "1": { @@ -180,6 +427,17 @@ }, "match": "(\\s*)(\\bCF(?:AbsoluteTime(?:AddGregorianUnits|Get(?:D(?:ayOf(?:Week|Year)|ifferenceAsGregorianUnits)|GregorianDate|WeekOfYear))|GregorianDate(?:GetAbsoluteTime|IsValid)|PropertyList(?:Create(?:From(?:Stream|XMLData)|XMLData)|WriteToStream))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.11.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AudioHardwareService(?:AddPropertyListener|GetPropertyData(?:Size)?|HasProperty|IsPropertySettable|RemovePropertyListener|SetPropertyData)|CF(?:FTPCreateParsedResourceListing|ReadStreamCreate(?:For(?:HTTPRequest|StreamedHTTPRequest)|WithFTPURL)|WriteStreamCreateWithFTPURL)|LS(?:Copy(?:DisplayNameForURL|ItemInfoForURL|KindStringForURL)|Get(?:ExtensionInfo|HandlerOptionsForContentType)|S(?:et(?:ExtensionHiddenForURL|HandlerOptionsForContentType)|haredFileList(?:AddObserver|C(?:opy(?:Property|Snapshot)|reate)|Get(?:SeedValue|TypeID)|I(?:nsertItemURL|tem(?:Copy(?:DisplayName|IconRef|Property|ResolvedURL)|Get(?:ID|TypeID)|Move|Remove|SetProperty))|Remove(?:AllItems|Observer)|Set(?:Authorization|Property))))|SSLSetEncryptionCertificate)\\b)" + }, { "captures": { "1": { @@ -202,6 +460,17 @@ }, "match": "(\\s*)(\\bCGDisplayModeCopyPixelEncoding\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.12.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:OS(?:Atomic(?:A(?:dd(?:32(?:Barrier)?|64(?:Barrier)?)|nd32(?:Barrier|Orig(?:Barrier)?)?)|CompareAndSwap(?:32(?:Barrier)?|64(?:Barrier)?|Int(?:Barrier)?|Long(?:Barrier)?|Ptr(?:Barrier)?)|Decrement(?:32(?:Barrier)?|64(?:Barrier)?)|Increment(?:32(?:Barrier)?|64(?:Barrier)?)|Or32(?:Barrier|Orig(?:Barrier)?)?|TestAnd(?:Clear(?:Barrier)?|Set(?:Barrier)?)|Xor32(?:Barrier|Orig(?:Barrier)?)?)|MemoryBarrier|SpinLock(?:Lock|Try|Unlock))|SecCertificateCopyNormalized(?:IssuerContent|SubjectContent)|os_log_is_(?:debug_enabled|enabled))\\b)" + }, { "captures": { "1": { @@ -211,7 +480,106 @@ "name": "invalid.deprecated.10.12.support.function.clib.c" } }, - "match": "(\\s*)(\\bsyscall\\b)" + "match": "(\\s*)(\\b(?:arc4random_addrandom|syscall)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.13.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:C(?:FNetDiagnostic(?:C(?:opyNetworkStatusPassively|reateWith(?:Streams|URL))|DiagnoseProblemInteractively|SetName)|MSDecoderSetSearchKeychain)|DisposeSRCallBackUPP|GetIconRefFromFileInfo|InvokeSRCallBackUPP|NewSRCallBackUPP|Re(?:adIconFromFSRef|gisterIconRefFromFSRef)|S(?:R(?:Add(?:LanguageObject|Text)|C(?:ancelRecognition|hangeLanguageObject|loseRecognitionSystem|o(?:ntinueRecognition|untItems))|Draw(?:RecognizedText|Text)|EmptyLanguageObject|Get(?:IndexedItem|LanguageModel|Property|Reference)|Idle|New(?:Language(?:Model|ObjectFrom(?:DataFile|Handle))|P(?:ath|hrase)|Recognizer|Word)|OpenRecognitionSystem|P(?:rocess(?:Begin|End)|utLanguageObjectInto(?:DataFile|Handle))|Re(?:leaseObject|move(?:IndexedItem|LanguageObject))|S(?:et(?:IndexedItem|LanguageModel|Property)|pe(?:ak(?:AndDrawText|Text)|echBusy)|t(?:artListening|op(?:Listening|Speech))))|ec(?:CertificateCopySerialNumber|Keychain(?:CopyAccess|SetAccess)|TrustSetKeychains))|UnregisterIconRef|os_trace_(?:debug_enabled|info_enabled|type_enabled))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.13.support.function.quartz.c" + } + }, + "match": "(\\s*)(\\bCGColorSpaceC(?:opyICCProfile|reateWithICCProfile)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.14.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:CVOpenGL(?:Buffer(?:Attach|Create|Get(?:Attributes|TypeID)|Pool(?:Create(?:OpenGLBuffer)?|Get(?:Attributes|OpenGLBufferAttributes|TypeID)|Re(?:lease|tain))|Re(?:lease|tain))|Texture(?:Cache(?:Create(?:TextureFromImage)?|Flush|GetTypeID|Re(?:lease|tain))|Get(?:CleanTexCoords|Name|T(?:arget|ypeID))|IsFlipped|Re(?:lease|tain)))|DR(?:AudioTrackCreate|F(?:SObjectGetRealFSRef|ileCreateReal|olderCreateReal))|SecCertificateCopyPublicKey)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.15.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AcquireIconRef|C(?:C_MD(?:2(?:_(?:Final|Init|Update))?|4(?:_(?:Final|Init|Update))?|5(?:_(?:Final|Init|Update))?)|TFont(?:CreateWithQuickdrawInstance|Manager(?:RegisterFontsForURLs|UnregisterFontsForURLs))|ompositeIconRef)|Get(?:CustomIconsEnabled|IconRef(?:From(?:Component|Folder|IconFamilyPtr|TypeInfo)|Owners)?)|Is(?:DataAvailableInIconRef|IconRefComposite|ValidIconRef)|LSCopy(?:AllHandlersForURLScheme|DefaultHandlerForURLScheme)|OverrideIconRef|Re(?:gisterIconRefFromIconFamily|leaseIconRef|moveIconRefOverride)|S(?:SL(?:AddDistinguishedName|C(?:lose|o(?:ntextGetTypeID|py(?:ALPNProtocols|CertificateAuthorities|DistinguishedNames|PeerTrust|RequestedPeerName(?:Length)?))|reateContext)|Get(?:BufferedReadSize|C(?:lientCertificateState|onnection)|D(?:atagramWriteSize|iffieHellmanParams)|EnabledCiphers|MaxDatagramRecordSize|N(?:egotiated(?:Cipher|ProtocolVersion)|umber(?:EnabledCiphers|SupportedCiphers))|P(?:eer(?:DomainName(?:Length)?|ID)|rotocolVersionM(?:ax|in))|S(?:ession(?:Option|State)|upportedCiphers))|Handshake|Re(?:Handshake|ad)|Set(?:ALPNProtocols|C(?:ertificate(?:Authorities)?|lientSideAuthenticate|onnection)|D(?:atagramHelloCookie|iffieHellmanParams)|E(?:nabledCiphers|rror)|IOFuncs|MaxDatagramRecordSize|OCSPResponse|P(?:eer(?:DomainName|ID)|rotocolVersionM(?:ax|in))|Session(?:Config|Option|TicketsEnabled))|Write)|e(?:cTrust(?:Evaluate(?:Async)?|edApplication(?:C(?:opyData|reateFromPath)|SetData))|tCustomIconsEnabled))|UpdateIconRef|sec_protocol_(?:metadata_get_negotiated_(?:ciphersuite|protocol_version)|options_(?:add_tls_ciphersuite(?:_group)?|set_tls_(?:diffie_hellman_parameters|m(?:ax_version|in_version)))))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.15.support.function.cf.c" + } + }, + "match": "(\\s*)(\\bCF(?:Bundle(?:CloseBundleResourceMap|OpenBundleResource(?:Files|Map))|URLCopyParameterString)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.15.support.function.quartz.c" + } + }, + "match": "(\\s*)(\\bCGColorSpaceIsHDR\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.3.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:DisposeGetScrapDataUPP|InvokeGetScrapDataUPP|NewGetScrapDataUPP|ReleaseFolder)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.4.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AH(?:GotoMainTOC|RegisterHelpBook)|CFNetServiceRe(?:gister|solve)|Dispose(?:CaretHookUPP|DrawHookUPP|EOLHookUPP|Hi(?:ghHookUPP|tTestHookUPP)|NWidthHookUPP|T(?:E(?:ClickLoopUPP|DoTextUPP|FindWordUPP|RecalcUPP)|XNActionKeyMapperUPP|extWidthHookUPP)|URL(?:NotifyUPP|SystemEventUPP)|WidthHookUPP)|In(?:s(?:Time|XTime|tall(?:TimeTask|XTimeTask))|voke(?:CaretHookUPP|DrawHookUPP|EOLHookUPP|Hi(?:ghHookUPP|tTestHookUPP)|NWidthHookUPP|T(?:E(?:ClickLoopUPP|DoTextUPP|FindWordUPP|RecalcUPP)|XNActionKeyMapperUPP|extWidthHookUPP)|URL(?:NotifyUPP|SystemEventUPP)|WidthHookUPP))|LM(?:Get(?:ApFontID|SysFontSize)|Set(?:ApFontID|SysFontFam))|New(?:CaretHookUPP|DrawHookUPP|EOLHookUPP|Hi(?:ghHookUPP|tTestHookUPP)|NWidthHookUPP|T(?:E(?:ClickLoopUPP|DoTextUPP|FindWordUPP|RecalcUPP)|XNActionKeyMapperUPP|extWidthHookUPP)|URL(?:NotifyUPP|SystemEventUPP)|WidthHookUPP)|P(?:L(?:pos|str(?:c(?:at|hr|mp|py)|len|nc(?:at|mp|py)|pbrk|rchr|s(?:pn|tr)))|rimeTime(?:Task)?)|R(?:emoveTimeTask|mvTime)|SKSearch(?:Group(?:C(?:opyIndexes|reate)|GetTypeID)|Results(?:C(?:opyMatchingTerms|reateWith(?:Documents|Query))|Get(?:Count|InfoInRange|TypeID))))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.5.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:S(?:GetSourceStyles|SetSourceStyles)|UGraph(?:CountNodeConnections|Get(?:ConnectionInfo|N(?:ode(?:Connections|Info)|umberOfConnections))|NewNode)|udio(?:ConverterFillBuffer|Device(?:AddIOProc|Re(?:ad|moveIOProc))|FileComponent(?:DataIsThisFormat|FileIsThisFormat)))|Dispose(?:AVL(?:CompareItemsUPP|DisposeItemUPP|ItemSizeUPP|WalkUPP)|Drag(?:DrawingUPP|ReceiveHandlerUPP|SendDataUPP|TrackingHandlerUPP)|List(?:ClickLoopUPP|DefUPP|SearchUPP)|Menu(?:ItemDrawingUPP|TitleDrawingUPP)|S(?:crapPromiseKeeperUPP|leepQUPP)|Theme(?:ButtonDrawUPP|EraseUPP|IteratorUPP|TabTitleDrawUPP)|Window(?:PaintUPP|TitleDrawingUPP))|Get(?:NameFromSoundBank|ScriptManagerVariable)|Invoke(?:AVL(?:CompareItemsUPP|DisposeItemUPP|ItemSizeUPP|WalkUPP)|Drag(?:DrawingUPP|ReceiveHandlerUPP|SendDataUPP|TrackingHandlerUPP)|List(?:ClickLoopUPP|DefUPP|SearchUPP)|Menu(?:ItemDrawingUPP|TitleDrawingUPP)|S(?:crapPromiseKeeperUPP|leepQUPP)|Theme(?:ButtonDrawUPP|EraseUPP|IteratorUPP|TabTitleDrawUPP)|Window(?:PaintUPP|TitleDrawingUPP))|Music(?:Device(?:PrepareInstrument|ReleaseInstrument)|Sequence(?:LoadSMF(?:DataWithFlags|WithFlags)|Save(?:MIDIFile|SMFData)))|New(?:AVL(?:CompareItemsUPP|DisposeItemUPP|ItemSizeUPP|WalkUPP)|Drag(?:DrawingUPP|ReceiveHandlerUPP|SendDataUPP|TrackingHandlerUPP)|List(?:ClickLoopUPP|DefUPP|SearchUPP)|Menu(?:ItemDrawingUPP|TitleDrawingUPP)|S(?:crapPromiseKeeperUPP|leepQUPP)|Theme(?:ButtonDrawUPP|EraseUPP|IteratorUPP|TabTitleDrawUPP)|Window(?:PaintUPP|TitleDrawingUPP))|S(?:CNetworkInterfaceRefreshConfiguration|etScriptManagerVariable))\\b)" }, { "captures": { @@ -235,6 +603,17 @@ }, "match": "(\\s*)(\\bCG(?:ContextDrawPDFDocument|PDFDocumentGet(?:ArtBox|BleedBox|CropBox|MediaBox|RotationAngle|TrimBox))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.6.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:Audio(?:Device(?:AddPropertyListener|GetProperty(?:Info)?|RemovePropertyListener|SetProperty)|File(?:C(?:omponent(?:Create|Initialize|OpenFile)|reate)|Initialize|Open)|Hardware(?:AddPropertyListener|GetProperty(?:Info)?|RemovePropertyListener|SetProperty)|Stream(?:AddPropertyListener|GetProperty(?:Info)?|RemovePropertyListener|SetProperty))|Button|DisposeKCCallbackUPP|ExtAudioFile(?:CreateNew|Open)|FlushEvents|I(?:nvokeKCCallbackUPP|sCmdChar)|KC(?:Add(?:AppleSharePassword|Callback|GenericPassword|I(?:nternetPassword(?:WithPath)?|tem))|C(?:hangeSettings|o(?:pyItem|untKeychains)|reateKeychain)|DeleteItem|Find(?:AppleSharePassword|FirstItem|GenericPassword|InternetPassword(?:WithPath)?|NextItem)|Get(?:Attribute|D(?:ata|efaultKeychain)|IndKeychain|Keychain(?:ManagerVersion|Name)?|Status)|IsInteractionAllowed|Lock|Make(?:AliasFromKCRef|KCRefFrom(?:Alias|FSRef))|NewItem|Re(?:lease(?:Item|Keychain|Search)|moveCallback)|Set(?:Attribute|D(?:ata|efaultKeychain)|InteractionAllowed)|U(?:nlock|pdateItem))|Munger|New(?:KCCallbackUPP|MusicTrackFrom)|S(?:CNetworkCheckReachabilityBy(?:Address|Name)|ecHost(?:CreateGuest|RemoveGuest|Se(?:lect(?:Guest|edGuest)|t(?:GuestStatus|HostingPort))))|UC(?:CreateTextBreakLocator|DisposeTextBreakLocator|FindTextBreak)|kc(?:add(?:applesharepassword|genericpassword|internetpassword(?:withpath)?)|createkeychain|find(?:applesharepassword|genericpassword|internetpassword(?:withpath)?)|getkeychainname|unlock))\\b)" + }, { "captures": { "1": { @@ -246,6 +625,17 @@ }, "match": "(\\s*)(\\bCG(?:ConfigureDisplayMode|Display(?:AvailableModes|BestModeForParameters(?:AndRefreshRate)?|CurrentMode|SwitchToMode)|EnableEventStateCombining|FontCreateWithPlatformFont|InhibitLocalEvents|Post(?:KeyboardEvent|MouseEvent|ScrollWheelEvent)|SetLocalEvents(?:FilterDuringSuppressionState|SuppressionInterval))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.7.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:Au(?:dioHardware(?:AddRunLoopSource|RemoveRunLoopSource)|thorization(?:CopyPrivilegedReference|ExecuteWithPrivileges))|C(?:MSEncode(?:rSetEncapsulatedContentType)?|SSM_(?:AC_(?:AuthCompute|PassThrough)|C(?:L_(?:C(?:ert(?:Abort(?:Cache|Query)|C(?:ache|reateTemplate)|DescribeFormat|G(?:et(?:All(?:Fields|TemplateFields)|First(?:CachedFieldValue|FieldValue)|KeyInfo|Next(?:CachedFieldValue|FieldValue))|roup(?:FromVerifiedBundle|ToSignedBundle))|Sign|Verify(?:WithKey)?)|rl(?:A(?:bort(?:Cache|Query)|ddCert)|C(?:ache|reateTemplate)|DescribeFormat|Get(?:All(?:CachedRecordFields|Fields)|First(?:CachedFieldValue|FieldValue)|Next(?:CachedFieldValue|FieldValue))|RemoveCert|S(?:etFields|ign)|Verify(?:WithKey)?))|FreeField(?:Value|s)|IsCertInC(?:achedCrl|rl)|PassThrough)|SP_(?:C(?:hangeLogin(?:Acl|Owner)|reate(?:AsymmetricContext|D(?:eriveKeyContext|igestContext)|KeyGenContext|MacContext|PassThroughContext|RandomGenContext|S(?:ignatureContext|ymmetricContext)))|Get(?:Login(?:Acl|Owner)|OperationalStatistics)|Log(?:in|out)|ObtainPrivateKeyFromPublicKey|PassThrough)|hangeKey(?:Acl|Owner))|D(?:L_(?:Authenticate|C(?:hangeDb(?:Acl|Owner)|reateRelation)|D(?:ata(?:AbortQuery|Delete|Get(?:F(?:irst|romUniqueRecordId)|Next)|Insert|Modify)|b(?:C(?:lose|reate)|Delete|Open)|estroyRelation)|Free(?:NameList|UniqueRecord)|GetDb(?:Acl|Name(?:FromHandle|s)|Owner)|PassThrough)|e(?:cryptData(?:Final|Init(?:P)?|P|Update)?|leteContext(?:Attributes)?|riveKey)|igestData(?:Clone|Final|Init|Update)?)|EncryptData(?:Final|Init(?:P)?|P|Update)?|Free(?:Context|Key)|Ge(?:nerate(?:AlgorithmParams|Key(?:P(?:air(?:P)?)?)?|Mac(?:Final|Init|Update)?|Random)|t(?:APIMemoryFunctions|Context(?:Attribute)?|Key(?:Acl|Owner)|ModuleGUIDFromHandle|Privilege|SubserviceUIDFromHandle|TimeValue))|In(?:it|troduce)|ListAttachedModuleManagers|Module(?:Attach|Detach|Load|Unload)|Query(?:KeySizeInBits|Size)|Retrieve(?:Counter|UniqueId)|S(?:et(?:Context|Privilege)|ignData(?:Final|Init|Update)?)|T(?:P_(?:ApplyCrlToDb|C(?:ert(?:CreateTemplate|G(?:etAllTemplateFields|roup(?:Construct|Prune|ToTupleGroup|Verify))|Re(?:claim(?:Abort|Key)|moveFromCrlTemplate|voke)|Sign)|onfirmCredResult|rl(?:CreateTemplate|Sign|Verify))|Form(?:Request|Submit)|PassThrough|Re(?:ceiveConfirmation|trieveCredResult)|SubmitCredRequest|TupleGroupToCertGroup)|erminate)|U(?:n(?:introduce|wrapKey(?:P)?)|pdateContextAttributes)|Verify(?:D(?:ata(?:Final|Init|Update)?|evice)|Mac(?:Final|Init|Update)?)|WrapKey(?:P)?)|reateThreadPool)|Dispose(?:Debugger(?:DisposeThreadUPP|NewThreadUPP|ThreadSchedulerUPP)|Thread(?:EntryUPP|S(?:chedulerUPP|witchUPP)|TerminationUPP)?)|Get(?:CurrentThread|DefaultThreadStackSize|Thread(?:CurrentTaskRef|State(?:GivenTaskRef)?))|I(?:C(?:Add(?:MapEntry|Profile)|Begin|C(?:ount(?:MapEntries|Pr(?:ef|ofiles))|reateGURLEvent)|Delete(?:MapEntry|Pr(?:ef|ofile))|E(?:ditPreferences|nd)|FindPrefHandle|Get(?:C(?:onfigName|urrentProfile)|DefaultPref|Ind(?:MapEntry|Pr(?:ef|ofile))|MapEntry|P(?:erm|r(?:ef(?:Handle)?|ofileName))|Seed|Version)|LaunchURL|Map(?:Entries(?:Filename|TypeCreator)|Filename|TypeCreator)|ParseURL|S(?:e(?:ndGURLEvent|t(?:CurrentProfile|MapEntry|Pr(?:ef(?:Handle)?|ofileName)))|t(?:art|op)))|nvoke(?:Debugger(?:DisposeThreadUPP|NewThreadUPP|ThreadSchedulerUPP)|Thread(?:EntryUPP|S(?:chedulerUPP|witchUPP)|TerminationUPP))|sMetric)|M(?:DS_(?:In(?:itialize|stall)|Terminate|Uninstall)|P(?:A(?:llocate(?:Aligned|TaskStorageIndex)?|rmTimer)|BlockC(?:lear|opy)|C(?:a(?:ncelTimer|useNotification)|reate(?:CriticalRegion|Event|Notification|Queue|Semaphore|T(?:ask|imer))|urrentTaskID)|D(?:e(?:allocateTaskStorageIndex|l(?:ayUntil|ete(?:CriticalRegion|Event|Notification|Queue|Semaphore|Timer)))|isposeTaskException)|E(?:nterCriticalRegion|x(?:it(?:CriticalRegion)?|tractTaskState))|Free|Get(?:AllocatedBlockSize|Next(?:CpuID|TaskID)|TaskStorageValue)|ModifyNotification(?:Parameters)?|NotifyQueue|Processors(?:Scheduled)?|Re(?:gisterDebugger|moteCall(?:CFM)?)|S(?:et(?:E(?:vent|xceptionHandler)|QueueReserve|T(?:ask(?:St(?:ate|orageValue)|Weight)|imerNotify))|ignalSemaphore)|T(?:askIsPreemptive|erminateTask|hrowException)|UnregisterDebugger|Wait(?:ForEvent|On(?:Queue|Semaphore))|Yield)|usicTrackNewExtendedControlEvent)|New(?:Debugger(?:DisposeThreadUPP|NewThreadUPP|ThreadSchedulerUPP)|Thread(?:EntryUPP|S(?:chedulerUPP|witchUPP)|TerminationUPP)?)|Se(?:c(?:A(?:CL(?:C(?:opySimpleContents|reateFromSimpleContents)|GetAuthorizations|Set(?:Authorizations|SimpleContents))|ccess(?:C(?:opySelectedACLList|reateFromOwnerAndACL)|GetOwnerAndACL))|Certificate(?:C(?:opyPreference|reateFromData)|Get(?:AlgorithmID|CLHandle|Data|Issuer|Subject|Type)|SetPreference)|Identity(?:CopyPreference|Se(?:arch(?:C(?:opyNext|reate)|GetTypeID)|tPreference))|Key(?:CreatePair|Ge(?:nerate|tC(?:S(?:PHandle|SMKey)|redentials))|chain(?:Get(?:CSPHandle|DLDBHandle)|Item(?:Export|Get(?:DLDBHandle|UniqueRecordID)|Import)|Search(?:C(?:opyNext|reateFromAttributes)|GetTypeID)))|Policy(?:Get(?:OID|TPHandle|Value)|Se(?:arch(?:C(?:opyNext|reate)|GetTypeID)|tValue))|Trust(?:Get(?:CssmResult(?:Code)?|Result|TPHandle)|SetParameters))|t(?:DebuggerNotificationProcs|Thread(?:ReadyGivenTaskRef|S(?:cheduler|tate(?:EndCritical)?|witcher)|Terminator)))|Thread(?:BeginCritical|CurrentStackSpace|EndCritical)|YieldTo(?:AnyThread|Thread))\\b)" + }, { "captures": { "1": { @@ -257,6 +647,17 @@ }, "match": "(\\s*)(\\bCFURLEnumeratorGetSourceDidChange\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.8.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:TS(?:CreateFontQueryRunLoopSource|Font(?:A(?:ctivateFrom(?:FileReference|Memory)|pplyFunction)|Deactivate|F(?:amily(?:ApplyFunction|FindFrom(?:Name|QuickDrawName)|Get(?:Encoding|Generation|Name|QuickDrawName)|Iterator(?:Create|Next|Re(?:lease|set)))|indFrom(?:Container|Name|PostScriptName))|Get(?:AutoActivationSettingForApplication|Container(?:FromFileReference)?|F(?:ileReference|ontFamilyResource)|G(?:eneration|lobalAutoActivationSetting)|HorizontalMetrics|Name|PostScriptName|Table(?:Directory)?|VerticalMetrics)|I(?:sEnabled|terator(?:Create|Next|Re(?:lease|set)))|Notif(?:ication(?:Subscribe|Unsubscribe)|y)|Set(?:AutoActivationSettingForApplication|Enabled|GlobalAutoActivationSetting))|GetGeneration)|bsolute(?:DeltaTo(?:Duration|Nanoseconds)|To(?:Duration|Nanoseconds))|dd(?:A(?:bsoluteToAbsolute|tomic(?:16|8)?)|CollectionItem(?:Hdl)?|DurationToAbsolute|FolderDescriptor|NanosecondsToAbsolute|Resource))|B(?:atteryCount|it(?:And(?:Atomic(?:16|8)?)?|Clr|Not|Or(?:Atomic(?:16|8)?)?|S(?:et|hift)|Tst|Xor(?:Atomic(?:16|8)?)?))|C(?:S(?:Copy(?:MachineName|UserName)|GetComponentsThreadMode|SetComponentsThreadMode)|a(?:llComponent(?:C(?:anDo|lose)|Dispatch|Function(?:WithStorage(?:ProcInfo)?)?|Get(?:MPWorkFunction|PublicResource)|Open|Register|Target|Unregister|Version)|ptureComponent)|hangedResource|lo(?:neCollection|se(?:Component(?:ResFile)?|ResFile))|o(?:llectionTagExists|mpareAndSwap|pyCollection|reEndian(?:FlipData|GetFlipper|InstallFlipper)|unt(?:1(?:Resources|Types)|Co(?:llection(?:Items|Owners|Tags)|mponent(?:Instances|s))|Resources|T(?:aggedCollectionItems|ypes)))|ur(?:ResFile|rentProcessorSpeed))|D(?:e(?:bugAssert|crementAtomic(?:16|8)?|l(?:ay|e(?:gateComponentCall|teGestaltValue))|queue|t(?:achResource(?:File)?|ermineIfPathIsEnclosedByFolder))|ispose(?:Co(?:llection(?:ExceptionUPP|FlattenUPP)?|mponent(?:FunctionUPP|MPWorkFunctionUPP|RoutineUPP))|De(?:bug(?:AssertOutputHandlerUPP|Component(?:CallbackUPP)?)|ferredTaskUPP)|ExceptionHandlerUPP|F(?:NSubscriptionUPP|SVolume(?:EjectUPP|MountUPP|UnmountUPP)|olderManagerNotificationUPP)|GetMissingComponentResourceUPP|Handle|IOCompletionUPP|Ptr|ResErrUPP|S(?:electorFunctionUPP|peech(?:DoneUPP|ErrorUPP|PhonemeUPP|SyncUPP|TextDoneUPP|WordUPP))|TimerUPP)|urationTo(?:Absolute|Nanoseconds))|E(?:mpty(?:Collection|Handle)|nqueue)|F(?:N(?:GetDirectoryForSubscription|Notify(?:All|ByPath)?|Subscribe(?:ByPath)?|Unsubscribe)|S(?:AllocateFork|C(?:a(?:ncelVolumeOperation|talogSearch)|lose(?:Fork|Iterator)|o(?:mpareFSRefs|py(?:AliasInfo|D(?:ADiskForVolume|iskIDForVolume)|Object(?:Async|Sync)|URLForVolume))|reate(?:DirectoryUnicode|F(?:ile(?:AndOpenForkUnicode|Unicode)|ork)|Res(?:File|ourceF(?:ile|ork))|StringFromHFSUniStr|VolumeOperation))|D(?:e(?:lete(?:Fork|Object)|termineIfRefIsEnclosedByFolder)|isposeVolumeOperation)|E(?:jectVolume(?:Async|Sync)|xchangeObjects)|F(?:i(?:le(?:Operation(?:C(?:ancel|opyStatus|reate)|GetTypeID|ScheduleWithRunLoop|UnscheduleFromRunLoop)|Security(?:C(?:opyAccessControlList|reate(?:WithFSPermissionInfo)?)|Get(?:Group(?:UUID)?|Mode|Owner(?:UUID)?|TypeID)|RefCreateCopy|Set(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)))|ndFolder)|lush(?:Fork|Volume)|ollowFinderAlias)|Get(?:Async(?:EjectStatus|MountStatus|UnmountStatus)|CatalogInfo(?:Bulk)?|DataForkName|Fork(?:CBInfo|Position|Size)|HFSUniStrFromString|ResourceForkName|TemporaryDirectoryForReplaceObject|Volume(?:ForD(?:ADisk|iskID)|Info|MountInfo(?:Size)?|Parms))|I(?:s(?:AliasFile|FSRefValid)|terateForks)|LockRange|M(?:a(?:keFSRefUnicode|tchAliasBulk)|o(?:unt(?:LocalVolume(?:Async|Sync)|ServerVolume(?:Async|Sync))|veObject(?:Async|Sync|ToTrash(?:Async|Sync))?))|NewAlias(?:FromPath|Minimal(?:Unicode)?|Unicode)?|Open(?:Fork|Iterator|OrphanResFile|Res(?:File|ourceFile))|Path(?:CopyObject(?:Async|Sync)|FileOperationCopyStatus|GetTemporaryDirectoryForReplaceObject|M(?:akeRef(?:WithOptions)?|oveObject(?:Async|Sync|ToTrash(?:Async|Sync)))|ReplaceObject)|Re(?:adFork|fMakePath|nameUnicode|placeObject|so(?:lve(?:Alias(?:File(?:WithMountFlags)?|WithMountFlags)?|NodeID)|urceFileAlreadyOpen))|Set(?:CatalogInfo|Fork(?:Position|Size)|VolumeInfo)|U(?:n(?:l(?:inkObject|ockRange)|mountVolume(?:Async|Sync))|pdateAlias)|VolumeMount|WriteFork)|i(?:nd(?:Folder|NextComponent)|x(?:2(?:Frac|Long|X)|ATan2|Div|Mul|R(?:atio|ound)))|latten(?:Collection(?:ToHdl)?|PartialCollection)|rac(?:2(?:Fix|X)|Cos|Div|Mul|S(?:in|qrt)))|Ge(?:stalt|t(?:1(?:Ind(?:Resource|Type)|NamedResource|Resource)|Alias(?:Size(?:FromPtr)?|UserType(?:FromPtr)?)|C(?:PUSpeed|o(?:llection(?:DefaultAttributes|ExceptionProc|Item(?:Hdl|Info)?|RetainCount)|mponent(?:In(?:dString|fo|stance(?:Error|Storage))|ListModSeed|Public(?:IndString|Resource(?:List)?)|Re(?:fcon|source)|TypeModSeed)))|Debug(?:ComponentInfo|OptionInfo)|Folder(?:NameUnicode|Types)|HandleSize|Ind(?:Resource|Type|exedCollection(?:Item(?:Hdl|Info)?|Tag))|Ma(?:cOSStatus(?:CommentString|ErrorString)|xResourceSize)|N(?:amedResource|e(?:wCollection|xt(?:FOND|ResourceFile)))|PtrSize|Res(?:Attrs|FileAttrs|Info|ource(?:SizeOnDisk)?)|SpeechInfo|T(?:aggedCollectionItem(?:Info)?|opResourceFile)))|H(?:ClrRBit|GetState|Lock(?:Hi)?|Set(?:RBit|State)|Unlock|and(?:AndHand|ToHand)|omeResFile)|I(?:dentifyFolder|n(?:crementAtomic(?:16|8)?|s(?:ertResourceFile|tall(?:DebugAssertOutputHandler|ExceptionHandler))|v(?:alidateFolderDescriptorCache|oke(?:Co(?:llection(?:ExceptionUPP|FlattenUPP)|mponent(?:MPWorkFunctionUPP|RoutineUPP))|De(?:bug(?:AssertOutputHandlerUPP|ComponentCallbackUPP)|ferredTaskUPP)|ExceptionHandlerUPP|F(?:NSubscriptionUPP|SVolume(?:EjectUPP|MountUPP|UnmountUPP)|olderManagerNotificationUPP)|GetMissingComponentResourceUPP|IOCompletionUPP|ResErrUPP|S(?:electorFunctionUPP|peech(?:DoneUPP|ErrorUPP|PhonemeUPP|SyncUPP|TextDoneUPP|WordUPP))|TimerUPP)))|s(?:H(?:andleValid|eapValid)|PointerValid))|L(?:M(?:Get(?:BootDrive|IntlSpec|MemErr|Res(?:Err|Load)|SysMap|TmpResLoad)|Set(?:BootDrive|IntlSpec|MemErr|Res(?:Err|Load)|Sys(?:FontSize|Map)|TmpResLoad))|o(?:adResource|cale(?:CountNames|Get(?:IndName|Name)|Operation(?:CountLocales|GetLocales))|ng2Fix))|M(?:PSetTaskType|aximumProcessorSpeed|emError|i(?:croseconds|nimumProcessorSpeed))|N(?:anosecondsTo(?:Absolute|Duration)|ew(?:Co(?:llection(?:ExceptionUPP|FlattenUPP)?|mponent(?:FunctionUPP|MPWorkFunctionUPP|RoutineUPP))|De(?:bug(?:AssertOutputHandlerUPP|Component(?:CallbackUPP)?|Option)|ferredTaskUPP)|E(?:mptyHandle|xceptionHandlerUPP)|F(?:NSubscriptionUPP|SVolume(?:EjectUPP|MountUPP|UnmountUPP)|olderManagerNotificationUPP)|Ge(?:staltValue|tMissingComponentResourceUPP)|Handle(?:Clear)?|IOCompletionUPP|Ptr(?:Clear)?|ResErrUPP|S(?:electorFunctionUPP|peech(?:DoneUPP|ErrorUPP|PhonemeUPP|SyncUPP|TextDoneUPP|WordUPP))|TimerUPP))|Open(?:A(?:Component(?:ResFile)?|DefaultComponent)|Component(?:ResFile)?|DefaultComponent)|P(?:B(?:AllocateFork(?:Async|Sync)|C(?:atalogSearch(?:Async|Sync)|lose(?:Fork(?:Async|Sync)|Iterator(?:Async|Sync))|ompareFSRefs(?:Async|Sync)|reate(?:DirectoryUnicode(?:Async|Sync)|F(?:ile(?:AndOpenForkUnicode(?:Async|Sync)|Unicode(?:Async|Sync))|ork(?:Async|Sync))))|Delete(?:Fork(?:Async|Sync)|Object(?:Async|Sync))|ExchangeObjects(?:Async|Sync)|F(?:S(?:CopyFile(?:Async|Sync)|ResolveNodeID(?:Async|Sync))|lush(?:Fork(?:Async|Sync)|Volume(?:Async|Sync)))|Get(?:CatalogInfo(?:Async|Bulk(?:Async|Sync)|Sync)|Fork(?:CBInfo(?:Async|Sync)|Position(?:Async|Sync)|Size(?:Async|Sync))|VolumeInfo(?:Async|Sync))|IterateForks(?:Async|Sync)|M(?:akeFSRefUnicode(?:Async|Sync)|oveObject(?:Async|Sync))|Open(?:Fork(?:Async|Sync)|Iterator(?:Async|Sync))|Re(?:adFork(?:Async|Sync)|nameUnicode(?:Async|Sync))|Set(?:CatalogInfo(?:Async|Sync)|Fork(?:Position(?:Async|Sync)|Size(?:Async|Sync))|VolumeInfo(?:Async|Sync))|UnlinkObject(?:Async|Sync)|WriteFork(?:Async|Sync)|X(?:LockRange(?:Async|Sync)|UnlockRange(?:Async|Sync)))|tr(?:AndHand|To(?:Hand|XHand))|urgeCollection(?:Tag)?)|Re(?:a(?:d(?:Location|PartialResource)|llocateHandle)|coverHandle|gisterComponent(?:FileRef(?:Entries)?|Resource(?:File)?)?|lease(?:Collection|Resource)|move(?:CollectionItem|FolderDescriptor|IndexedCollectionItem|Resource)|place(?:GestaltValue|IndexedCollectionItem(?:Hdl)?)|s(?:Error|olveComponentAlias)|tainCollection)|S(?:64Compare|SL(?:GetProtocolVersion|SetProtocolVersion)|e(?:cTranformCustomGetAttribute|t(?:AliasUserType(?:WithPtr)?|Co(?:llection(?:DefaultAttributes|ExceptionProc|ItemInfo)|mponent(?:Instance(?:Error|Storage)|Refcon))|De(?:bugOptionValue|faultComponent)|GestaltValue|HandleSize|IndexedCollectionItemInfo|PtrSize|Res(?:Attrs|FileAttrs|Info|Load|Purge|ourceSize)|SpeechInfo))|leepQ(?:Install|Remove)|peak(?:Buffer|String|Text)|ub(?:AbsoluteFromAbsolute|DurationFromAbsolute|NanosecondsFromAbsolute)|ysError)|T(?:askLevel|e(?:mpNewHandle|stAnd(?:Clear|Set)|xtToPhonemes)|ickCount)|U(?:64Compare|n(?:captureComponent|flattenCollection(?:FromHdl)?|ique(?:1ID|ID)|registerComponent|signedFixedMulDiv)|p(?:Time|date(?:ResFile|SystemActivity))|se(?:Dictionary|ResFile))|W(?:S(?:Get(?:CFTypeIDFromWSTypeID|WSTypeIDFromCFType)|Method(?:Invocation(?:Add(?:DeserializationOverride|SerializationOverride)|C(?:opy(?:P(?:arameters|roperty)|Serialization)|reate(?:FromSerialization)?)|GetTypeID|Invoke|S(?:cheduleWithRunLoop|et(?:CallBack|P(?:arameters|roperty)))|UnscheduleFromRunLoop)|ResultIsFault)|ProtocolHandler(?:C(?:opy(?:FaultDocument|Property|Re(?:plyD(?:ictionary|ocument)|questD(?:ictionary|ocument)))|reate)|GetTypeID|Set(?:DeserializationOverride|Property|SerializationOverride)))|ide(?:Add|BitShift|Compare|Divide|Multiply|Negate|S(?:hift|quareRoot|ubtract)|WideDivide)|rite(?:PartialResource|Resource))|X2F(?:ix|rac)|annuity|compound|d(?:ec2(?:f|l|num|s(?:tr)?)|tox80)|getaudit|num(?:2dec|tostring)|r(?:andomx|elation)|s(?:etaudit|tr2dec)|x80tod)\\b)" + }, { "captures": { "1": { @@ -290,6 +691,17 @@ }, "match": "(\\s*)(\\bCG(?:Re(?:gisterScreenRefreshCallback|leaseScreenRefreshRects)|Screen(?:RegisterMoveCallback|UnregisterMoveCallback)|UnregisterScreenRefreshCallback|W(?:aitForScreen(?:RefreshRects|UpdateRects)|indowServerCFMachPort))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.9.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AX(?:APIEnabled|MakeProcessTrusted|UIElementPostKeyboardEvent)|CopyProcessName|ExitToShell|Get(?:CurrentProcess|FrontProcess|NextProcess|Process(?:BundleLocation|ForPID|Information|PID))|IsProcessVisible|KillProcess|LaunchApplication|ProcessInformationCopyDictionary|S(?:SL(?:Copy(?:PeerCertificates|TrustedRoots)|DisposeContext|Get(?:Allows(?:AnyRoot|Expired(?:Certs|Roots))|EnableCertVerify|ProtocolVersionEnabled|RsaBlinding)|NewContext|Set(?:Allows(?:AnyRoot|Expired(?:Certs|Roots))|EnableCertVerify|ProtocolVersionEnabled|RsaBlinding|TrustedRoots))|ameProcess|e(?:c(?:ChooseIdentity(?:AsSheet)?|DisplayCertificate(?:Group)?|EditTrust(?:AsSheet)?|Policy(?:CreateWithOID|SetProperties))|tFrontProcess(?:WithOptions)?)|howHideProcess)|WakeUpProcess)\\b)" + }, { "captures": { "1": { @@ -334,6 +746,116 @@ }, "match": "(\\s*)(\\bCG(?:C(?:ontextS(?:electFont|how(?:Glyphs(?:AtPoint|WithAdvances)?|Text(?:AtPoint)?))|ursorIs(?:DrawnInFramebuffer|Visible))|Display(?:FadeOperationInProgress|I(?:OServicePort|sCaptured)))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.tba.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AUGraph(?:Add(?:Node|RenderNotify)|C(?:l(?:earConnections|ose)|o(?:nnectNodeInput|untNodeInteractions))|DisconnectNodeInput|Get(?:CPULoad|In(?:dNode|teractionInfo)|MaxCPULoad|N(?:ode(?:Count|In(?:foSubGraph|teractions))|umberOfInteractions))|I(?:nitialize|s(?:Initialized|NodeSubGraph|Open|Running))|N(?:ewNodeSubGraph|odeInfo)|Open|Remove(?:Node|RenderNotify)|S(?:etNodeInputCallback|t(?:art|op))|U(?:ninitialize|pdate))|DisposeAUGraph|NewAUGraph|QL(?:PreviewRequest(?:C(?:opy(?:ContentUTI|Options|URL)|reate(?:Context|PDFContext))|FlushContext|Get(?:DocumentObject|GeneratorBundle|TypeID)|IsCancelled|Set(?:D(?:ataRepresentation|ocumentObject)|URLRepresentation))|Thumbnail(?:C(?:ancel|opy(?:DocumentURL|Image|Options)|reate)|DispatchAsync|Get(?:ContentRect|MaximumSize|TypeID)|IsCancelled|Request(?:C(?:opy(?:ContentUTI|Options|URL)|reateContext)|FlushContext|Get(?:DocumentObject|GeneratorBundle|MaximumSize|TypeID)|IsCancelled|Set(?:DocumentObject|Image(?:AtURL|WithData)?|ThumbnailWith(?:DataRepresentation|URLRepresentation))))))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.10.c" + } + }, + "match": "(\\s*)(\\b(?:C(?:GLGetDeviceFromGLRenderer|MS(?:DecoderCopySignerTimestampWithPolicy|EncoderCopySignerTimestampWithPolicy)|TRubyAnnotation(?:Create(?:Copy)?|Get(?:Alignment|Overhang|SizeFactor|T(?:extForPosition|ypeID))))|JSGlobalContext(?:CopyName|SetName)|LSCopy(?:ApplicationURLsForBundleIdentifier|DefaultApplicationURLFor(?:ContentType|URL))|SecAccessControl(?:CreateWithFlags|GetTypeID)|UTType(?:CopyAllTagsWithClass|IsD(?:eclared|ynamic))|launch_activate_socket|os_re(?:lease|tain)|qos_class_(?:main|self)|simd_inverse)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.11.c" + } + }, + "match": "(\\s*)(\\b(?:Audio(?:ComponentInstantiate|ServicesPlay(?:AlertSoundWithCompletion|SystemSoundWithCompletion))|C(?:MSEncoderSetSignerAlgorithm|T(?:LineEnumerateCaretOffsets|RunGetBaseAdvancesAndOrigins))|IORegistryEntryCopy(?:FromPath|Path)|JSValueIs(?:Array|Date)|MIDI(?:ClientCreateWithBlock|DestinationCreateWithBlock|InputPortCreateWithBlock)|connectx|disconnectx|xpc_(?:array_get_(?:array|dictionary)|dictionary_get_(?:array|dictionary)))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.12.c" + } + }, + "match": "(\\s*)(\\b(?:CTRubyAnnotationCreateWithAttributes|IOSurface(?:AllowsPixelSizeCasting|SetPurgeable)|JS(?:Object(?:Get(?:ArrayBufferByte(?:Length|sPtr)|TypedArray(?:B(?:uffer|yte(?:Length|Offset|sPtr))|Length))|Make(?:ArrayBufferWithBytesNoCopy|TypedArray(?:With(?:ArrayBuffer(?:AndOffset)?|BytesNoCopy))?))|ValueGetTypedArrayType)|MDItemsCopyAttributes|Sec(?:CertificateCopyNormalized(?:IssuerSequence|SubjectSequence)|Key(?:C(?:opy(?:Attributes|ExternalRepresentation|KeyExchangeResult|PublicKey)|reate(?:DecryptedData|EncryptedData|RandomKey|Signature|WithData))|IsAlgorithmSupported|VerifySignature))|os_(?:log_(?:create|type_enabled)|unfair_lock_(?:assert_(?:not_owner|owner)|lock|trylock|unlock))|xpc_connection_activate)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.13.c" + } + }, + "match": "(\\s*)(\\b(?:AudioUnitExtension(?:CopyComponentList|SetComponentList)|C(?:GImage(?:DestinationAddAuxiliaryDataInfo|SourceCopyAuxiliaryDataInfoAtIndex)|TFontManagerCreateFontDescriptorsFromData|V(?:ColorPrimariesGet(?:IntegerCodePointForString|StringForIntegerCodePoint)|TransferFunctionGet(?:IntegerCodePointForString|StringForIntegerCodePoint)|YCbCrMatrixGet(?:IntegerCodePointForString|StringForIntegerCodePoint)))|IOSurfaceGet(?:Bit(?:DepthOfComponentOfPlane|OffsetOfComponentOfPlane)|N(?:ameOfComponentOfPlane|umberOfComponentsOfPlane)|RangeOfComponentOfPlane|Subsampling|TypeOfComponentOfPlane)|SecCertificateCopySerialNumberData)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.14.c" + } + }, + "match": "(\\s*)(\\b(?:AEDeterminePermissionToAutomateTarget|C(?:GImageSourceGetPrimaryImageIndex|TFramesetterCreateWithTypesetter)|Sec(?:CertificateCopyKey|Trust(?:EvaluateWithError|SetSignedCertificateTimestamps))|sec_(?:certificate_c(?:opy_ref|reate)|identity_c(?:opy_(?:certificates_ref|ref)|reate(?:_with_certificates)?)|protocol_(?:metadata_(?:access_(?:distinguished_names|ocsp_response|peer_certificate_chain|supported_signature_algorithms)|c(?:hallenge_parameters_are_equal|opy_peer_public_key|reate_secret(?:_with_context)?)|get_(?:early_data_accepted|negotiated_(?:protocol|tls_ciphersuite)|server_name)|peers_are_equal)|options_(?:add_(?:pre_shared_key|tls_application_protocol)|set_(?:challenge_block|key_update_block|local_identity|peer_authentication_required|tls_(?:false_start_enabled|is_fallback_attempt|ocsp_enabled|re(?:negotiation_enabled|sumption_enabled)|s(?:ct_enabled|erver_name)|tickets_enabled)|verify_block)))|trust_c(?:opy_ref|reate)))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.15.c" + } + }, + "match": "(\\s*)(\\b(?:C(?:GAnimateImage(?:AtURLWithBlock|DataWithBlock)|T(?:FontManager(?:RegisterFont(?:Descriptors|URLs)|UnregisterFont(?:Descriptors|URLs))|GlyphInfoGetGlyph))|JS(?:Object(?:DeletePropertyForKey|GetPropertyForKey|HasPropertyForKey|MakeDeferredPromise|SetPropertyForKey)|Value(?:IsSymbol|MakeSymbol))|SecTrustEvaluateAsyncWithError|aligned_alloc|sec_(?:identity_access_certificates|protocol_(?:metadata_(?:access_pre_shared_keys|get_negotiated_tls_protocol_version)|options_(?:a(?:ppend_tls_ciphersuite(?:_group)?|re_equal)|get_default_m(?:ax_(?:dtls_protocol_version|tls_protocol_version)|in_(?:dtls_protocol_version|tls_protocol_version))|set_(?:m(?:ax_tls_protocol_version|in_tls_protocol_version)|pre_shared_key_selection_block|tls_pre_shared_key_identity_hint))))|xpc_type_get_name)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.8.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:ECompareDesc|udioQueueProcessingTapGetQueueTime)|C(?:GImage(?:Destination(?:AddImageAndMetadata|CopyImageSource)|Metadata(?:C(?:opy(?:StringValueWithPath|Tag(?:MatchingImageProperty|WithPath|s))|reate(?:FromXMPData|Mutable(?:Copy)?|XMPData))|EnumerateTagsUsingBlock|Re(?:gisterNamespaceForPrefix|moveTagWithPath)|Set(?:TagWithPath|Value(?:MatchingImageProperty|WithPath))|Tag(?:C(?:opy(?:Name(?:space)?|Prefix|Qualifiers|Value)|reate)|GetType(?:ID)?))|SourceCopyMetadataAtIndex)|MS(?:DecoderCopySigner(?:SigningTime|Timestamp(?:Certificates)?)|EncoderCopySignerTimestamp)|T(?:Font(?:CopyDefaultCascadeListForLanguages|GetOpticalBoundsForGlyphs|Manager(?:RegisterGraphicsFont|UnregisterGraphicsFont))|LineGetBoundsWithOptions)|VImageBufferCreateColorSpaceFromAttachments|opyInstrumentInfoFromSoundBank))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.9.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:XIsProcessTrustedWithOptions|udioHardware(?:CreateAggregateDevice|DestroyAggregateDevice))|C(?:GImageSourceRemoveCacheAtIndex|TFont(?:CreateForStringWithLanguage|Descriptor(?:CreateCopyWith(?:Family|SymbolicTraits)|MatchFontDescriptorsWithProgressHandler)))|FSEventStreamSetExclusionPaths|Sec(?:PolicyCreate(?:Revocation|WithProperties)|Trust(?:Copy(?:Exceptions|Result)|GetNetworkFetchAllowed|Set(?:Exceptions|NetworkFetchAllowed|OCSPResponse)))|xpc_activity_(?:copy_criteria|get_state|register|s(?:et_(?:criteria|state)|hould_defer)|unregister))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:E(?:Build(?:AppleEvent|Desc|Parameters)|C(?:allObjectAccessor|heckIsRecord|o(?:erce(?:Desc|Ptr)|untItems)|reate(?:AppleEvent|Desc(?:FromExternalPtr)?|List|RemoteProcessResolver))|D(?:e(?:codeMessage|lete(?:Item|Param))|ispose(?:Desc|RemoteProcessResolver|Token)|uplicateDesc)|FlattenDesc|Get(?:A(?:rray|ttribute(?:Desc|Ptr))|CoercionHandler|DescData(?:Range|Size)?|EventHandler|InteractionAllowed|Nth(?:Desc|Ptr)|ObjectAccessor|Param(?:Desc|Ptr)|RegisteredMachPort|SpecialHandler|TheCurrentEvent)|In(?:itializeDesc|stall(?:CoercionHandler|EventHandler|ObjectAccessor|SpecialHandler)|teractWithUser)|ManagerInfo|ObjectInit|P(?:r(?:intDescToHandle|ocess(?:AppleEvent|Event|Message))|ut(?:A(?:rray|ttribute(?:Desc|Ptr))|Desc|P(?:aram(?:Desc|Ptr)|tr)))|Re(?:mo(?:teProcessResolver(?:GetProcesses|ScheduleWithRunLoop)|ve(?:CoercionHandler|EventHandler|ObjectAccessor|SpecialHandler))|placeDescData|s(?:etTimer|olve|umeTheCurrentEvent))|S(?:e(?:nd(?:Message)?|t(?:InteractionAllowed|ObjectCallbacks|TheCurrentEvent))|izeOf(?:Attribute|FlattenedDesc|NthItem|Param)|tream(?:C(?:lose(?:Desc|List|Record)?|reateEvent)|Op(?:en(?:Desc|Event|KeyDesc|List|Record)?|tionalParam)|SetRecordType|Write(?:AEDesc|D(?:ata|esc)|Key(?:Desc)?))|uspendTheCurrentEvent)|UnflattenDesc)|H(?:GotoPage|LookupAnchor|RegisterHelpBookWithURL|Search)|S(?:CopySourceAttributes|GetSourceStyleNames|Init|SetSourceAttributes)|U(?:EventListener(?:AddEventType|Create(?:WithDispatchQueue)?|Notify|RemoveEventType)|Listener(?:AddParameter|Create(?:WithDispatchQueue)?|Dispose|RemoveParameter)|Parameter(?:FormatValue|ListenerNotify|Set|Value(?:FromLinear|ToLinear)))|X(?:IsProcessTrusted|Observer(?:AddNotification|Create(?:WithInfoCallback)?|Get(?:RunLoopSource|TypeID)|RemoveNotification)|UIElement(?:C(?:opy(?:A(?:ction(?:Description|Names)|ttribute(?:Names|Value(?:s)?))|ElementAtPosition|MultipleAttributeValues|ParameterizedAttribute(?:Names|Value))|reate(?:Application|SystemWide))|Get(?:AttributeValueCount|Pid|TypeID)|IsAttributeSettable|PerformAction|Set(?:AttributeValue|MessagingTimeout))|Value(?:Create|Get(?:Type(?:ID)?|Value)))|cquireFirstMatchingEventInQueue|ddEventTypesToHandler|u(?:dio(?:C(?:hannelLayoutTag_GetNumberOfChannels|o(?:dec(?:AppendInput(?:BufferList|Data)|GetProperty(?:Info)?|Initialize|ProduceOutput(?:BufferList|Packets)|Reset|SetProperty|Uninitialize)|mponent(?:Co(?:py(?:ConfigurationInfo|Name)|unt)|FindNext|Get(?:Description|Version)|Instance(?:CanDo|Dispose|GetComponent|New)|Register|Validate)|nverter(?:Convert(?:Buffer|ComplexBuffer)|Dispose|FillComplexBuffer|GetProperty(?:Info)?|New(?:Specific)?|Reset|SetProperty)))|Device(?:CreateIOProcID(?:WithBlock)?|DestroyIOProcID|Get(?:CurrentTime|NearestStartTime)|St(?:art(?:AtTime)?|op)|TranslateTime)|F(?:ile(?:C(?:lose|o(?:mponent(?:C(?:loseFile|ountUserData|reateURL)|ExtensionIsThisFormat|FileDataIsThisFormat|Get(?:GlobalInfo(?:Size)?|Property(?:Info)?|UserData(?:Size)?)|InitializeWithCallbacks|Op(?:en(?:URL|WithCallbacks)|timize)|Re(?:ad(?:Bytes|Packet(?:Data|s))|moveUserData)|Set(?:Property|UserData)|Write(?:Bytes|Packets))|untUserData)|reateWithURL)|Get(?:GlobalInfo(?:Size)?|Property(?:Info)?|UserData(?:Size)?)|InitializeWithCallbacks|Op(?:en(?:URL|WithCallbacks)|timize)|Re(?:ad(?:Bytes|PacketData)|moveUserData)|S(?:et(?:Property|UserData)|tream(?:Close|GetProperty(?:Info)?|Open|ParseBytes|Se(?:ek|tProperty)))|Write(?:Bytes|Packets))|ormatGetProperty(?:Info)?)|HardwareUnload|O(?:bject(?:AddPropertyListener(?:Block)?|GetPropertyData(?:Size)?|HasProperty|IsPropertySettable|RemovePropertyListener(?:Block)?|S(?:etPropertyData|how))|utputUnitSt(?:art|op))|Queue(?:A(?:ddPropertyListener|llocateBuffer(?:WithPacketDescriptions)?)|CreateTimeline|D(?:evice(?:Get(?:CurrentTime|NearestStartTime)|TranslateTime)|ispose(?:Timeline)?)|EnqueueBuffer(?:WithParameters)?|F(?:lush|reeBuffer)|Get(?:CurrentTime|P(?:arameter|roperty(?:Size)?))|New(?:Input(?:WithDispatchQueue)?|Output(?:WithDispatchQueue)?)|OfflineRender|P(?:ause|r(?:ime|ocessingTap(?:Dispose|GetSourceAudio|New)))|Re(?:movePropertyListener|set)|S(?:et(?:OfflineRenderFormat|P(?:arameter|roperty))|t(?:art|op)))|Services(?:AddSystemSoundCompletion|CreateSystemSoundID|DisposeSystemSoundID|GetProperty(?:Info)?|Play(?:AlertSound|SystemSound)|RemoveSystemSoundCompletion|SetProperty)|Unit(?:Add(?:PropertyListener|RenderNotify)|GetP(?:arameter|roperty(?:Info)?)|Initialize|Process(?:Multiple)?|Re(?:move(?:PropertyListenerWithUserData|RenderNotify)|nder|set)|S(?:cheduleParameters|etP(?:arameter|roperty))|Uninitialize))|thorization(?:C(?:opy(?:Info|Rights(?:Async)?)|reate(?:FromExternalForm)?)|Free(?:ItemSet)?|MakeExternalForm|Right(?:Get|Remove|Set))))|C(?:A(?:C(?:lock(?:A(?:ddListener|rm)|B(?:arBeatTimeToBeats|eatsToBarBeatTime)|Dis(?:arm|pose)|Get(?:CurrentT(?:empo|ime)|P(?:layRate|roperty(?:Info)?)|StartTime)|New|ParseMIDI|RemoveListener|S(?:MPTETimeToSeconds|e(?:condsToSMPTETime|t(?:CurrentT(?:empo|ime)|P(?:layRate|roperty)))|t(?:art|op))|TranslateTime)|urrentMediaTime)|Show(?:File)?|Transform3D(?:Concat|EqualToTransform|GetAffineTransform|I(?:nvert|s(?:Affine|Identity))|Make(?:AffineTransform|Rotation|Scale|Translation)|Rotate|Scale|Translate))|C_SHA(?:1(?:_(?:Final|Init|Update))?|2(?:24(?:_(?:Final|Init|Update))?|56(?:_(?:Final|Init|Update))?)|384(?:_(?:Final|Init|Update))?|512(?:_(?:Final|Init|Update))?)|F(?:H(?:TTP(?:Authentication(?:AppliesToRequest|C(?:opy(?:Domains|Method|Realm)|reateFromResponse)|GetTypeID|IsValid|Requires(?:AccountDomain|OrderedRequests|UserNameAndPassword))|Message(?:A(?:ddAuthentication|pp(?:endBytes|lyCredential(?:Dictionary|s)))|C(?:opy(?:AllHeaderFields|Body|HeaderFieldValue|Re(?:quest(?:Method|URL)|sponseStatusLine)|SerializedMessage|Version)|reate(?:Copy|Empty|Re(?:quest|sponse)))|Get(?:ResponseStatusCode|TypeID)|Is(?:HeaderComplete|Request)|Set(?:Body|HeaderFieldValue)))|ost(?:C(?:ancelInfoResolution|reate(?:Copy|With(?:Address|Name)))|Get(?:Addressing|Names|Reachability|TypeID)|S(?:cheduleWithRunLoop|etClient|tartInfoResolution)|UnscheduleFromRunLoop))|Net(?:Service(?:Browser(?:Create|GetTypeID|Invalidate|S(?:cheduleWithRunLoop|earchFor(?:Domains|Services)|topSearch)|UnscheduleFromRunLoop)|C(?:ancel|reate(?:Copy|DictionaryWithTXTData|TXTDataWithDictionary)?)|Get(?:Addressing|Domain|Name|PortNumber|T(?:XTData|argetHost|ype(?:ID)?))|Monitor(?:Create|GetTypeID|Invalidate|S(?:cheduleWithRunLoop|t(?:art|op))|UnscheduleFromRunLoop)|Re(?:gisterWithOptions|solveWithTimeout)|S(?:cheduleWithRunLoop|et(?:Client|TXTData))|UnscheduleFromRunLoop)|work(?:Copy(?:ProxiesFor(?:AutoConfigurationScript|URL)|SystemProxySettings)|ExecuteProxyAutoConfiguration(?:Script|URL)))|S(?:ocketStreamSOCKSGetError(?:Subdomain)?|treamCreatePairWithSocketTo(?:CFHost|NetService)))|G(?:Display(?:CreateUUIDFromDisplayID|GetDisplayIDFromUUID)|Image(?:Destination(?:AddImage(?:FromSource)?|C(?:opyTypeIdentifiers|reateWith(?:Data(?:Consumer)?|URL))|Finalize|GetTypeID|SetProperties)|MetadataGetTypeID|Source(?:C(?:opy(?:Properties(?:AtIndex)?|TypeIdentifiers)|reate(?:I(?:mageAtIndex|ncremental)|ThumbnailAtIndex|With(?:Data(?:Provider)?|URL)))|Get(?:Count|Status(?:AtIndex)?|Type(?:ID)?)|UpdateData(?:Provider)?))|L(?:C(?:hoosePixelFormat|learDrawable|opyContext|reate(?:Context|PBuffer))|D(?:es(?:cribe(?:P(?:Buffer|ixelFormat)|Renderer)|troy(?:Context|P(?:Buffer|ixelFormat)|RendererInfo))|isable)|E(?:nable|rrorString)|FlushDrawable|Get(?:C(?:ontextRetainCount|urrentContext)|GlobalOption|O(?:ffScreen|ption)|P(?:Buffer(?:RetainCount)?|arameter|ixelFormat(?:RetainCount)?)|ShareGroup|V(?:ersion|irtualScreen))|IsEnabled|LockContext|QueryRendererInfo|Re(?:lease(?:Context|P(?:Buffer|ixelFormat))|tain(?:Context|P(?:Buffer|ixelFormat)))|Set(?:CurrentContext|FullScreen(?:OnDisplay)?|GlobalOption|O(?:ffScreen|ption)|P(?:Buffer|arameter)|VirtualScreen)|TexImage(?:IOSurface2D|PBuffer)|U(?:nlockContext|pdateContext)))|M(?:CalibrateDisplay|Plugin(?:ExamineContext|HandleSelection|PostMenuCleanup)|S(?:Decoder(?:C(?:opy(?:AllCerts|Content|DetachedContent|EncapsulatedContentType|Signer(?:Cert|EmailAddress|Status))|reate)|FinalizeMessage|Get(?:NumSigners|TypeID)|IsContentEncrypted|SetDetachedContent|UpdateMessage)|Encode(?:Content|r(?:Add(?:Recipients|S(?:igne(?:dAttributes|rs)|upportingCerts))|C(?:opy(?:Enc(?:apsulatedContentType|odedContent)|Recipients|S(?:igners|upportingCerts))|reate)|Get(?:CertificateChainMode|HasDetachedContent|TypeID)|Set(?:CertificateChainMode|EncapsulatedContentTypeOID|HasDetachedContent)|UpdateContent))))|S(?:Backup(?:IsItemExcluded|SetItemExcluded)|DiskSpace(?:CancelRecovery|GetRecoveryEstimate|StartRecovery)|Get(?:DefaultIdentityAuthority|LocalIdentityAuthority|ManagedIdentityAuthority)|Identity(?:A(?:dd(?:Alias|Member)|uth(?:enticateUsingPassword|ority(?:CopyLocalizedName|GetTypeID)))|C(?:ommit(?:Asynchronously)?|reate(?:Copy|GroupMembershipQuery|PersistentReference)?)|Delete|Get(?:A(?:liases|uthority)|C(?:ertificate|lass)|EmailAddress|FullName|Image(?:Data(?:Type)?|URL)|Posix(?:ID|Name)|TypeID|UUID)|Is(?:Committing|Enabled|Hidden|MemberOfGroup)|Query(?:C(?:opyResults|reate(?:For(?:CurrentUser|Name|P(?:ersistentReference|osixID)|UUID))?)|Execute(?:Asynchronously)?|GetTypeID|Stop)|Remove(?:Alias|Client|Member)|Set(?:Certificate|EmailAddress|FullName|I(?:mage(?:Data|URL)|sEnabled)|Password)))|T(?:F(?:ont(?:C(?:o(?:llection(?:C(?:opy(?:ExclusionDescriptors|FontAttribute(?:s)?|QueryDescriptors)|reate(?:CopyWithFontDescriptors|FromAvailableFonts|M(?:atchingFontDescriptors(?:ForFamily|SortedWithCallback|WithOptions)?|utableCopy)|WithFontDescriptors))|GetTypeID|Set(?:ExclusionDescriptors|QueryDescriptors))|py(?:A(?:ttribute|vailableTables)|CharacterSet|DisplayName|F(?:amilyName|eature(?:Settings|s)|ontDescriptor|ullName)|GraphicsFont|LocalizedName|Name|PostScriptName|SupportedLanguages|T(?:able|raits)|Variation(?:Axes)?))|reate(?:CopyWith(?:Attributes|Family|SymbolicTraits)|ForString|PathForGlyph|UIFontForLanguage|With(?:FontDescriptor(?:AndOptions)?|GraphicsFont|Name(?:AndOptions)?|PlatformFont)))|D(?:escriptor(?:C(?:opy(?:Attribute(?:s)?|LocalizedAttribute)|reate(?:CopyWith(?:Attributes|Feature|Variation)|MatchingFontDescriptor(?:s)?|With(?:Attributes|NameAndSize)))|GetTypeID)|rawGlyphs)|Get(?:A(?:dvancesForGlyphs|scent)|Bounding(?:Box|RectsForGlyphs)|CapHeight|Descent|Glyph(?:Count|WithName|sForCharacters)|L(?:eading|igatureCaretPositions)|Matrix|PlatformFont|S(?:ize|lantAngle|tringEncoding|ymbolicTraits)|TypeID|Un(?:derline(?:Position|Thickness)|itsPerEm)|VerticalTranslationsForGlyphs|XHeight)|Manager(?:C(?:o(?:mpareFontFamilyNames|py(?:Available(?:Font(?:FamilyNames|URLs)|PostScriptNames)|RegisteredFontDescriptors))|reateFont(?:Descriptor(?:FromData|sFromURL)|RequestRunLoopSource))|EnableFontDescriptors|Get(?:AutoActivationSetting|ScopeForURL)|IsSupportedFont|Re(?:gisterFonts(?:ForURL|WithAssetNames)|questFonts)|SetAutoActivationSetting|UnregisterFontsForURL))|rame(?:Draw|Get(?:FrameAttributes|Line(?:Origins|s)|Path|StringRange|TypeID|VisibleStringRange)|setter(?:Create(?:Frame|WithAttributedString)|GetType(?:ID|setter)|SuggestFrameSizeWithConstraints)))|G(?:etCoreTextVersion|lyphInfo(?:CreateWith(?:CharacterIdentifier|Glyph(?:Name)?)|Get(?:Character(?:Collection|Identifier)|GlyphName|TypeID)))|Line(?:Create(?:JustifiedLine|TruncatedLine|WithAttributedString)|Draw|Get(?:Glyph(?:Count|Runs)|ImageBounds|OffsetForStringIndex|PenOffsetForFlush|String(?:IndexForPosition|Range)|T(?:railingWhitespaceWidth|yp(?:eID|ographicBounds))))|ParagraphStyle(?:Create(?:Copy)?|Get(?:TypeID|ValueForSpecifier))|Run(?:D(?:elegate(?:Create|Get(?:RefCon|TypeID))|raw)|Get(?:A(?:dvances(?:Ptr)?|ttributes)|Glyph(?:Count|s(?:Ptr)?)|ImageBounds|Positions(?:Ptr)?|St(?:atus|ring(?:Indices(?:Ptr)?|Range))|T(?:extMatrix|yp(?:eID|ographicBounds))))|T(?:extTab(?:Create|Get(?:Alignment|Location|Options|TypeID))|ypesetter(?:Create(?:Line(?:WithOffset)?|WithAttributedString(?:AndOptions)?)|GetTypeID|Suggest(?:ClusterBreak(?:WithOffset)?|LineBreak(?:WithOffset)?))))|V(?:Buffer(?:GetAttachment(?:s)?|PropagateAttachments|Re(?:lease|moveA(?:llAttachments|ttachment)|tain)|SetAttachment(?:s)?)|DisplayLink(?:CreateWith(?:ActiveCGDisplays|CGDisplay(?:s)?|OpenGLDisplayMask)|Get(?:ActualOutputVideoRefreshPeriod|Current(?:CGDisplay|Time)|NominalOutputVideoRefreshPeriod|OutputVideoLatency|TypeID)|IsRunning|Re(?:lease|tain)|S(?:et(?:CurrentCGDisplay(?:FromOpenGLContext)?|Output(?:Callback|Handler))|t(?:art|op))|TranslateTime)|Get(?:CurrentHostTime|HostClock(?:Frequency|MinimumTimeDelta))|ImageBuffer(?:Get(?:C(?:leanRect|olorSpace)|DisplaySize|EncodedSize)|IsFlipped)|Pixel(?:Buffer(?:Create(?:ResolvedAttributesDictionary|With(?:Bytes|IOSurface|PlanarBytes))?|FillExtendedPixels|Get(?:B(?:aseAddress(?:OfPlane)?|ytesPerRow(?:OfPlane)?)|DataSize|ExtendedPixels|Height(?:OfPlane)?|IOSurface|P(?:ixelFormatType|laneCount)|TypeID|Width(?:OfPlane)?)|IsPlanar|LockBaseAddress|Pool(?:Create(?:PixelBuffer(?:WithAuxAttributes)?)?|Flush|Get(?:Attributes|PixelBufferAttributes|TypeID)|Re(?:lease|tain))|Re(?:lease|tain)|UnlockBaseAddress)|FormatDescription(?:ArrayCreateWithAllPixelFormatTypes|CreateWithPixelFormatType|RegisterDescriptionWithPixelFormatType)))|allNextEventHandler|h(?:ange(?:TextToUnicodeInfo|UnicodeToTextInfo)|eckEventQueueForUserCancel)|o(?:lorSync(?:C(?:MM(?:C(?:opy(?:CMMIdentifier|LocalizedName)|reate)|Get(?:Bundle|TypeID))|reateCodeFragment)|Device(?:CopyDeviceInfo|SetCustomProfiles)|Iterate(?:DeviceProfiles|Installed(?:CMMs|Profiles))|Profile(?:C(?:o(?:ntainsTag|py(?:D(?:ata|escriptionString)|Header|Tag(?:Signatures)?))|reate(?:D(?:eviceProfile|isplayTransferTablesFromVCGT)|Link|Mutable(?:Copy)?|With(?:DisplayID|Name|URL))?)|EstimateGamma(?:WithDisplayID)?|Get(?:DisplayTransferFormulaFromVCGT|MD5|TypeID|URL)|Install|RemoveTag|Set(?:Header|Tag)|Uninstall|Verify)|RegisterDevice|Transform(?:C(?:o(?:nvert|pyProperty)|reate)|GetTypeID|SetProperty)|UnregisterDevice)|n(?:tinueSpeech|vertFrom(?:PStringToUnicode|TextToUnicode|UnicodeTo(?:PString|ScriptCodeRun|Text(?:Run)?)))|py(?:Event(?:As|CGEvent)?|NameFromSoundBank|PhonemesFromText|S(?:peechProperty|ymbolicHotKeys)|ThemeIdentifier)|unt(?:UnicodeMappings|Voices))|reate(?:CompDescriptor|Event(?:WithCGEvent)?|LogicalDescriptor|O(?:bjSpecifier|ffsetDescriptor)|RangeDescriptor|Text(?:Encoding|ToUnicodeInfo(?:ByEncoding)?)|UnicodeToText(?:Info(?:ByEncoding)?|RunInfo(?:By(?:Encoding|ScriptCode))?)))|D(?:A(?:A(?:ddCallbackToSession|pprovalSession(?:Create|GetTypeID|ScheduleWithRunLoop|UnscheduleFromRunLoop))|CallbackCreate|Disk(?:C(?:opy(?:Description|IOMedia|WholeDisk)|reateFrom(?:BSDName|IOMedia|VolumePath))|Get(?:BSDName|TypeID))|GetCallbackFromSession|RemoveCallbackFromSession(?:WithKey)?|Session(?:Create|GetTypeID|S(?:cheduleWithRunLoop|etDispatchQueue)|UnscheduleFromRunLoop))|CS(?:CopyTextDefinition|GetTermRangeInString)|R(?:AudioTrackCreateWithURL|Burn(?:Abort|C(?:opyStatus|reate)|Get(?:Device|Properties|TypeID)|SetProperties|WriteLayout)|C(?:DTextBlock(?:Create(?:ArrayFromPackList)?|Flatten|Get(?:Properties|T(?:rackDictionaries|ypeID)|Value)|Set(?:Properties|TrackDictionaries|Value))|opy(?:DeviceArray|LocalizedStringFor(?:AdditionalSense|DiscRecordingError|SenseCode|Value)))|Device(?:Acquire(?:ExclusiveAccess|MediaReservation)|C(?:loseTray|opy(?:DeviceFor(?:BSDName|IORegistryEntryPath)|Info|Status))|EjectMedia|GetTypeID|IsValid|KPSForXFactor|OpenTray|Release(?:ExclusiveAccess|MediaReservation)|XFactorForKPS)|Erase(?:C(?:opyStatus|reate)|Get(?:Device|Properties|TypeID)|S(?:etProperties|tart))|F(?:SObject(?:Copy(?:BaseName|FilesystemPropert(?:ies|y)|MangledName(?:s)?|RealURL|SpecificName(?:s)?)|Get(?:FilesystemMask|Parent)|IsVirtual|Set(?:BaseName|Filesystem(?:Mask|Propert(?:ies|y))|SpecificName(?:s)?))|ile(?:Create(?:RealWithURL|Virtual(?:Link|With(?:Callback|Data)))|GetTypeID|systemTrack(?:Create|EstimateOverhead))|older(?:AddChild|C(?:o(?:nvertRealToVirtual|pyChildren|untChildren)|reate(?:RealWithURL|Virtual))|GetTypeID|RemoveChild))|Get(?:RefCon|Version)|NotificationCenter(?:AddObserver|Create(?:RunLoopSource)?|GetTypeID|RemoveObserver)|SetRefCon|Track(?:Create|EstimateLength|Get(?:Properties|TypeID)|S(?:etProperties|peedTest)))|ebugPrint(?:Event|MainEventQueue)|is(?:ableSecureEventInput|pose(?:AE(?:Coerce(?:DescUPP|PtrUPP)|DisposeExternalUPP|EventHandlerUPP|FilterUPP|IdleUPP)|C(?:a(?:librate(?:EventUPP|UPP)|nCalibrateUPP)|ontrol(?:ActionUPP|EditTextValidationUPP|KeyFilterUPP|UserPane(?:ActivateUPP|DrawUPP|FocusUPP|HitTestUPP|IdleUPP|KeyDownUPP|TrackingUPP)))|D(?:ataBrowser(?:A(?:cceptDragUPP|ddDragItemUPP)|DrawItemUPP|EditItemUPP|GetContextualMenuUPP|HitTestUPP|Item(?:AcceptDragUPP|CompareUPP|D(?:ataUPP|ragRgnUPP)|HelpContentUPP|Notification(?:UPP|WithItemUPP)|ReceiveDragUPP|UPP)|PostProcessDragUPP|ReceiveDragUPP|SelectContextualMenuUPP|TrackingUPP)|ragInputUPP)|E(?:ditUnicodePostUpdateUPP|vent(?:ComparatorUPP|HandlerUPP|Loop(?:IdleTimerUPP|TimerUPP)))|HM(?:ControlContentUPP|Menu(?:ItemContentUPP|TitleContentUPP)|WindowContentUPP)|I(?:con(?:ActionUPP|GetterUPP)|ndexToUCStringUPP)|M(?:odalFilter(?:UPP|YDUPP)|usic(?:EventIterator|Player|Sequence))|N(?:ColorChangedUPP|MUPP)|OS(?:A(?:ActiveUPP|CreateAppleEventUPP|SendUPP)|L(?:A(?:ccessorUPP|djustMarksUPP)|Co(?:mpareUPP|untUPP)|DisposeTokenUPP|Get(?:ErrDescUPP|MarkTokenUPP)|MarkUPP))|SpeechChannel|T(?:XN(?:ActionNameMapperUPP|ContextualMenuSetupUPP|FindUPP|ScrollInfoUPP)|extToUnicodeInfo)|U(?:nicodeToText(?:FallbackUPP|Info|RunInfo)|serItemUPP))))|E(?:nableSecureEventInput|xtAudioFile(?:CreateWithURL|Dispose|GetProperty(?:Info)?|OpenURL|Read|Se(?:ek|tProperty)|Tell|Wr(?:apAudioFileID|ite(?:Async)?)))|F(?:C(?:Add(?:Collection|FontDescriptorToCollection)|Copy(?:CollectionNames|FontDescriptorsInCollection)|FontDescriptorCreateWith(?:FontAttributes|Name)|Remove(?:Collection|FontDescriptorFromCollection))|P(?:IsFontPanelVisible|ShowHideFontPanel)|SEvent(?:Stream(?:C(?:opy(?:Description|PathsBeingWatched)|reate(?:RelativeToDevice)?)|Flush(?:Async|Sync)|Get(?:DeviceBeingWatched|LatestEventId)|Invalidate|Re(?:lease|tain)|S(?:cheduleWithRunLoop|etDispatchQueue|how|t(?:art|op))|UnscheduleFromRunLoop)|s(?:CopyUUIDForDevice|Get(?:CurrentEventId|LastEventIdForDeviceBeforeTime)|PurgeEventsForDeviceUpToEventId))|indSpecificEventInQueue|lush(?:Event(?:Queue|sMatchingListFromQueue)|SpecificEventsFromQueue))|Get(?:A(?:pplication(?:EventTarget|TextEncoding)|udioUnitParameterDisplayType)|C(?:FRunLoopFromEventLoop|olor|urrent(?:ButtonState|Event(?:ButtonState|KeyModifiers|Loop|Queue|Time)?|KeyModifiers))|Event(?:Class|DispatcherTarget|Kind|MonitorTarget|Parameter|RetainCount|Time)|I(?:con(?:FamilyData|RefVariant)|ndVoice)|Keys|M(?:ainEvent(?:Loop|Queue)|enuTrackingData)|NumEventsInQueue|S(?:criptInfoFromTextEncoding|peech(?:Pitch|Rate)|y(?:mbolicHotKeyMode|stemUIMode))|T(?:extEncoding(?:Base|F(?:ormat|romScriptInfo)|Name|Variant)|hemeMe(?:nu(?:ItemExtra|SeparatorHeight|TitleExtra)|tric))|Voice(?:Description|Info))|HI(?:DictionaryWindowShow|GetMousePosition|MouseTrackingGetParameters|Object(?:AddDelegate|C(?:opy(?:ClassID|Delegates)|reate(?:FromBundle)?)|DynamicCast|FromEventTarget|GetEvent(?:HandlerObject|Target)|Is(?:ArchivingIgnored|OfClass)|PrintDebugInfo|Re(?:gisterSubclass|moveDelegate)|UnregisterClass)|PointConvert|RectConvert|S(?:earchWindowShow|hape(?:C(?:ontainsPoint|reate(?:Copy|Difference|Empty|Intersection|Mutable(?:Copy|WithRect)?|Union|With(?:QDRgn|Rect)|Xor))|Difference|Enumerate|Get(?:AsQDRgn|Bounds|TypeID)|I(?:n(?:set|tersect(?:sRect)?)|s(?:Empty|Rectangular))|Offset|ReplacePathInCGContext|Set(?:Empty|WithShape)|Union(?:WithRect)?|Xor)|izeConvert)|Theme(?:ApplyBackground|B(?:eginFocus|rushCreateCGColor)|Draw(?:B(?:ackground|utton)|ChasingArrows|F(?:ocusRect|rame)|G(?:enericWell|r(?:abber|o(?:upBox|wBox)))|Header|Menu(?:Ba(?:ckground|rBackground)|Item|Separator|Title)|P(?:aneSplitter|lacard|opupArrow)|S(?:crollBarDelimiters|e(?:gment|parator))|T(?:ab(?:Pane)?|extBox|i(?:ckMark|tleBarWidget)|rack(?:TickMarks)?)|WindowFrame)|EndFocus|Get(?:Button(?:BackgroundBounds|ContentBounds|Shape)|GrowBoxBounds|MenuBackgroundShape|ScrollBarTrackRect|T(?:ab(?:DrawShape|Pane(?:ContentShape|DrawShape)|Shape)|ext(?:ColorForThemeBrush|Dimensions)|rack(?:Bounds|DragRect|LiveValue|Part(?:Bounds|s)|Thumb(?:PositionFrom(?:Bounds|Offset)|Shape)))|UIFontType|Window(?:RegionHit|Shape))|HitTest(?:ScrollBarArrows|Track)|Set(?:Fill|Stroke|TextFill)))|I(?:O(?:BSDNameMatching|C(?:atalogue(?:GetData|ModuleLoaded|Reset|SendData|Terminate)|onnect(?:Add(?:Client|Ref)|Call(?:Async(?:Method|S(?:calarMethod|tructMethod))|Method|S(?:calarMethod|tructMethod))|GetService|MapMemory(?:64)?|Release|Set(?:CFPropert(?:ies|y)|NotificationPort)|Trap(?:0|1|2|3|4|5|6)|UnmapMemory(?:64)?)|reateReceivePort)|DispatchCalloutFromMessage|Iterator(?:IsValid|Next|Reset)|Kit(?:GetBusyState|WaitQuiet)|MasterPort|NotificationPort(?:Create|Destroy|Get(?:MachPort|RunLoopSource)|Set(?:DispatchQueue|ImportanceReceiver))|O(?:bject(?:Co(?:nformsTo|py(?:BundleIdentifierForClass|Class|SuperclassForClass))|Get(?:Class|KernelRetainCount|RetainCount|UserRetainCount)|IsEqualTo|Re(?:lease|tain))|penFirmwarePathMatching)|Registry(?:CreateIterator|Entry(?:Create(?:CFPropert(?:ies|y)|Iterator)|FromPath|Get(?:Child(?:Entry|Iterator)|LocationInPlane|Name(?:InPlane)?|P(?:a(?:rent(?:Entry|Iterator)|th)|roperty)|RegistryEntryID)|I(?:DMatching|nPlane)|Se(?:archCFProperty|tCFPropert(?:ies|y)))|GetRootEntry|IteratorE(?:nterEntry|xitEntry))|S(?:ervice(?:A(?:dd(?:InterestNotification|MatchingNotification|Notification)|uthorize)|Close|Get(?:BusyState|MatchingService(?:s)?)|Match(?:PropertyTable|ing)|NameMatching|O(?:FPathToBSDName|pen(?:AsFileDescriptor)?)|RequestProbe|WaitQuiet)|urface(?:AlignProperty|C(?:opy(?:AllValues|Value)|reate(?:MachPort|XPCObject)?)|DecrementUseCount|Get(?:AllocSize|B(?:aseAddress(?:OfPlane)?|ytesPer(?:Element(?:OfPlane)?|Row(?:OfPlane)?))|Element(?:Height(?:OfPlane)?|Width(?:OfPlane)?)|Height(?:OfPlane)?|ID|P(?:ixelFormat|laneCount|roperty(?:Alignment|Maximum))|Seed|TypeID|UseCount|Width(?:OfPlane)?)|I(?:ncrementUseCount|sInUse)|Lo(?:ck|okup(?:From(?:MachPort|XPCObject))?)|Remove(?:AllValues|Value)|SetValue(?:s)?|Unlock)))|conRef(?:ContainsCGPoint|IntersectsCGRect|To(?:HIShape|IconFamily))|n(?:stallEvent(?:Handler|LoopTimer)|voke(?:AE(?:Coerce(?:DescUPP|PtrUPP)|DisposeExternalUPP|EventHandlerUPP|FilterUPP|IdleUPP)|C(?:a(?:librate(?:EventUPP|UPP)|nCalibrateUPP)|ontrol(?:ActionUPP|EditTextValidationUPP|KeyFilterUPP|UserPane(?:ActivateUPP|DrawUPP|FocusUPP|HitTestUPP|IdleUPP|KeyDownUPP|TrackingUPP)))|D(?:ataBrowser(?:A(?:cceptDragUPP|ddDragItemUPP)|DrawItemUPP|EditItemUPP|GetContextualMenuUPP|HitTestUPP|Item(?:AcceptDragUPP|CompareUPP|D(?:ataUPP|ragRgnUPP)|HelpContentUPP|Notification(?:UPP|WithItemUPP)|ReceiveDragUPP|UPP)|PostProcessDragUPP|ReceiveDragUPP|SelectContextualMenuUPP|TrackingUPP)|ragInputUPP)|E(?:ditUnicodePostUpdateUPP|vent(?:ComparatorUPP|HandlerUPP|Loop(?:IdleTimerUPP|TimerUPP)))|HM(?:ControlContentUPP|Menu(?:ItemContentUPP|TitleContentUPP)|WindowContentUPP)|I(?:con(?:ActionUPP|GetterUPP)|ndexToUCStringUPP)|ModalFilter(?:UPP|YDUPP)|N(?:ColorChangedUPP|MUPP)|OS(?:A(?:ActiveUPP|CreateAppleEventUPP|SendUPP)|L(?:A(?:ccessorUPP|djustMarksUPP)|Co(?:mpareUPP|untUPP)|DisposeTokenUPP|Get(?:ErrDescUPP|MarkTokenUPP)|MarkUPP))|TXN(?:ActionNameMapperUPP|ContextualMenuSetupUPP|FindUPP|ScrollInfoUPP)|U(?:nicodeToTextFallbackUPP|serItemUPP)))|s(?:EventInQueue|IconRefMaskEmpty|SecureEventInputEnabled|UserCancelEventRef))|JS(?:C(?:heckScriptSyntax|lass(?:Create|Re(?:lease|tain))|ontextG(?:etG(?:lobal(?:Context|Object)|roup)|roup(?:Create|Re(?:lease|tain))))|EvaluateScript|G(?:arbageCollect|lobalContext(?:Create(?:InGroup)?|Re(?:lease|tain)))|Object(?:C(?:allAs(?:Constructor|Function)|opyPropertyNames)|DeleteProperty|GetPr(?:ivate|o(?:perty(?:AtIndex)?|totype))|HasProperty|Is(?:Constructor|Function)|Make(?:Array|Constructor|Date|Error|Function(?:WithCallback)?|RegExp)?|SetPr(?:ivate|o(?:perty(?:AtIndex)?|totype)))|PropertyNameA(?:ccumulatorAddName|rray(?:Get(?:Count|NameAtIndex)|Re(?:lease|tain)))|String(?:C(?:opyCFString|reateWith(?:C(?:FString|haracters)|UTF8CString))|Get(?:CharactersPtr|Length|MaximumUTF8CStringSize|UTF8CString)|IsEqual(?:ToUTF8CString)?|Re(?:lease|tain))|Value(?:CreateJSONString|GetType|Is(?:Boolean|Equal|InstanceOfConstructor|Nu(?:ll|mber)|Object(?:OfClass)?|Stri(?:ctEqual|ng)|Undefined)|Make(?:Boolean|FromJSONString|Nu(?:ll|mber)|String|Undefined)|Protect|To(?:Boolean|Number|Object|StringCopy)|Unprotect))|KBGetLayoutType|L(?:MGetK(?:bd(?:Last|Type)|ey(?:RepThresh|Thresh))|S(?:C(?:anURLAcceptURL|opy(?:A(?:llRoleHandlersForContentType|pplicationURLsForURL)|DefaultRoleHandlerForContentType))|Open(?:CFURLRef|FromURLSpec)|RegisterURL|SetDefault(?:HandlerForURLScheme|RoleHandlerForContentType))|o(?:cale(?:Operation(?:CountNames|Get(?:IndName|Name))|Ref(?:FromL(?:angOrRegionCode|ocaleString)|GetPartString)|StringToLangAndRegionCodes)|ngDoubleTo(?:SInt64|UInt64)))|M(?:D(?:CopyLabel(?:Kinds|WithUUID|s(?:MatchingExpression|WithKind))|Item(?:C(?:opy(?:Attribute(?:List|Names|s)?|Labels)|reate(?:WithURL)?)|GetTypeID|RemoveLabel|SetLabel|sCreateWithURLs)|Label(?:C(?:opyAttribute(?:Name)?|reate)|Delete|GetTypeID|SetAttributes)|Query(?:C(?:opy(?:QueryString|SortingAttributes|Value(?:ListAttributes|sOfAttribute))|reate(?:ForItems|Subset)?)|DisableUpdates|E(?:nableUpdates|xecute)|Get(?:AttributeValueOfResultAtIndex|BatchingParameters|CountOfResultsWithAttributeValue|IndexOfResult|Result(?:AtIndex|Count)|SortOptionFlagsForAttribute|TypeID)|IsGatheringComplete|S(?:et(?:BatchingParameters|Create(?:ResultFunction|ValueFunction)|DispatchQueue|MaxCount|S(?:earchScope|ort(?:Comparator(?:Block)?|O(?:ptionFlagsForAttribute|rder))))|top))|SchemaCopy(?:A(?:llAttributes|ttributesForContentType)|Display(?:DescriptionForAttribute|NameForAttribute)|MetaAttributesForAttribute))|IDI(?:Client(?:Create|Dispose)|De(?:stinationCreate|viceGet(?:Entity|NumberOfEntities))|En(?:dpoint(?:Dispose|GetEntity)|tityGet(?:De(?:stination|vice)|NumberOf(?:Destinations|Sources)|Source))|FlushOutput|Get(?:De(?:stination|vice)|ExternalDevice|NumberOf(?:De(?:stinations|vices)|ExternalDevices|Sources)|Source)|InputPortCreate|O(?:bject(?:FindByUniqueID|Get(?:D(?:ataProperty|ictionaryProperty)|IntegerProperty|Properties|StringProperty)|RemoveProperty|Set(?:D(?:ataProperty|ictionaryProperty)|IntegerProperty|StringProperty))|utputPortCreate)|P(?:acket(?:List(?:Add|Init)|Next)|ort(?:ConnectSource|Dis(?:connectSource|pose)))|Re(?:ceived|start)|S(?:end(?:Sysex)?|ourceCreate))|akeVoiceSpec|usic(?:Device(?:MIDIEvent|S(?:t(?:artNote|opNote)|ysEx))|EventIterator(?:DeleteEvent|GetEventInfo|Has(?:CurrentEvent|NextEvent|PreviousEvent)|NextEvent|PreviousEvent|Se(?:ek|tEvent(?:Info|Time)))|Player(?:Get(?:BeatsForHostTime|HostTimeForBeats|PlayRateScalar|Sequence|Time)|IsPlaying|Preroll|S(?:et(?:PlayRateScalar|Sequence|Time)|t(?:art|op)))|Sequence(?:B(?:arBeatTimeToBeats|eatsToBarBeatTime)|DisposeTrack|File(?:Create(?:Data)?|Load(?:Data)?)|Get(?:AUGraph|BeatsForSeconds|In(?:dTrack|foDictionary)|S(?:MPTEResolution|e(?:condsForBeats|quenceType))|T(?:empoTrack|rack(?:Count|Index)))|NewTrack|Reverse|Set(?:AUGraph|MIDIEndpoint|S(?:MPTEResolution|equenceType)|UserCallback))|Track(?:C(?:lear|opyInsert|ut)|Get(?:Dest(?:MIDIEndpoint|Node)|Property|Sequence)|M(?:erge|oveEvents)|New(?:AUPresetEvent|Extended(?:NoteEvent|TempoEvent)|M(?:IDI(?:ChannelEvent|NoteEvent|RawDataEvent)|etaEvent)|ParameterEvent|UserEvent)|Set(?:Dest(?:MIDIEndpoint|Node)|Property))))|N(?:PickColor|X(?:Convert(?:Host(?:DoubleToSwapped|FloatToSwapped)|Swapped(?:DoubleToHost|FloatToHost))|HostByteOrder|Swap(?:Big(?:DoubleToHost|FloatToHost|IntToHost|Long(?:LongToHost|ToHost)|ShortToHost)|Double|Float|Host(?:DoubleTo(?:Big|Little)|FloatTo(?:Big|Little)|IntTo(?:Big|Little)|Long(?:LongTo(?:Big|Little)|To(?:Big|Little))|ShortTo(?:Big|Little))|Int|L(?:ittle(?:DoubleToHost|FloatToHost|IntToHost|Long(?:LongToHost|ToHost)|ShortToHost)|ong(?:Long)?)|Short))|e(?:arestMacTextEncodings|w(?:AE(?:Coerce(?:DescUPP|PtrUPP)|DisposeExternalUPP|EventHandlerUPP|FilterUPP|IdleUPP)|C(?:a(?:librate(?:EventUPP|UPP)|nCalibrateUPP)|ontrol(?:ActionUPP|EditTextValidationUPP|KeyFilterUPP|UserPane(?:ActivateUPP|DrawUPP|FocusUPP|HitTestUPP|IdleUPP|KeyDownUPP|TrackingUPP)))|D(?:ataBrowser(?:A(?:cceptDragUPP|ddDragItemUPP)|DrawItemUPP|EditItemUPP|GetContextualMenuUPP|HitTestUPP|Item(?:AcceptDragUPP|CompareUPP|D(?:ataUPP|ragRgnUPP)|HelpContentUPP|Notification(?:UPP|WithItemUPP)|ReceiveDragUPP|UPP)|PostProcessDragUPP|ReceiveDragUPP|SelectContextualMenuUPP|TrackingUPP)|ragInputUPP)|E(?:ditUnicodePostUpdateUPP|vent(?:ComparatorUPP|HandlerUPP|Loop(?:IdleTimerUPP|TimerUPP)))|HM(?:ControlContentUPP|Menu(?:ItemContentUPP|TitleContentUPP)|WindowContentUPP)|I(?:con(?:ActionUPP|GetterUPP)|ndexToUCStringUPP)|M(?:odalFilter(?:UPP|YDUPP)|usic(?:EventIterator|Player|Sequence))|N(?:ColorChangedUPP|MUPP)|OS(?:A(?:ActiveUPP|CreateAppleEventUPP|SendUPP)|L(?:A(?:ccessorUPP|djustMarksUPP)|Co(?:mpareUPP|untUPP)|DisposeTokenUPP|Get(?:ErrDescUPP|MarkTokenUPP)|MarkUPP))|SpeechChannel|TXN(?:ActionNameMapperUPP|ContextualMenuSetupUPP|FindUPP|ScrollInfoUPP)|U(?:nicodeToTextFallbackUPP|serItemUPP))|xtAudioFileRegion)|um(?:AudioFileMarkersToNumBytes|BytesToNumAudioFileMarkers))|OS(?:A(?:A(?:ddStorageType|vailableDialect(?:CodeList|s))|Co(?:erce(?:FromDesc|ToDesc)|mpile(?:Execute)?|py(?:DisplayString|ID|S(?:cript(?:ingDefinition(?:FromURL)?)?|ourceString)))|D(?:isp(?:lay|ose)|o(?:Event|Script(?:File)?))|Execute(?:Event)?|Ge(?:nericToRealID|t(?:ActiveProc|C(?:reateProc|urrentDialect)|D(?:efaultScriptingComponent|ialectInfo)|Handler(?:Names)?|Property(?:Names)?|ResumeDispatchProc|S(?:cript(?:DataFromURL|Info|ingComponent(?:FromStored)?)|endProc|ource|torageType|ysTerminology)))|Load(?:Execute(?:File)?|File|ScriptData)?|MakeContext|Re(?:alToGenericID|moveStorageType)|S(?:cript(?:Error|ingComponentName)|et(?:ActiveProc|C(?:reateProc|urrentDialect)|Default(?:ScriptingComponent|Target)|Handler|Property|ResumeDispatchProc|S(?:criptInfo|endProc))|t(?:artRecording|o(?:pRecording|re(?:File)?)))|tomic(?:Dequeue|Enqueue|Fifo(?:Dequeue|Enqueue)))|GetNotificationFromMessage)|P(?:M(?:C(?:GImageCreateWithEPSDataProvider|opy(?:AvailablePPDs|LocalizedPPD|P(?:PDData|ageFormat|rintSettings))|reate(?:GenericPrinter|P(?:ageFormat(?:WithPMPaper)?|rintSettings)|Session))|Get(?:AdjustedPa(?:geRect|perRect)|Co(?:llate|pies)|Duplex|FirstPage|LastPage|Orientation|Page(?:Format(?:ExtendedData|Paper)|Range)|Scale|UnadjustedPa(?:geRect|perRect))|P(?:a(?:geFormat(?:Create(?:DataRepresentation|WithDataRepresentation)|GetPrinterID)|per(?:Create(?:Custom|LocalizedName)|Get(?:Height|ID|Margins|P(?:PDPaperName|rinterID)|Width)|IsCustom))|r(?:eset(?:C(?:opyName|reatePrintSettings)|GetAttributes)|int(?:Settings(?:C(?:opy(?:AsDictionary|Keys)|reate(?:DataRepresentation|WithDataRepresentation))|Get(?:JobName|Value)|Set(?:JobName|Value)|ToOptions(?:WithPrinterAndPageFormat)?)|er(?:C(?:opy(?:De(?:scriptionURL|viceURI)|HostName|Presets|State)|reateFromPrinterID)|Get(?:CommInfo|Driver(?:Creator|ReleaseInfo)|I(?:D|ndexedPrinterResolution)|L(?:anguageInfo|ocation)|M(?:akeAndModelName|imeTypes)|Name|OutputResolution|P(?:aperList|rinterResolutionCount)|State)|Is(?:Default|Favorite|PostScript(?:Capable|Printer)|Remote)|PrintWith(?:File|Provider)|Se(?:ndCommand|t(?:Default|OutputResolution))|WritePostScriptToURL))))|Re(?:lease|tain)|Se(?:rver(?:CreatePrinterList|LaunchPrinterBrowser)|ssion(?:Begin(?:CGDocumentNoDialog|PageNoDialog)|C(?:opy(?:Destination(?:Format|Location)|OutputFormatList)|reateP(?:ageFormatList|rinterList))|DefaultP(?:ageFormat|rintSettings)|E(?:nd(?:DocumentNoDialog|PageNoDialog)|rror)|Get(?:C(?:GGraphicsContext|urrentPrinter)|D(?:ataFromSession|estinationType))|Set(?:CurrentPMPrinter|D(?:ataInSession|estination)|Error)|ValidateP(?:ageFormat|rintSettings))|t(?:Co(?:llate|pies)|Duplex|FirstPage|LastPage|Orientation|Page(?:FormatExtendedData|Range)|Scale))|Workflow(?:CopyItems|SubmitPDFWith(?:Options|Settings)))|a(?:steboard(?:C(?:lear|opy(?:ItemFlavor(?:Data|s)|Name|PasteLocation)|reate)|Get(?:Item(?:Count|FlavorFlags|Identifier)|TypeID)|PutItemFlavor|ResolvePromises|S(?:etP(?:asteLocation|romiseKeeper)|ynchronize))|useSpeechAt)|lotIconRefInContext|o(?:pSymbolicHotKeyMode|stEventToQueue)|rocessHICommand|ushSymbolicHotKeyMode)|Q(?:LThumbnailImageCreate|u(?:eryUnicodeMappings|itEventLoop))|R(?:e(?:ceiveNextEvent|gisterEventHotKey|leaseEvent|moveEvent(?:FromQueue|Handler|LoopTimer|Parameter|TypesFromHandler)|s(?:et(?:TextToUnicodeInfo|UnicodeToText(?:Info|RunInfo))|olveDefaultTextEncoding)|tainEvent|vertTextEncodingToScriptInfo)|unCurrentEventLoop)|S(?:32Set|64(?:A(?:dd|nd)|Bitwise(?:And|Eor|Not|Or)|Div(?:ide)?|Eor|M(?:ax|in|od|ultiply)|N(?:egate|ot)|Or|S(?:et(?:U)?|hift(?:Left|Right)|ubtract))|C(?:Bond(?:Interface(?:C(?:opy(?:A(?:ll|vailableMemberInterfaces)|Status)|reate)|Get(?:MemberInterfaces|Options)|Remove|Set(?:LocalizedDisplayName|MemberInterfaces|Options))|StatusGet(?:InterfaceStatus|MemberInterfaces|TypeID))|CopyLastError|DynamicStore(?:Add(?:TemporaryValue|Value)|C(?:opy(?:Co(?:mputerName|nsoleUser)|KeyList|Loca(?:lHostName|tion)|Multiple|NotifiedKeys|Proxies|Value)|reate(?:RunLoopSource|WithOptions)?)|GetTypeID|KeyCreate(?:Co(?:mputerName|nsoleUser)|HostNames|Location|Network(?:GlobalEntity|Interface(?:Entity)?|ServiceEntity)|Proxies)?|NotifyValue|RemoveValue|Set(?:DispatchQueue|Multiple|NotificationKeys|Value))|Error(?:String)?|Network(?:Connection(?:C(?:opy(?:ExtendedStatus|S(?:erviceID|tatistics)|User(?:Options|Preferences))|reateWithServiceID)|Get(?:Status|TypeID)|S(?:cheduleWithRunLoop|etDispatchQueue|t(?:art|op))|UnscheduleFromRunLoop)|Interface(?:C(?:opy(?:All|M(?:TU|edia(?:Options|SubType(?:Options|s))))|reateWithInterface)|ForceConfigurationRefresh|Get(?:BSDName|Configuration|ExtendedConfiguration|HardwareAddressString|Interface(?:Type)?|LocalizedDisplayName|Supported(?:InterfaceTypes|ProtocolTypes)|TypeID)|Set(?:Configuration|ExtendedConfiguration|M(?:TU|ediaOptions)))|Protocol(?:Get(?:Configuration|Enabled|ProtocolType|TypeID)|Set(?:Configuration|Enabled))|Reachability(?:CreateWith(?:Address(?:Pair)?|Name)|Get(?:Flags|TypeID)|S(?:cheduleWithRunLoop|et(?:Callback|DispatchQueue))|UnscheduleFromRunLoop)|Se(?:rvice(?:AddProtocolType|C(?:opy(?:All|Protocol(?:s)?)?|reate)|EstablishDefaultConfiguration|Get(?:Enabled|Interface|Name|ServiceID|TypeID)|Remove(?:ProtocolType)?|Set(?:Enabled|Name))|t(?:AddService|C(?:o(?:ntainsInterface|py(?:All|Current|Services)?)|reate)|Get(?:Name|Se(?:rviceOrder|tID)|TypeID)|Remove(?:Service)?|Set(?:Current|Name|ServiceOrder))))|Preferences(?:A(?:ddValue|pplyChanges)|C(?:o(?:mmitChanges|pyKeyList)|reate(?:WithAuthorization)?)|Get(?:Signature|TypeID|Value)|Lock|Path(?:CreateUniqueChild|Get(?:Link|Value)|RemoveValue|Set(?:Link|Value))|RemoveValue|S(?:cheduleWithRunLoop|et(?:C(?:allback|omputerName)|DispatchQueue|LocalHostName|Value)|ynchronize)|Un(?:lock|scheduleFromRunLoop))|VLANInterface(?:C(?:opyA(?:ll|vailablePhysicalInterfaces)|reate)|Get(?:Options|PhysicalInterface|Tag)|Remove|Set(?:LocalizedDisplayName|Options|PhysicalInterfaceAndTag)))|Int64To(?:LongDouble|UInt64|Wide)|K(?:Document(?:C(?:opyURL|reate(?:WithURL)?)|Get(?:Name|Parent|SchemeName|TypeID))|Index(?:AddDocument(?:WithText)?|C(?:lose|o(?:mpact|py(?:Document(?:ForDocumentID|IDArrayForTermID|Properties|RefsForDocumentIDs|URLsForDocumentIDs)|InfoForDocumentIDs|Term(?:IDArrayForDocumentID|StringForTermID)))|reateWith(?:MutableData|URL))|DocumentIterator(?:C(?:opyNext|reate)|GetTypeID)|Flush|Get(?:AnalysisProperties|Document(?:Count|ID|State|Term(?:Count|Frequency))|IndexType|Maximum(?:BytesBeforeFlush|DocumentID|TermID)|T(?:erm(?:DocumentCount|IDForTermString)|ypeID))|MoveDocument|OpenWith(?:Data|MutableData|URL)|Re(?:moveDocument|nameDocument)|Set(?:DocumentProperties|MaximumBytesBeforeFlush))|LoadDefaultExtractorPlugIns|S(?:earch(?:C(?:ancel|reate)|FindMatches|GetTypeID)|ummary(?:C(?:opy(?:Paragraph(?:AtIndex|SummaryString)|Sentence(?:AtIndex|SummaryString))|reateWithString)|Get(?:Paragraph(?:Count|SummaryInfo)|Sentence(?:Count|SummaryInfo)|TypeID))))|e(?:c(?:A(?:CL(?:C(?:opy(?:Authorizations|Contents)|reateWithSimpleContents)|GetTypeID|Remove|SetContents|UpdateAuthorizations)|ccess(?:C(?:opy(?:ACLList|MatchingACLList|OwnerAndACL)|reate(?:WithOwnerAndACL)?)|GetTypeID)|ddSharedWebCredential)|C(?:ertificate(?:AddToKeychain|C(?:opy(?:CommonName|Data|EmailAddresses|LongDescription|Preferred|S(?:hortDescription|ubjectSummary)|Values)|reateWithData)|GetTypeID|SetPreferred)|o(?:de(?:C(?:heckValidity(?:WithErrors)?|opy(?:DesignatedRequirement|GuestWithAttributes|Host|Path|S(?:elf|igningInformation|taticCode)))|GetTypeID|MapMemory)|pyErrorMessageString)|reateSharedWebCredentialPassword)|D(?:ec(?:odeTransformCreate|ryptTransform(?:Create|GetTypeID))|igestTransform(?:Create|GetTypeID))|Enc(?:odeTransformCreate|ryptTransform(?:Create|GetTypeID))|GroupTransformGetTypeID|I(?:dentity(?:C(?:opy(?:Certificate|Pr(?:eferred|ivateKey)|SystemIdentity)|reateWithCertificate)|GetTypeID|Set(?:Preferred|SystemIdentity))|tem(?:Add|CopyMatching|Delete|Export|Import|Update))|Key(?:CreateFromData|DeriveFromPassword|Ge(?:nerate(?:Pair(?:Async)?|Symmetric)|t(?:BlockSize|TypeID))|UnwrapSymmetric|WrapSymmetric|chain(?:A(?:dd(?:Callback|GenericPassword|InternetPassword)|ttributeInfoForItemID)|C(?:opy(?:D(?:efault|omain(?:Default|SearchList))|Se(?:archList|ttings))|reate)|Delete|F(?:ind(?:GenericPassword|InternetPassword)|reeAttributeInfo)|Get(?:P(?:ath|referenceDomain)|Status|TypeID|UserInteractionAllowed|Version)|Item(?:C(?:opy(?:A(?:ccess|ttributesAndData)|Content|FromPersistentReference|Keychain)|reate(?:Copy|FromContent|PersistentReference))|Delete|Free(?:AttributesAndData|Content)|GetTypeID|Modify(?:AttributesAndData|Content)|SetAccess)|Lock(?:All)?|Open|RemoveCallback|Set(?:D(?:efault|omain(?:Default|SearchList))|PreferenceDomain|Se(?:archList|ttings)|UserInteractionAllowed)|Unlock))|P(?:KCS12Import|olicy(?:C(?:opyProperties|reate(?:BasicX509|SSL))|GetTypeID))|R(?:andomCopyBytes|equ(?:estSharedWebCredential|irement(?:C(?:opy(?:Data|String)|reateWith(?:Data|String(?:AndErrors)?))|GetTypeID)))|S(?:ignTransformCreate|taticCode(?:C(?:heckValidity(?:WithErrors)?|reateWithPath(?:AndAttributes)?)|GetTypeID))|T(?:ask(?:C(?:opy(?:SigningIdentifier|Value(?:ForEntitlement|sForEntitlements))|reate(?:FromSelf|WithAuditToken))|Get(?:CodeSignStatus|TypeID))|r(?:ansform(?:C(?:o(?:nnectTransforms|pyExternalRepresentation)|reate(?:FromExternalRepresentation|GroupTransform|ReadTransformWithReadStream)?|ustom(?:GetAttribute|SetAttribute))|Execute(?:Async)?|FindByName|Get(?:Attribute|TypeID)|NoData|PushbackAttribute|Register|Set(?:Attribute(?:Action)?|DataAction|TransformAction))|ust(?:C(?:opy(?:AnchorCertificates|CustomAnchorCertificates|P(?:olicies|roperties|ublicKey))|reateWithCertificates)|Get(?:Certificate(?:AtIndex|Count)|T(?:rustResult|ypeID)|VerifyTime)|Set(?:AnchorCertificates(?:Only)?|Options|Policies|VerifyDate|tings(?:C(?:opy(?:Certificates|ModificationDate|TrustSettings)|reateExternalRepresentation)|ImportExternalRepresentation|RemoveTrustSettings|SetTrustSettings))|edApplicationGetTypeID)))|VerifyTransformCreate)|ndEventToEventTarget(?:WithOptions)?|ssion(?:Create|GetInfo)|t(?:AudioUnitParameterDisplayType|Event(?:LoopTimerNextFireTime|Parameter|Time)|F(?:allbackUnicodeToText(?:Run)?|ontInfoForSelection)|IconFamilyData|S(?:peech(?:P(?:itch|roperty)|Rate)|ystemUIMode)))|pe(?:akCFString|ech(?:Busy(?:SystemWide)?|ManagerVersion|Synthesis(?:RegisterModuleURL|UnregisterModuleURL)))|topSpeech(?:At)?)|T(?:EC(?:C(?:lear(?:ConverterContextInfo|SnifferContextInfo)|o(?:nvertText(?:ToMultipleEncodings)?|pyTextEncodingInternetNameAndMIB|unt(?:Available(?:Sniffers|TextEncodings)|D(?:estinationTextEncodings|irectTextEncodingConversions)|MailTextEncodings|SubTextEncodings|WebTextEncodings))|reate(?:Converter(?:FromPath)?|OneToManyConverter|Sniffer))|Dispose(?:Converter|Sniffer)|Flush(?:MultipleEncodings|Text)|Get(?:Available(?:Sniffers|TextEncodings)|D(?:estinationTextEncodings|irectTextEncodingConversions)|EncodingList|Info|MailTextEncodings|SubTextEncodings|TextEncoding(?:FromInternetName(?:OrMIB)?|InternetName)|WebTextEncodings)|S(?:etBasicOptions|niffTextEncoding))|IS(?:C(?:opy(?:Current(?:ASCIICapableKeyboard(?:InputSource|LayoutInputSource)|Keyboard(?:InputSource|LayoutInputSource))|Input(?:MethodKeyboardLayoutOverride|SourceForLanguage))|reate(?:ASCIICapableInputSourceList|InputSourceList))|D(?:eselectInputSource|isableInputSource)|EnableInputSource|GetInputSourceProperty|InputSourceGetTypeID|RegisterInputSource|Se(?:lectInputSource|tInputMethodKeyboardLayoutOverride))|SM(?:Get(?:ActiveDocument|DocumentProperty)|RemoveDocumentProperty|SetDocumentProperty)|r(?:ans(?:formProcessType|lation(?:C(?:opy(?:DestinationType|SourceType)|reate(?:WithSourceArray)?)|GetT(?:ranslationFlags|ypeID)|PerformFor(?:Data|File|URL)))|uncateFor(?:TextToUnicode|UnicodeToText)))|U(?:32SetU|64(?:A(?:dd|nd)|Bitwise(?:And|Eor|Not|Or)|Div(?:ide)?|Eor|M(?:ax|od|ultiply)|Not|Or|S(?:et(?:U)?|hift(?:Left|Right)|ubtract))|AZoom(?:ChangeFocus|Enabled)|C(?:C(?:o(?:mpare(?:CollationKeys|Text(?:Default|NoLocale)?)|nvert(?:CFAbsoluteTimeTo(?:LongDateTime|Seconds|UTCDateTime)|LongDateTimeToCFAbsoluteTime|SecondsToCFAbsoluteTime|UTCDateTimeToCFAbsoluteTime))|reateCollator)|DisposeCollator|Get(?:C(?:harProperty|ollationKey)|UnicodeScalarValueForSurrogatePair)|IsSurrogate(?:HighCharacter|LowCharacter)|KeyTranslate|TypeSelect(?:AddKeyToSelector|C(?:ompare|reateSelector)|F(?:indItem|lushSelectorData)|ReleaseSelector|W(?:alkList|ouldResetBuffer)))|Int64To(?:LongDouble|SInt64|UnsignedWide)|T(?:CreateStringForOSType|GetOSTypeFromString|Type(?:C(?:o(?:nformsTo|py(?:De(?:clar(?:ation|ingBundleURL)|scription)|PreferredTagWithClass))|reate(?:AllIdentifiersForTag|PreferredIdentifierForTag))|Equal))|n(?:registerEventHotKey|signedWideToUInt64)|pgradeScriptInfoToTextEncoding|seSpeechDictionary)|WideToSInt64|a(?:c(?:cept|l_(?:add_(?:flag_np|perm)|c(?:alc_mask|lear_(?:flags_np|perms)|opy_(?:e(?:ntry|xt(?:_native)?)|int(?:_native)?)|reate_entry(?:_np)?)|d(?:elete_(?:def_file|entry|flag_np|perm)|up)|fr(?:ee|om_text)|get_(?:entry|f(?:d(?:_np)?|ile|lag(?:_np|set_np))|link_np|perm(?:_np|set(?:_mask_np)?)|qualifier|tag_type)|init|maximal_permset_mask_np|s(?:et_(?:f(?:d(?:_np)?|ile|lagset_np)|link_np|permset(?:_mask_np)?|qualifier|tag_type)|ize)|to_text|valid(?:_(?:f(?:d_np|ile_np)|link_np))?)|t_(?:get_state|set_state))|udit(?:_session_(?:join|port|self)|ctl|on)?)|bind|c(?:a(?:bs(?:f|l)?|cos(?:f|h(?:f|l)?|l)?|lloc|rg(?:f|l)?|sin(?:f|h(?:f|l)?|l)?|tan(?:f|h(?:f|l)?|l)?)|cos(?:f|h(?:f|l)?|l)?|exp(?:f|l)?|imag(?:f|l)?|lo(?:ck_(?:get_res|s(?:et_(?:attributes|res|time)|leep(?:_trap)?))|g(?:f|l)?)|on(?:j(?:f|l)?|nect)|p(?:ow(?:f|l)?|roj(?:f|l)?)|real(?:f|l)?|s(?:in(?:f|h(?:f|l)?|l)?|qrt(?:f|l)?|sm(?:AlgToOid|OidToAlg|Perror))|t(?:an(?:f|h(?:f|l)?|l)?|ermid))|d(?:e(?:bug_control_port_for_pid|c2numl)|igittoint)|etap_trace_thread|f(?:e(?:clearexcept|get(?:e(?:nv|xceptflag)|round)|holdexcept|raiseexcept|set(?:e(?:nv|xceptflag)|round)|testexcept|updateenv)|ree)|g(?:et(?:au(?:dit_addr|id)|peername|sock(?:name|opt))|l(?:A(?:c(?:cum|tive(?:StencilFaceEXT|Texture(?:ARB)?))|lphaFunc|r(?:eTexturesResident|rayElement)|ttach(?:ObjectARB|Shader))|B(?:egin(?:ConditionalRenderNV|Query(?:ARB)?|TransformFeedbackEXT)?|i(?:nd(?:AttribLocation(?:ARB)?|Buffer(?:ARB|BaseEXT|OffsetEXT|RangeEXT)?|Fra(?:gDataLocationEXT|mebuffer(?:EXT)?)|ProgramARB|Renderbuffer(?:EXT)?|Texture|VertexArrayAPPLE)|tmap)|l(?:end(?:Color(?:EXT)?|Equation(?:EXT|Separate(?:ATI|EXT)?)?|Func(?:Separate(?:EXT)?)?)|itFramebuffer(?:EXT)?)|uffer(?:Data(?:ARB)?|ParameteriAPPLE|SubData(?:ARB)?))|C(?:allList(?:s)?|heckFramebufferStatus(?:EXT)?|l(?:ampColorARB|ear(?:Accum|Color(?:I(?:iEXT|uiEXT))?|Depth|Index|Stencil)?|i(?:ent(?:ActiveTexture(?:ARB)?|WaitSync)|pPlane))|o(?:lor(?:3(?:b(?:v)?|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?|u(?:b(?:v)?|i(?:v)?|s(?:v)?))|4(?:b(?:v)?|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?|u(?:b(?:v)?|i(?:v)?|s(?:v)?))|Ma(?:sk(?:IndexedEXT)?|terial)|Pointer|SubTable|Table(?:Parameter(?:fv|iv))?)|mp(?:ileShader(?:ARB)?|ressedTex(?:Image(?:1D(?:ARB)?|2D(?:ARB)?|3D(?:ARB)?)|SubImage(?:1D(?:ARB)?|2D(?:ARB)?|3D(?:ARB)?)))|nvolution(?:Filter(?:1D|2D)|Parameter(?:f(?:v)?|i(?:v)?))|py(?:Co(?:lor(?:SubTable|Table)|nvolutionFilter(?:1D|2D))|Pixels|Tex(?:Image(?:1D|2D)|SubImage(?:1D|2D|3D))))|reate(?:Program(?:ObjectARB)?|Shader(?:ObjectARB)?)|ullFace)|D(?:e(?:lete(?:Buffers(?:ARB)?|F(?:encesAPPLE|ramebuffers(?:EXT)?)|Lists|ObjectARB|Program(?:sARB)?|Queries(?:ARB)?|Renderbuffers(?:EXT)?|S(?:hader|ync)|Textures|VertexArraysAPPLE)|pth(?:BoundsEXT|Func|Mask|Range)|tach(?:ObjectARB|Shader))|isable(?:ClientState|IndexedEXT|VertexAttribA(?:PPLE|rray(?:ARB)?))?|raw(?:Arrays(?:InstancedARB)?|Buffer(?:s(?:ARB)?)?|Element(?:ArrayAPPLE|s(?:BaseVertex|Instanced(?:ARB|BaseVertex))?)|Pixels|RangeElement(?:ArrayAPPLE|s(?:BaseVertex|EXT)?)))|E(?:dgeFlag(?:Pointer|v)?|lementPointerAPPLE|n(?:able(?:ClientState|IndexedEXT|VertexAttribA(?:PPLE|rray(?:ARB)?))?|d(?:ConditionalRenderNV|List|Query(?:ARB)?|TransformFeedbackEXT)?)|val(?:Coord(?:1(?:d(?:v)?|f(?:v)?)|2(?:d(?:v)?|f(?:v)?))|Mesh(?:1|2)|Point(?:1|2)))|F(?:e(?:edbackBuffer|nceSync)|inish(?:FenceAPPLE|ObjectAPPLE|RenderAPPLE)?|lush(?:MappedBufferRangeAPPLE|RenderAPPLE|VertexArrayRangeAPPLE)?|og(?:Coord(?:Pointer(?:EXT)?|d(?:EXT|v(?:EXT)?)?|f(?:EXT|v(?:EXT)?)?)|f(?:v)?|i(?:v)?)|r(?:amebuffer(?:Renderbuffer(?:EXT)?|Texture(?:1D(?:EXT)?|2D(?:EXT)?|3D(?:EXT)?|EXT|FaceEXT|Layer(?:EXT)?))|ontFace|ustum))|Ge(?:n(?:Buffers(?:ARB)?|F(?:encesAPPLE|ramebuffers(?:EXT)?)|Lists|ProgramsARB|Queries(?:ARB)?|Renderbuffers(?:EXT)?|Textures|VertexArraysAPPLE|erateMipmap(?:EXT)?)|t(?:A(?:ctive(?:Attrib(?:ARB)?|Uniform(?:ARB)?)|tt(?:ached(?:ObjectsARB|Shaders)|ribLocation(?:ARB)?))|B(?:oolean(?:IndexedvEXT|v)|uffer(?:P(?:arameteriv(?:ARB)?|ointerv(?:ARB)?)|SubData(?:ARB)?))|C(?:lipPlane|o(?:lorTable(?:Parameter(?:fv|iv))?|mpressedTexImage(?:ARB)?|nvolution(?:Filter|Parameter(?:fv|iv))))|Doublev|Error|F(?:loatv|ra(?:gDataLocationEXT|mebufferAttachmentParameteriv(?:EXT)?))|H(?:andleARB|istogram(?:Parameter(?:fv|iv))?)|In(?:foLogARB|teger(?:64v|IndexedvEXT|v))|Light(?:fv|iv)|M(?:a(?:p(?:dv|fv|iv)|terial(?:fv|iv))|inmax(?:Parameter(?:fv|iv))?)|Object(?:LabelEXT|Parameter(?:fvARB|ivA(?:PPLE|RB)))|P(?:ixelMap(?:fv|u(?:iv|sv))|o(?:interv|lygonStipple)|rogram(?:EnvParameter(?:dvARB|fvARB)|InfoLog|LocalParameter(?:dvARB|fvARB)|StringARB|iv(?:ARB)?))|Query(?:Object(?:i(?:64vEXT|v(?:ARB)?)|ui(?:64vEXT|v(?:ARB)?))|iv(?:ARB)?)|RenderbufferParameteriv(?:EXT)?|S(?:eparableFilter|hader(?:InfoLog|Source(?:ARB)?|iv)|tring|ynciv)|T(?:ex(?:Env(?:fv|iv)|Gen(?:dv|fv|iv)|Image|LevelParameter(?:fv|iv)|Parameter(?:I(?:ivEXT|uivEXT)|PointervAPPLE|fv|iv))|ransformFeedbackVaryingEXT)|Uniform(?:BufferSizeEXT|Location(?:ARB)?|OffsetEXT|fv(?:ARB)?|iv(?:ARB)?|uivEXT)|VertexAttrib(?:I(?:ivEXT|uivEXT)|Pointerv(?:ARB)?|dv(?:ARB)?|fv(?:ARB)?|iv(?:ARB)?)))|Hi(?:nt|stogram)|I(?:n(?:dex(?:Mask|Pointer|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?|ub(?:v)?)|itNames|sertEventMarkerEXT|terleavedArrays)|s(?:Buffer(?:ARB)?|Enabled(?:IndexedEXT)?|F(?:enceAPPLE|ramebuffer(?:EXT)?)|List|Program(?:ARB)?|Query(?:ARB)?|Renderbuffer(?:EXT)?|S(?:hader|ync)|Texture|VertexA(?:rrayAPPLE|ttribEnabledAPPLE)))|L(?:abelObjectEXT|i(?:ght(?:Model(?:f(?:v)?|i(?:v)?)|f(?:v)?|i(?:v)?)|n(?:e(?:Stipple|Width)|kProgram(?:ARB)?)|stBase)|o(?:ad(?:Identity|Matrix(?:d|f)|Name|TransposeMatrix(?:d(?:ARB)?|f(?:ARB)?))|gicOp))|M(?:a(?:p(?:1(?:d|f)|2(?:d|f)|Buffer(?:ARB)?|Grid(?:1(?:d|f)|2(?:d|f))|VertexAttrib(?:1(?:dAPPLE|fAPPLE)|2(?:dAPPLE|fAPPLE)))|t(?:erial(?:f(?:v)?|i(?:v)?)|rixMode))|inmax|ult(?:Matrix(?:d|f)|TransposeMatrix(?:d(?:ARB)?|f(?:ARB)?)|i(?:Draw(?:Arrays(?:EXT)?|Element(?:ArrayAPPLE|s(?:BaseVertex|EXT)?)|RangeElementArrayAPPLE)|TexCoord(?:1(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|2(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|3(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|4(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)))))|N(?:ewList|ormal(?:3(?:b(?:v)?|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|Pointer))|O(?:bject(?:PurgeableAPPLE|UnpurgeableAPPLE)|rtho)|P(?:assThrough|ixel(?:Map(?:fv|u(?:iv|sv))|Store(?:f|i)|Transfer(?:f|i)|Zoom)|o(?:int(?:Parameter(?:f(?:ARB|v(?:ARB)?)?|i(?:NV|v(?:NV)?)?)|Size(?:PointerAPPLE)?)|lygon(?:Mode|Offset|Stipple)|p(?:Attrib|ClientAttrib|GroupMarkerEXT|Matrix|Name))|r(?:ioritizeTextures|o(?:gram(?:EnvParameter(?:4(?:d(?:ARB|vARB)|f(?:ARB|vARB))|s4fvEXT)|LocalParameter(?:4(?:d(?:ARB|vARB)|f(?:ARB|vARB))|s4fvEXT)|ParameteriEXT|StringARB)|vokingVertex(?:EXT)?))|ush(?:Attrib|ClientAttrib|GroupMarkerEXT|Matrix|Name))|R(?:asterPos(?:2(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|3(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|4(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?))|e(?:ad(?:Buffer|Pixels)|ct(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|nder(?:Mode|bufferStorage(?:EXT|Multisample(?:EXT)?)?)|set(?:Histogram|Minmax))|otate(?:d|f))|S(?:ampleCoverage(?:ARB)?|c(?:ale(?:d|f)|issor)|e(?:condaryColor(?:3(?:b(?:EXT|v(?:EXT)?)?|d(?:EXT|v(?:EXT)?)?|f(?:EXT|v(?:EXT)?)?|i(?:EXT|v(?:EXT)?)?|s(?:EXT|v(?:EXT)?)?|u(?:b(?:EXT|v(?:EXT)?)?|i(?:EXT|v(?:EXT)?)?|s(?:EXT|v(?:EXT)?)?))|Pointer(?:EXT)?)|lectBuffer|parableFilter2D|tFenceAPPLE)|hade(?:Model|rSource(?:ARB)?)|tencil(?:Func(?:Separate(?:ATI)?)?|Mask(?:Separate)?|Op(?:Separate(?:ATI)?)?)|wapAPPLE)|T(?:e(?:st(?:FenceAPPLE|ObjectAPPLE)|x(?:Coord(?:1(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|2(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|3(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|4(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|Pointer)|Env(?:f(?:v)?|i(?:v)?)|Gen(?:d(?:v)?|f(?:v)?|i(?:v)?)|Image(?:1D|2D|3D)|Parameter(?:I(?:ivEXT|uivEXT)|f(?:v)?|i(?:v)?)|SubImage(?:1D|2D|3D)|ture(?:BarrierNV|RangeAPPLE)))|rans(?:formFeedbackVaryingsEXT|late(?:d|f)))|U(?:n(?:iform(?:1(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|2(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|3(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|4(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|BufferEXT|Matrix(?:2(?:fv(?:ARB)?|x(?:3fv|4fv))|3(?:fv(?:ARB)?|x(?:2fv|4fv))|4(?:fv(?:ARB)?|x(?:2fv|3fv))))|mapBuffer(?:ARB)?)|seProgram(?:ObjectARB)?)|V(?:alidateProgram(?:ARB)?|ertex(?:2(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|3(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|4(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|A(?:rray(?:ParameteriAPPLE|RangeAPPLE)|ttrib(?:1(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|2(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|3(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|4(?:N(?:bv(?:ARB)?|iv(?:ARB)?|sv(?:ARB)?|u(?:b(?:ARB|v(?:ARB)?)?|iv(?:ARB)?|sv(?:ARB)?))|bv(?:ARB)?|d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|iv(?:ARB)?|s(?:ARB|v(?:ARB)?)?|u(?:bv(?:ARB)?|iv(?:ARB)?|sv(?:ARB)?))|DivisorARB|I(?:1(?:i(?:EXT|vEXT)|ui(?:EXT|vEXT))|2(?:i(?:EXT|vEXT)|ui(?:EXT|vEXT))|3(?:i(?:EXT|vEXT)|ui(?:EXT|vEXT))|4(?:bvEXT|i(?:EXT|vEXT)|svEXT|u(?:bvEXT|i(?:EXT|vEXT)|svEXT))|PointerEXT)|Pointer(?:ARB)?))|BlendARB|Point(?:SizefAPPLE|er))|iewport)|W(?:aitSync|eight(?:PointerARB|bvARB|dvARB|fvARB|ivARB|svARB|u(?:bvARB|ivARB|svARB))|indowPos(?:2(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|3(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)))))|host_(?:c(?:heck_multiuser_mode|reate_mach_voucher(?:_trap)?)|default_memory_manager|get_(?:UNDServer|atm_diagnostic_flag|boot_info|clock_(?:control|service)|exception_ports|io_master|multiuser_config_flags|special_port)|info|kernel_version|lockgroup_info|p(?:age_size|r(?:iv_statistics|ocessor(?:_(?:info|set(?:_priv|s))|s)))|re(?:boot|gister_(?:mach_voucher_attr_manager|well_known_mach_voucher_attr_manager)|quest_notification)|s(?:e(?:curity_(?:create_task_token|set_task_token)|t_(?:UNDServer|atm_diagnostic_flag|exception_ports|multiuser_config_flags|special_port))|tatistics(?:64)?|wap_exception_ports)|virtual_physical_table_info)|i(?:max(?:abs|div)|s(?:a(?:l(?:num|pha)|scii)|blank|cntrl|digit|graph|hexnumber|ideogram|lower|number|p(?:honogram|rint|unct)|rune|sp(?:ace|ecial)|upper|xdigit))|k(?:ext_request|mod_(?:c(?:ontrol|reate)|destroy|get_info))|l(?:dtox80|isten|ock_(?:acquire|handoff(?:_accept)?|make_stable|release|set_(?:create|destroy)|try))|m(?:a(?:c(?:h_(?:error(?:_(?:string|type))?|generate_activity_id|host_self|m(?:ake_memory_entry(?:_64)?|emory_(?:info|object_memory_entry(?:_64)?)|sg(?:_(?:destroy|overwrite|receive|se(?:nd|rver(?:_(?:importance|once))?)))?)|port(?:_(?:allocate(?:_(?:full|name|qos))?|construct|d(?:e(?:allocate|str(?:oy|uct))|nrequest_info)|extract_(?:member|right)|g(?:et_(?:attributes|context|refs|s(?:et_status|rights))|uard(?:_with_flags)?)|insert_(?:member|right)|k(?:ernel_object|object(?:_description)?)|mo(?:d_refs|ve_member)|names|peek|re(?:name|quest_notification)|s(?:et_(?:attributes|context|mscount|seqno)|pace_(?:basic_info|info)|wap_guard)|type|unguard)|s_(?:lookup|register))|thread_self|v(?:m_(?:region_info(?:_64)?|wire)|oucher_(?:deallocate|extract_attr_recipe_trap))|zone_info(?:_for_zone)?)|x_(?:backing_store_(?:recovery|suspend)|swapo(?:ff|n)|triggers))|dvise|lloc|trix_(?:multiply|scale))|i(?:g_(?:allocate|dealloc(?:_reply_port|ate)|get_reply_port|put_reply_port|reply_setup|strncpy(?:_zerofill)?)|n(?:core|herit))|lock(?:all)?|map|protect|sync|un(?:lock(?:all)?|map))|num2decl|p(?:anic(?:_init)?|fctlinput|id_for_task|osix_m(?:advise|emalign)|rocessor_(?:assign|control|exit|get_assignment|info|s(?:et_(?:create|de(?:fault|stroy)|info|max_priority|policy_(?:control|disable|enable)|sta(?:ck_usage|tistics)|t(?:asks|hreads))|tart)))|re(?:alloc|cv(?:from|msg)?|lationl)|s(?:afe_gets|e(?:c_re(?:lease|tain)|maphore_(?:create|destroy|signal(?:_(?:all|thread))?|timedwait(?:_signal)?|wait(?:_signal)?)|nd(?:file|msg|to)?|t(?:au(?:dit_addr|id)|sockopt))|h(?:m_(?:open|unlink)|utdown)|imd_(?:a(?:bs|ct|dd|l(?:l|most_equal_elements(?:_relative)?)|n(?:gle|y)|xis)|b(?:ezier|itselect)|c(?:har(?:_sat)?|lamp|onjugate|ross)|d(?:eterminant|i(?:agonal_matrix|stance(?:_squared)?)|o(?:t|uble))|equal|f(?:ast_(?:distance|length|normalize|project|r(?:ecip|sqrt))|loat|ract)|i(?:mag|n(?:circle|sphere|t(?:_sat)?|verse))|l(?:ength(?:_squared)?|inear_combination|ong(?:_sat)?)|m(?:a(?:ke_(?:char(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|64(?:_undef)?|8(?:_undef)?)|double(?:2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|float(?:16(?:_undef)?|2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|int(?:16(?:_undef)?|2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|long(?:2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|short(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|8(?:_undef)?)|u(?:char(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|64(?:_undef)?|8(?:_undef)?)|int(?:16(?:_undef)?|2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|long(?:2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|short(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|8(?:_undef)?)))|trix(?:3x3|4x4|_from_rows)?|x)|i(?:n|x)|ul)|n(?:egate|orm(?:_(?:inf|one)|alize))|orient|pr(?:ecise_(?:distance|length|normalize|project|r(?:ecip|sqrt))|oject)|quaternion|r(?:e(?:al|cip|duce_(?:add|m(?:ax|in))|f(?:lect|ract))|sqrt)|s(?:elect|hort(?:_sat)?|ign|lerp(?:_longest)?|moothstep|pline|tep|ub)|transpose|u(?:char(?:_sat)?|int(?:_sat)?|long(?:_sat)?|short(?:_sat)?))|lot_name|ock(?:atmark|et(?:pair)?)|trto(?:imax|umax)|wtch(?:_pri)?)|t(?:ask_(?:assign(?:_default)?|create(?:_suid_cred)?|for_pid|ge(?:nerate_corpse|t_(?:assignment|dyld_image_infos|e(?:mulation_vector|xc(?:_guard_behavior|eption_ports))|mach_voucher|s(?:pecial_port|tate)))|in(?:fo|spect)|map_corpse_info(?:_64)?|name_for_pid|p(?:olicy(?:_(?:get|set))?|urgable_info)|re(?:gister_dyld_(?:get_process_state|image_infos|s(?:et_dyld_state|hared_cache_image_info))|sume(?:2)?)|s(?:ample|e(?:lf_trap|t_(?:e(?:mulation(?:_vector)?|xc(?:_guard_behavior|eption_ports))|info|mach_voucher|p(?:hys_footprint_limit|o(?:licy|rt_space))|ras_pc|s(?:pecial_port|tate)))|uspend(?:2)?|wap_(?:exception_ports|mach_voucher))|t(?:erminate|hreads)|unregister_dyld_image_infos|wire|zone_info)|hread_(?:a(?:bort(?:_safely)?|ssign(?:_default)?)|create(?:_running)?|depress_abort|get_(?:assignment|exception_ports|mach_voucher|s(?:pecial_port|tate))|info|policy(?:_(?:get|set))?|resume|s(?:ample|et_(?:exception_ports|mach_voucher|policy|s(?:pecial_port|tate))|uspend|w(?:ap_(?:exception_ports|mach_voucher)|itch))|terminate|wire)|o(?:ascii|lower|upper))|uuid_(?:c(?:lear|o(?:mpare|py))|generate(?:_(?:early_random|random|time))?|is_null|parse|unparse(?:_(?:lower|upper))?)|v(?:AEBuild(?:AppleEvent|Desc|Parameters)|alloc|ector(?:16|2|3(?:2)?|4|8)|m_(?:allocate(?:_cpm)?|behavior_set|copy|deallocate|inherit|m(?:a(?:chine_attribute|p(?:_(?:64|exec_lockdown|page_query)|ped_pages_info)?)|sync)|p(?:rotect|urgable_control)|re(?:ad(?:_(?:list|overwrite))?|gion(?:_(?:64|recurse(?:_64)?))?|map)|w(?:ire|rite))|oucher_mach_msg_(?:adopt|clear|revert|set))|wcsto(?:imax|umax)|x(?:80told|pc_(?:array_(?:app(?:end_value|ly)|create(?:_connection)?|dup_fd|get_(?:bool|count|d(?:at(?:a|e)|ouble)|int64|string|u(?:int64|uid)|value)|set_(?:bool|connection|d(?:at(?:a|e)|ouble)|fd|int64|string|u(?:int64|uid)|value))|bool_(?:create|get_value)|co(?:nnection_(?:c(?:ancel|reate(?:_(?:from_endpoint|mach_service))?)|get_(?:asid|context|e(?:gid|uid)|name|pid)|resume|s(?:e(?:nd_(?:barrier|message(?:_with_reply(?:_sync)?)?)|t_(?:context|event_handler|finalizer_f|target_queue))|uspend))|py(?:_description)?)|d(?:at(?:a_(?:create(?:_with_dispatch_data)?|get_(?:bytes(?:_ptr)?|length))|e_(?:create(?:_from_current)?|get_value))|ebugger_api_misuse_info|ictionary_(?:apply|create(?:_(?:connection|reply))?|dup_fd|get_(?:bool|count|d(?:at(?:a|e)|ouble)|int64|remote_connection|string|u(?:int64|uid)|value)|set_(?:bool|connection|d(?:at(?:a|e)|ouble)|fd|int64|string|u(?:int64|uid)|value))|ouble_(?:create|get_value))|e(?:ndpoint_create|qual)|fd_(?:create|dup)|get_type|hash|int64_(?:create|get_value)|main|null_create|re(?:lease|tain)|s(?:et_event_stream_handler|hmem_(?:create|map)|tring_(?:create(?:_with_format(?:_and_arguments)?)?|get_(?:length|string_ptr)))|transaction_(?:begin|end)|u(?:int64_(?:create|get_value)|uid_(?:create|get_bytes)))))\\b)" + }, { "captures": { "1": { @@ -376,7 +898,7 @@ "name": "support.function.cf.c" } }, - "match": "(\\s*)(\\bCF(?:A(?:bsoluteTimeGetCurrent|llocator(?:Allocate|Create|Deallocate|Get(?:Context|Default|PreferredSizeForSize|TypeID)|Reallocate|SetDefault)|rray(?:App(?:end(?:Array|Value)|lyFunction)|BSearchValues|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|ExchangeValuesAtIndices|Get(?:Count(?:OfValue)?|FirstIndexOfValue|LastIndexOfValue|TypeID|Value(?:AtIndex|s))|InsertValueAtIndex|Re(?:move(?:AllValues|ValueAtIndex)|placeValues)|S(?:etValueAtIndex|ortValues))|ttributedString(?:BeginEditing|Create(?:Copy|Mutable(?:Copy)?|WithSubstring)?|EndEditing|Get(?:Attribute(?:AndLongestEffectiveRange|s(?:AndLongestEffectiveRange)?)?|Length|MutableString|String|TypeID)|Re(?:moveAttribute|place(?:AttributedString|String))|SetAttribute(?:s)?))|B(?:ag(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|i(?:naryHeap(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy)?)|Get(?:Count(?:OfValue)?|Minimum(?:IfPresent)?|TypeID|Values)|Remove(?:AllValues|MinimumValue))|tVector(?:C(?:ontainsBit|reate(?:Copy|Mutable(?:Copy)?)?)|FlipBit(?:AtIndex|s)|Get(?:Bit(?:AtIndex|s)|Count(?:OfBit)?|FirstIndexOfBit|LastIndexOfBit|TypeID)|Set(?:AllBits|Bit(?:AtIndex|s)|Count)))|ooleanGet(?:TypeID|Value)|undle(?:C(?:loseBundleResourceMap|opy(?:AuxiliaryExecutableURL|Bu(?:iltInPlugInsURL|ndle(?:Localizations|URL))|Executable(?:Architectures(?:ForURL)?|URL)|InfoDictionary(?:ForURL|InDirectory)|Localiz(?:ationsFor(?:Preferences|URL)|edString)|Pr(?:eferredLocalizationsFromArray|ivateFrameworksURL)|Resource(?:URL(?:ForLocalization|InDirectory|sOfType(?:ForLocalization|InDirectory)?)?|sDirectoryURL)|S(?:hared(?:FrameworksURL|SupportURL)|upportFilesDirectoryURL))|reate(?:BundlesFromDirectory)?)|Get(?:AllBundles|BundleWithIdentifier|D(?:ataPointer(?:ForName|sForNames)|evelopmentRegion)|FunctionPointer(?:ForName|sForNames)|I(?:dentifier|nfoDictionary)|LocalInfoDictionary|MainBundle|P(?:ackageInfo(?:InDirectory)?|lugIn)|TypeID|V(?:alueForInfoDictionaryKey|ersionNumber))|IsExecutableLoaded|LoadExecutable(?:AndReturnError)?|OpenBundleResource(?:Files|Map)|PreflightExecutable|UnloadExecutable)|yteOrderGetCurrent)|C(?:alendar(?:AddComponents|C(?:o(?:mposeAbsoluteTime|py(?:Current|Locale|TimeZone))|reateWithIdentifier)|DecomposeAbsoluteTime|Get(?:ComponentDifference|FirstWeekday|Identifier|M(?:aximumRangeOfUnit|inimum(?:DaysInFirstWeek|RangeOfUnit))|OrdinalityOfUnit|RangeOfUnit|T(?:imeRangeOfUnit|ypeID))|Set(?:FirstWeekday|Locale|MinimumDaysInFirstWeek|TimeZone))|haracterSet(?:AddCharactersIn(?:Range|String)|Create(?:BitmapRepresentation|Copy|InvertedSet|Mutable(?:Copy)?|With(?:BitmapRepresentation|CharactersIn(?:Range|String)))|Get(?:Predefined|TypeID)|HasMemberInPlane|I(?:n(?:tersect|vert)|s(?:CharacterMember|LongCharacterMember|SupersetOfSet))|RemoveCharactersIn(?:Range|String)|Union)|o(?:nvert(?:Double(?:HostToSwapped|SwappedToHost)|Float(?:32(?:HostToSwapped|SwappedToHost)|64(?:HostToSwapped|SwappedToHost)|HostToSwapped|SwappedToHost))|py(?:Description|HomeDirectoryURL|TypeIDDescription)))|D(?:at(?:a(?:AppendBytes|Create(?:Copy|Mutable(?:Copy)?|WithBytesNoCopy)?|DeleteBytes|Find|Get(?:Byte(?:Ptr|s)|Length|MutableBytePtr|TypeID)|IncreaseLength|ReplaceBytes|SetLength)|e(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:DateF(?:ormatFromTemplate|romString)|StringWith(?:AbsoluteTime|Date))?)|Get(?:AbsoluteTimeFromString|DateStyle|Format|Locale|T(?:imeStyle|ypeID))|Set(?:Format|Property))|Get(?:AbsoluteTime|T(?:imeIntervalSinceDate|ypeID))))|ictionary(?:A(?:ddValue|pplyFunction)|C(?:ontains(?:Key|Value)|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:Of(?:Key|Value))?|KeysAndValues|TypeID|Value(?:IfPresent)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue))|E(?:qual|rror(?:C(?:opy(?:Description|FailureReason|RecoverySuggestion|UserInfo)|reate(?:WithUserInfoKeysAndValues)?)|Get(?:Code|Domain|TypeID)))|File(?:Descriptor(?:Create(?:RunLoopSource)?|DisableCallBacks|EnableCallBacks|Get(?:Context|NativeDescriptor|TypeID)|I(?:nvalidate|sValid))|Security(?:C(?:opy(?:AccessControlList|GroupUUID|OwnerUUID)|reate(?:Copy)?)|Get(?:Group|Mode|Owner|TypeID)|Set(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)))|Get(?:Allocator|RetainCount|TypeID)|Hash|Locale(?:C(?:opy(?:AvailableLocaleIdentifiers|C(?:ommonISOCurrencyCodes|urrent)|DisplayNameForPropertyValue|ISO(?:C(?:ountryCodes|urrencyCodes)|LanguageCodes)|PreferredLanguages)|reate(?:C(?:anonicalL(?:anguageIdentifierFromString|ocaleIdentifierFromS(?:criptManagerCodes|tring))|o(?:mponentsFromLocaleIdentifier|py))|LocaleIdentifierFrom(?:Components|WindowsLocaleCode))?)|Get(?:Identifier|Language(?:CharacterDirection|LineDirection)|System|TypeID|Value|WindowsLocaleCodeFromLocaleIdentifier))|M(?:a(?:chPort(?:Create(?:RunLoopSource|WithPort)?|Get(?:Context|InvalidationCallBack|Port|TypeID)|I(?:nvalidate|sValid)|SetInvalidationCallBack)|keCollectable)|essagePort(?:Create(?:Local|R(?:emote|unLoopSource))|Get(?:Context|InvalidationCallBack|Name|TypeID)|I(?:nvalidate|s(?:Remote|Valid))|Se(?:ndRequest|t(?:DispatchQueue|InvalidationCallBack|Name))))|N(?:otificationCenter(?:AddObserver|Get(?:D(?:arwinNotifyCenter|istributedCenter)|LocalCenter|TypeID)|PostNotification(?:WithOptions)?|Remove(?:EveryObserver|Observer))|u(?:llGetTypeID|mber(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:NumberFromString|StringWith(?:Number|Value))?)|Get(?:DecimalInfoForCurrencyCode|Format|Locale|Style|TypeID|ValueFromString)|Set(?:Format|Property))|Get(?:ByteSize|Type(?:ID)?|Value)|IsFloatType)))|P(?:lugIn(?:AddInstanceForFactory|Create|FindFactoriesForPlugInType(?:InPlugIn)?|Get(?:Bundle|TypeID)|I(?:nstance(?:Create(?:WithInstanceDataSize)?|Get(?:FactoryName|In(?:stanceData|terfaceFunctionTable)|TypeID))|sLoadOnDemand)|Re(?:gister(?:FactoryFunction(?:ByName)?|PlugInType)|moveInstanceForFactory)|SetLoadOnDemand|Unregister(?:Factory|PlugInType))|r(?:eferences(?:A(?:ddSuitePreferencesToApp|pp(?:Synchronize|ValueIsForced))|Copy(?:AppValue|KeyList|Multiple|Value)|GetApp(?:BooleanValue|IntegerValue)|RemoveSuitePreferencesFromApp|S(?:et(?:AppValue|Multiple|Value)|ynchronize))|opertyList(?:Create(?:D(?:ata|eepCopy)|With(?:Data|Stream))|IsValid|Write)))|R(?:angeMake|e(?:adStream(?:C(?:lose|opy(?:Error|Property)|reateWith(?:BytesNoCopy|File))|Get(?:Buffer|Error|Status|TypeID)|HasBytesAvailable|Open|Read|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop)|lease|tain)|unLoop(?:Add(?:CommonMode|Observer|Source|Timer)|Co(?:ntains(?:Observer|Source|Timer)|py(?:AllModes|CurrentMode))|Get(?:Current|Main|NextTimerFireDate|TypeID)|IsWaiting|Observer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Activities|Context|Order|TypeID)|I(?:nvalidate|sValid))|PerformBlock|R(?:emove(?:Observer|Source|Timer)|un(?:InMode)?)|S(?:ource(?:Create|Get(?:Context|Order|TypeID)|I(?:nvalidate|sValid)|Signal)|top)|Timer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Context|Interval|NextFireDate|Order|TypeID)|I(?:nvalidate|sValid)|SetNextFireDate)|WakeUp))|S(?:et(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|how(?:Str)?|ocket(?:C(?:o(?:nnectToAddress|py(?:Address|PeerAddress|Registered(?:SocketSignature|Value)))|reate(?:ConnectedToSocketSignature|RunLoopSource|With(?:Native|SocketSignature))?)|DisableCallBacks|EnableCallBacks|Get(?:Context|DefaultNameRegistryPortNumber|Native|SocketFlags|TypeID)|I(?:nvalidate|sValid)|Register(?:SocketSignature|Value)|Se(?:ndData|t(?:Address|DefaultNameRegistryPortNumber|SocketFlags))|Unregister)|tr(?:eamCreate(?:BoundPair|PairWith(?:PeerSocketSignature|Socket(?:ToHost)?))|ing(?:Append(?:C(?:String|haracters)|Format(?:AndArguments)?|PascalString)?|C(?:apitalize|o(?:mpare(?:WithOptions(?:AndLocale)?)?|nvert(?:EncodingTo(?:IANACharSetName|NSStringEncoding|WindowsCodepage)|IANACharSetNameToEncoding|NSStringEncodingToEncoding|WindowsCodepageToEncoding))|reate(?:Array(?:BySeparatingStrings|WithFindResults)|ByCombiningStrings|Copy|ExternalRepresentation|FromExternalRepresentation|Mutable(?:Copy|WithExternalCharactersNoCopy)?|With(?:Bytes(?:NoCopy)?|C(?:String(?:NoCopy)?|haracters(?:NoCopy)?)|F(?:ileSystemRepresentation|ormat(?:AndArguments)?)|PascalString(?:NoCopy)?|Substring)))|Delete|F(?:ind(?:AndReplace|CharacterFromSet|WithOptions(?:AndLocale)?)?|old)|Get(?:Bytes|C(?:String(?:Ptr)?|haracter(?:AtIndex|FromInlineBuffer|s(?:Ptr)?))|DoubleValue|F(?:astestEncoding|ileSystemRepresentation)|HyphenationLocationBeforeIndex|IntValue|L(?:ength|i(?:neBounds|stOfAvailableEncodings)|ongCharacterForSurrogatePair)|M(?:aximumSize(?:ForEncoding|OfFileSystemRepresentation)|ostCompatibleMacStringEncoding)|NameOfEncoding|Pa(?:ragraphBounds|scalString(?:Ptr)?)|RangeOfComposedCharactersAtIndex|S(?:mallestEncoding|urrogatePairForLongCharacter|ystemEncoding)|TypeID)|Has(?:Prefix|Suffix)|I(?:n(?:itInlineBuffer|sert)|s(?:EncodingAvailable|HyphenationAvailableForLocale|Surrogate(?:HighCharacter|LowCharacter)))|Lowercase|Normalize|Pad|Replace(?:All)?|SetExternalCharactersNoCopy|T(?:okenizer(?:AdvanceToNextToken|C(?:opy(?:BestStringLanguage|CurrentTokenAttribute)|reate)|G(?:et(?:Current(?:SubTokens|TokenRange)|TypeID)|oToTokenAtIndex)|SetString)|r(?:ansform|im(?:Whitespace)?))|Uppercase))|wapInt(?:16(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|32(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|64(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?))|T(?:imeZone(?:C(?:opy(?:Abbreviation(?:Dictionary)?|Default|KnownNames|LocalizedName|System)|reate(?:With(?:Name|TimeIntervalFromGMT))?)|Get(?:Da(?:ta|ylightSavingTimeOffset)|N(?:ame|extDaylightSavingTimeTransition)|SecondsFromGMT|TypeID)|IsDaylightSavingTime|ResetSystem|Set(?:AbbreviationDictionary|Default))|ree(?:App(?:endChild|lyFunctionToChildren)|Create|FindRoot|Get(?:C(?:hild(?:AtIndex|Count|ren)|ontext)|FirstChild|NextSibling|Parent|TypeID)|InsertSibling|PrependChild|Remove(?:AllChildren)?|S(?:etContext|ortChildren)))|U(?:RL(?:C(?:anBeDecomposed|learResourcePropertyCache(?:ForKey)?|opy(?:AbsoluteURL|F(?:ileSystemPath|ragment)|HostName|LastPathComponent|NetLocation|Pa(?:rameterString|ssword|th(?:Extension)?)|QueryString|Resource(?:Propert(?:iesForKeys|yForKey)|Specifier)|S(?:cheme|trictPath)|UserName)|reate(?:AbsoluteURLWithBytes|B(?:ookmarkData(?:From(?:AliasRecord|File))?|yResolvingBookmarkData)|Copy(?:AppendingPath(?:Component|Extension)|Deleting(?:LastPathComponent|PathExtension))|Data|F(?:ile(?:PathURL|ReferenceURL)|romFileSystemRepresentation(?:RelativeToBase)?)|ResourcePropert(?:iesForKeysFromBookmarkData|yForKeyFromBookmarkData)|StringByReplacingPercentEscapes|With(?:Bytes|FileSystemPath(?:RelativeToBase)?|String)))|Enumerator(?:CreateFor(?:DirectoryURL|MountedVolumes)|Get(?:DescendentLevel|NextURL|TypeID)|SkipDescendents)|Get(?:B(?:aseURL|yte(?:RangeForComponent|s))|FileSystemRepresentation|PortNumber|String|TypeID)|HasDirectoryPath|ResourceIsReachable|S(?:et(?:ResourcePropert(?:iesForKeys|yForKey)|TemporaryResourcePropertyForKey)|t(?:artAccessingSecurityScopedResource|opAccessingSecurityScopedResource))|WriteBookmarkDataToFile)|UID(?:Create(?:From(?:String|UUIDBytes)|String|WithBytes)?|Get(?:ConstantUUIDWithBytes|TypeID|UUIDBytes))|serNotification(?:C(?:ancel|heckBoxChecked|reate(?:RunLoopSource)?)|Display(?:Alert|Notice)|Get(?:Response(?:Dictionary|Value)|TypeID)|PopUpSelection|ReceiveResponse|SecureTextField|Update))|WriteStream(?:C(?:anAcceptBytes|lose|opy(?:Error|Property)|reateWith(?:AllocatedBuffers|Buffer|File))|Get(?:Error|Status|TypeID)|Open|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop|Write)|XMLCreateStringBy(?:EscapingEntities|UnescapingEntities))\\b)" + "match": "(\\s*)(\\bCF(?:A(?:bsoluteTimeGetCurrent|llocator(?:Allocate|Create|Deallocate|Get(?:Context|Default|PreferredSizeForSize|TypeID)|Reallocate|SetDefault)|rray(?:App(?:end(?:Array|Value)|lyFunction)|BSearchValues|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|ExchangeValuesAtIndices|Get(?:Count(?:OfValue)?|FirstIndexOfValue|LastIndexOfValue|TypeID|Value(?:AtIndex|s))|InsertValueAtIndex|Re(?:move(?:AllValues|ValueAtIndex)|placeValues)|S(?:etValueAtIndex|ortValues))|ttributedString(?:BeginEditing|Create(?:Copy|Mutable(?:Copy)?|WithSubstring)?|EndEditing|Get(?:Attribute(?:AndLongestEffectiveRange|s(?:AndLongestEffectiveRange)?)?|Length|MutableString|String|TypeID)|Re(?:moveAttribute|place(?:AttributedString|String))|SetAttribute(?:s)?))|B(?:ag(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|i(?:naryHeap(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy)?)|Get(?:Count(?:OfValue)?|Minimum(?:IfPresent)?|TypeID|Values)|Remove(?:AllValues|MinimumValue))|tVector(?:C(?:ontainsBit|reate(?:Copy|Mutable(?:Copy)?)?)|FlipBit(?:AtIndex|s)|Get(?:Bit(?:AtIndex|s)|Count(?:OfBit)?|FirstIndexOfBit|LastIndexOfBit|TypeID)|Set(?:AllBits|Bit(?:AtIndex|s)|Count)))|ooleanGet(?:TypeID|Value)|undle(?:C(?:opy(?:AuxiliaryExecutableURL|Bu(?:iltInPlugInsURL|ndle(?:Localizations|URL))|Executable(?:Architectures(?:ForURL)?|URL)|InfoDictionary(?:ForURL|InDirectory)|Localiz(?:ationsFor(?:Preferences|URL)|edString)|Pr(?:eferredLocalizationsFromArray|ivateFrameworksURL)|Resource(?:URL(?:ForLocalization|InDirectory|sOfType(?:ForLocalization|InDirectory)?)?|sDirectoryURL)|S(?:hared(?:FrameworksURL|SupportURL)|upportFilesDirectoryURL))|reate(?:BundlesFromDirectory)?)|Get(?:AllBundles|BundleWithIdentifier|D(?:ataPointer(?:ForName|sForNames)|evelopmentRegion)|FunctionPointer(?:ForName|sForNames)|I(?:dentifier|nfoDictionary)|LocalInfoDictionary|MainBundle|P(?:ackageInfo(?:InDirectory)?|lugIn)|TypeID|V(?:alueForInfoDictionaryKey|ersionNumber))|IsExecutableLoaded|LoadExecutable(?:AndReturnError)?|PreflightExecutable|UnloadExecutable)|yteOrderGetCurrent)|C(?:alendar(?:AddComponents|C(?:o(?:mposeAbsoluteTime|py(?:Current|Locale|TimeZone))|reateWithIdentifier)|DecomposeAbsoluteTime|Get(?:ComponentDifference|FirstWeekday|Identifier|M(?:aximumRangeOfUnit|inimum(?:DaysInFirstWeek|RangeOfUnit))|OrdinalityOfUnit|RangeOfUnit|T(?:imeRangeOfUnit|ypeID))|Set(?:FirstWeekday|Locale|MinimumDaysInFirstWeek|TimeZone))|haracterSet(?:AddCharactersIn(?:Range|String)|Create(?:BitmapRepresentation|Copy|InvertedSet|Mutable(?:Copy)?|With(?:BitmapRepresentation|CharactersIn(?:Range|String)))|Get(?:Predefined|TypeID)|HasMemberInPlane|I(?:n(?:tersect|vert)|s(?:CharacterMember|LongCharacterMember|SupersetOfSet))|RemoveCharactersIn(?:Range|String)|Union)|o(?:nvert(?:Double(?:HostToSwapped|SwappedToHost)|Float(?:32(?:HostToSwapped|SwappedToHost)|64(?:HostToSwapped|SwappedToHost)|HostToSwapped|SwappedToHost))|py(?:Description|HomeDirectoryURL|TypeIDDescription)))|D(?:at(?:a(?:AppendBytes|Create(?:Copy|Mutable(?:Copy)?|WithBytesNoCopy)?|DeleteBytes|Find|Get(?:Byte(?:Ptr|s)|Length|MutableBytePtr|TypeID)|IncreaseLength|ReplaceBytes|SetLength)|e(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:DateF(?:ormatFromTemplate|romString)|StringWith(?:AbsoluteTime|Date))?)|Get(?:AbsoluteTimeFromString|DateStyle|Format|Locale|T(?:imeStyle|ypeID))|Set(?:Format|Property))|Get(?:AbsoluteTime|T(?:imeIntervalSinceDate|ypeID))))|ictionary(?:A(?:ddValue|pplyFunction)|C(?:ontains(?:Key|Value)|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:Of(?:Key|Value))?|KeysAndValues|TypeID|Value(?:IfPresent)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue))|E(?:qual|rror(?:C(?:opy(?:Description|FailureReason|RecoverySuggestion|UserInfo)|reate(?:WithUserInfoKeysAndValues)?)|Get(?:Code|Domain|TypeID)))|File(?:Descriptor(?:Create(?:RunLoopSource)?|DisableCallBacks|EnableCallBacks|Get(?:Context|NativeDescriptor|TypeID)|I(?:nvalidate|sValid))|Security(?:C(?:opy(?:AccessControlList|GroupUUID|OwnerUUID)|reate(?:Copy)?)|Get(?:Group|Mode|Owner|TypeID)|Set(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)))|Get(?:Allocator|RetainCount|TypeID)|Hash|Locale(?:C(?:opy(?:AvailableLocaleIdentifiers|C(?:ommonISOCurrencyCodes|urrent)|DisplayNameForPropertyValue|ISO(?:C(?:ountryCodes|urrencyCodes)|LanguageCodes)|PreferredLanguages)|reate(?:C(?:anonicalL(?:anguageIdentifierFromString|ocaleIdentifierFromS(?:criptManagerCodes|tring))|o(?:mponentsFromLocaleIdentifier|py))|LocaleIdentifierFrom(?:Components|WindowsLocaleCode))?)|Get(?:Identifier|Language(?:CharacterDirection|LineDirection)|System|TypeID|Value|WindowsLocaleCodeFromLocaleIdentifier))|M(?:a(?:chPort(?:Create(?:RunLoopSource|WithPort)?|Get(?:Context|InvalidationCallBack|Port|TypeID)|I(?:nvalidate|sValid)|SetInvalidationCallBack)|keCollectable)|essagePort(?:Create(?:Local|R(?:emote|unLoopSource))|Get(?:Context|InvalidationCallBack|Name|TypeID)|I(?:nvalidate|s(?:Remote|Valid))|Se(?:ndRequest|t(?:DispatchQueue|InvalidationCallBack|Name))))|N(?:otificationCenter(?:AddObserver|Get(?:D(?:arwinNotifyCenter|istributedCenter)|LocalCenter|TypeID)|PostNotification(?:WithOptions)?|Remove(?:EveryObserver|Observer))|u(?:llGetTypeID|mber(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:NumberFromString|StringWith(?:Number|Value))?)|Get(?:DecimalInfoForCurrencyCode|Format|Locale|Style|TypeID|ValueFromString)|Set(?:Format|Property))|Get(?:ByteSize|Type(?:ID)?|Value)|IsFloatType)))|P(?:lugIn(?:AddInstanceForFactory|Create|FindFactoriesForPlugInType(?:InPlugIn)?|Get(?:Bundle|TypeID)|I(?:nstance(?:Create(?:WithInstanceDataSize)?|Get(?:FactoryName|In(?:stanceData|terfaceFunctionTable)|TypeID))|sLoadOnDemand)|Re(?:gister(?:FactoryFunction(?:ByName)?|PlugInType)|moveInstanceForFactory)|SetLoadOnDemand|Unregister(?:Factory|PlugInType))|r(?:eferences(?:A(?:ddSuitePreferencesToApp|pp(?:Synchronize|ValueIsForced))|Copy(?:AppValue|KeyList|Multiple|Value)|GetApp(?:BooleanValue|IntegerValue)|RemoveSuitePreferencesFromApp|S(?:et(?:AppValue|Multiple|Value)|ynchronize))|opertyList(?:Create(?:D(?:ata|eepCopy)|With(?:Data|Stream))|IsValid|Write)))|R(?:angeMake|e(?:adStream(?:C(?:lose|opy(?:Error|Property)|reateWith(?:BytesNoCopy|File))|Get(?:Buffer|Error|Status|TypeID)|HasBytesAvailable|Open|Read|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop)|lease|tain)|unLoop(?:Add(?:CommonMode|Observer|Source|Timer)|Co(?:ntains(?:Observer|Source|Timer)|py(?:AllModes|CurrentMode))|Get(?:Current|Main|NextTimerFireDate|TypeID)|IsWaiting|Observer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Activities|Context|Order|TypeID)|I(?:nvalidate|sValid))|PerformBlock|R(?:emove(?:Observer|Source|Timer)|un(?:InMode)?)|S(?:ource(?:Create|Get(?:Context|Order|TypeID)|I(?:nvalidate|sValid)|Signal)|top)|Timer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Context|Interval|NextFireDate|Order|TypeID)|I(?:nvalidate|sValid)|SetNextFireDate)|WakeUp))|S(?:et(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|how(?:Str)?|ocket(?:C(?:o(?:nnectToAddress|py(?:Address|PeerAddress|Registered(?:SocketSignature|Value)))|reate(?:ConnectedToSocketSignature|RunLoopSource|With(?:Native|SocketSignature))?)|DisableCallBacks|EnableCallBacks|Get(?:Context|DefaultNameRegistryPortNumber|Native|SocketFlags|TypeID)|I(?:nvalidate|sValid)|Register(?:SocketSignature|Value)|Se(?:ndData|t(?:Address|DefaultNameRegistryPortNumber|SocketFlags))|Unregister)|tr(?:eamCreate(?:BoundPair|PairWith(?:PeerSocketSignature|Socket(?:ToHost)?))|ing(?:Append(?:C(?:String|haracters)|Format(?:AndArguments)?|PascalString)?|C(?:apitalize|o(?:mpare(?:WithOptions(?:AndLocale)?)?|nvert(?:EncodingTo(?:IANACharSetName|NSStringEncoding|WindowsCodepage)|IANACharSetNameToEncoding|NSStringEncodingToEncoding|WindowsCodepageToEncoding))|reate(?:Array(?:BySeparatingStrings|WithFindResults)|ByCombiningStrings|Copy|ExternalRepresentation|FromExternalRepresentation|Mutable(?:Copy|WithExternalCharactersNoCopy)?|With(?:Bytes(?:NoCopy)?|C(?:String(?:NoCopy)?|haracters(?:NoCopy)?)|F(?:ileSystemRepresentation|ormat(?:AndArguments)?)|PascalString(?:NoCopy)?|Substring)))|Delete|F(?:ind(?:AndReplace|CharacterFromSet|WithOptions(?:AndLocale)?)?|old)|Get(?:Bytes|C(?:String(?:Ptr)?|haracter(?:AtIndex|FromInlineBuffer|s(?:Ptr)?))|DoubleValue|F(?:astestEncoding|ileSystemRepresentation)|HyphenationLocationBeforeIndex|IntValue|L(?:ength|i(?:neBounds|stOfAvailableEncodings)|ongCharacterForSurrogatePair)|M(?:aximumSize(?:ForEncoding|OfFileSystemRepresentation)|ostCompatibleMacStringEncoding)|NameOfEncoding|Pa(?:ragraphBounds|scalString(?:Ptr)?)|RangeOfComposedCharactersAtIndex|S(?:mallestEncoding|urrogatePairForLongCharacter|ystemEncoding)|TypeID)|Has(?:Prefix|Suffix)|I(?:n(?:itInlineBuffer|sert)|s(?:EncodingAvailable|HyphenationAvailableForLocale|Surrogate(?:HighCharacter|LowCharacter)))|Lowercase|Normalize|Pad|Replace(?:All)?|SetExternalCharactersNoCopy|T(?:okenizer(?:AdvanceToNextToken|C(?:opy(?:BestStringLanguage|CurrentTokenAttribute)|reate)|G(?:et(?:Current(?:SubTokens|TokenRange)|TypeID)|oToTokenAtIndex)|SetString)|r(?:ansform|im(?:Whitespace)?))|Uppercase))|wapInt(?:16(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|32(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|64(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?))|T(?:imeZone(?:C(?:opy(?:Abbreviation(?:Dictionary)?|Default|KnownNames|LocalizedName|System)|reate(?:With(?:Name|TimeIntervalFromGMT))?)|Get(?:Da(?:ta|ylightSavingTimeOffset)|N(?:ame|extDaylightSavingTimeTransition)|SecondsFromGMT|TypeID)|IsDaylightSavingTime|ResetSystem|Set(?:AbbreviationDictionary|Default))|ree(?:App(?:endChild|lyFunctionToChildren)|Create|FindRoot|Get(?:C(?:hild(?:AtIndex|Count|ren)|ontext)|FirstChild|NextSibling|Parent|TypeID)|InsertSibling|PrependChild|Remove(?:AllChildren)?|S(?:etContext|ortChildren)))|U(?:RL(?:C(?:anBeDecomposed|learResourcePropertyCache(?:ForKey)?|opy(?:AbsoluteURL|F(?:ileSystemPath|ragment)|HostName|LastPathComponent|NetLocation|Pa(?:ssword|th(?:Extension)?)|QueryString|Resource(?:Propert(?:iesForKeys|yForKey)|Specifier)|S(?:cheme|trictPath)|UserName)|reate(?:AbsoluteURLWithBytes|B(?:ookmarkData(?:From(?:AliasRecord|File))?|yResolvingBookmarkData)|Copy(?:AppendingPath(?:Component|Extension)|Deleting(?:LastPathComponent|PathExtension))|Data|F(?:ile(?:PathURL|ReferenceURL)|romFileSystemRepresentation(?:RelativeToBase)?)|ResourcePropert(?:iesForKeysFromBookmarkData|yForKeyFromBookmarkData)|StringByReplacingPercentEscapes|With(?:Bytes|FileSystemPath(?:RelativeToBase)?|String)))|Enumerator(?:CreateFor(?:DirectoryURL|MountedVolumes)|Get(?:DescendentLevel|NextURL|TypeID)|SkipDescendents)|Get(?:B(?:aseURL|yte(?:RangeForComponent|s))|FileSystemRepresentation|PortNumber|String|TypeID)|HasDirectoryPath|ResourceIsReachable|S(?:et(?:ResourcePropert(?:iesForKeys|yForKey)|TemporaryResourcePropertyForKey)|t(?:artAccessingSecurityScopedResource|opAccessingSecurityScopedResource))|WriteBookmarkDataToFile)|UID(?:Create(?:From(?:String|UUIDBytes)|String|WithBytes)?|Get(?:ConstantUUIDWithBytes|TypeID|UUIDBytes))|serNotification(?:C(?:ancel|heckBoxChecked|reate(?:RunLoopSource)?)|Display(?:Alert|Notice)|Get(?:Response(?:Dictionary|Value)|TypeID)|PopUpSelection|ReceiveResponse|SecureTextField|Update))|WriteStream(?:C(?:anAcceptBytes|lose|opy(?:Error|Property)|reateWith(?:AllocatedBuffers|Buffer|File))|Get(?:Error|Status|TypeID)|Open|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop|Write)|XMLCreateStringBy(?:EscapingEntities|UnescapingEntities))\\b)" }, { "captures": { @@ -400,6 +922,28 @@ }, "match": "(\\s*)(\\b(?:clock_(?:get(?:res|time(?:_nsec_np)?)|settime)|mk(?:ostemp(?:s)?|pathat_np)|rename(?:atx_np|x_np)|timingsafe_bcmp)\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.clib.10.13.c" + } + }, + "match": "(\\s*)(\\b(?:fmemopen|mk(?:dtempat_np|ostempsat_np|stempsat_np)|open_memstream|ptsname_r|setattrlistat)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.clib.10.15.c" + } + }, + "match": "(\\s*)(\\b(?:rpmatch|timespec_get)\\b)" + }, { "captures": { "1": { @@ -420,7 +964,7 @@ "name": "support.function.clib.10.9.c" } }, - "match": "(\\s*)(\\bf(?:fsll|lsll)\\b)" + "match": "(\\s*)(\\b(?:f(?:fsll|lsll)|memset_s)\\b)" }, { "captures": { @@ -431,7 +975,7 @@ "name": "support.function.clib.c" } }, - "match": "(\\s*)(\\b(?:a(?:64l|b(?:ort|s)|c(?:c(?:ess(?:x_np)?|t)|os(?:f|h(?:f|l)?|l)?)|dd_profil|l(?:arm|loca)|rc4random(?:_(?:addrandom|buf|stir|uniform))?|s(?:ctime(?:_r)?|in(?:f|h(?:f|l)?|l)?|printf)|t(?:an(?:2(?:f|l)?|f|h(?:f|l)?|l)?|exit(?:_b)?|o(?:f|i|l(?:l)?)))|b(?:c(?:mp|opy)|rk|s(?:d_signal|earch(?:_b)?)|zero)|c(?:alloc|brt(?:f|l)?|eil(?:f|l)?|get(?:c(?:ap|lose)|ent|first|match|n(?:ext|um)|s(?:et|tr)|ustr)|h(?:dir|own|root)|l(?:earerr|o(?:ck|se))|o(?:nfstr|pysign(?:f|l)?|s(?:f|h(?:f|l)?|l)?)|r(?:eat|ypt)|t(?:ermid(?:_r)?|ime(?:_r)?))|d(?:evname(?:_r)?|i(?:fftime|gittoint|spatch_(?:time|walltime)|v)|printf|rand48|up(?:2)?)|e(?:cvt|n(?:crypt|dusershell)|r(?:and48|f(?:c(?:f|l)?|f|l)?)|x(?:changedata|ec(?:l(?:e|p)?|v(?:P|e|p)?)|it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|f(?:abs(?:f|l)?|c(?:h(?:dir|own)|lose|ntl|vt)|d(?:im(?:f|l)?|open)|e(?:of|rror)|f(?:l(?:agstostr|ush)|s(?:ctl|l)?)|get(?:attrlist|c|ln|pos|s)|ile(?:no|sec_(?:dup|free|get_property|init|query_property|set_property|unset_property))|l(?:o(?:ck(?:file)?|or(?:f|l)?)|s(?:l)?)|m(?:a(?:f|l|x(?:f|l)?)?|in(?:f|l)?|od(?:f|l)?|tcheck)|o(?:pen|rk)|p(?:athconf|rintf|u(?:rge|t(?:c|s)))|re(?:ad|e|open|xp(?:f|l)?)|s(?:c(?:anf|tl)|e(?:ek(?:o)?|t(?:attrlist|pos))|ync)|t(?:ell(?:o)?|r(?:uncate|ylockfile))|un(?:lockfile|open)|write)|g(?:cvt|et(?:attrlist|bsize|c(?:_unlocked|har(?:_unlocked)?|wd)?|d(?:ate|elim|irentriesattr|omainname|tablesize)|e(?:gid|nv|uid)|g(?:id|roup(?:list|s))|host(?:id|name)|iopolicy_np|l(?:ine|o(?:adavg|gin(?:_r)?))|mode|opt|p(?:a(?:gesize|ss)|eereid|g(?:id|rp)|id|pid|r(?:iority|ogname))|r(?:limit|usage)|s(?:groups_np|id|ubopt)?|u(?:id|sershell)|w(?:d|groups_np)?)|mtime(?:_r)?|rantpt)|h(?:eapsort(?:_b)?|ypot(?:f|l)?)|i(?:logb(?:f|l)?|n(?:dex|it(?:groups|state))|ruserok(?:_sa)?|s(?:a(?:l(?:num|pha)|scii|tty)|blank|cntrl|digit|graph|hexnumber|ideogram|lower|number|p(?:honogram|rint|unct)|rune|s(?:etugid|p(?:ace|ecial))|upper|xdigit))|j(?:0|1|n|rand48)|kill(?:pg)?|l(?:64a|abs|c(?:hown|ong48)|d(?:exp(?:f|l)?|iv)|gamma(?:f|l)?|ink|l(?:abs|div|r(?:int(?:f|l)?|ound(?:f|l)?))|o(?:c(?:al(?:econv|time(?:_r)?)|kf)|g(?:1(?:0(?:f|l)?|p(?:f|l)?)|2(?:f|l)?|b(?:f|l)?|f|l)?|ngjmp(?:error)?)|r(?:and48|int(?:f|l)?|ound(?:f|l)?)|seek)|m(?:a(?:jor|kedev|lloc)|b(?:len|stowcs|towc)|e(?:m(?:c(?:cpy|hr|mp|py)|m(?:em|ove)|set(?:_pattern(?:16|4|8))?)|rgesort(?:_b)?)|inor|k(?:dtemp|nod|stemp(?:_dprotected_np|s)?|t(?:emp|ime))|odf(?:f|l)?|rand48)|n(?:an(?:f|l|osleep)?|e(?:arbyint(?:f|l)?|xt(?:after(?:f|l)?|toward(?:f|l)?))|fssvc|ice|rand48)|open(?:_dprotected_np|x_np)?|p(?:a(?:thconf|use)|close|error|ipe|o(?:pen|six(?:2time|_(?:memalign|openpt))|w(?:f|l)?)|r(?:ead|intf|ofil)|s(?:elect|ignal|ort(?:_(?:b|r))?)|t(?:hread_(?:getugid_np|kill|s(?:etugid_np|igmask))|sname)|ut(?:c(?:_unlocked|har(?:_unlocked)?)?|env|s|w)|write)|qsort(?:_(?:b|r))?|r(?:a(?:dixsort|ise|nd(?:_r|om)?)|cmd(?:_af)?|e(?:a(?:d(?:link)?|l(?:loc(?:f)?|path))|boot|m(?:ainder(?:f|l)?|ove|quo(?:f|l)?)|name|voke|wind)|in(?:dex|t(?:f|l)?)|mdir|ound(?:f|l)?|resvport(?:_af)?|userok)|s(?:brk|ca(?:lb(?:ln(?:f|l)?|n(?:f|l)?)?|nf)|e(?:archfs|ed48|lect|t(?:attrlist|buf(?:fer)?|domainname|e(?:gid|nv|uid)|g(?:id|roups)|host(?:id|name)|iopolicy_np|jmp|key|l(?:inebuf|o(?:cale|gin))|mode|p(?:g(?:id|rp)|r(?:iority|ogname))|r(?:e(?:gid|uid)|gid|limit|uid)|s(?:groups_np|id|tate)|u(?:id|sershell)|vbuf|wgroups_np))|i(?:g(?:a(?:ction|ddset|ltstack)|block|delset|emptyset|fillset|hold|i(?:gnore|nterrupt|smember)|longjmp|nal|p(?:ause|ending|rocmask)|relse|s(?:et(?:jmp|mask)?|uspend)|vec|wait)|n(?:f|h(?:f|l)?|l)?)|leep|nprintf|printf|qrt(?:f|l)?|ra(?:dixsort|nd(?:48|dev|om(?:dev)?)?)|scanf|t(?:p(?:cpy|ncpy)|r(?:c(?:a(?:se(?:cmp|str)|t)|hr|mp|oll|py|spn)|dup|error(?:_r)?|ftime|l(?:c(?:at|py)|en)|mode|n(?:c(?:a(?:secmp|t)|mp|py)|dup|len|str)|p(?:brk|time)|rchr|s(?:ep|ignal|pn|tr)|to(?:d|f(?:flags)?|k(?:_r)?|l(?:d|l)?|q|u(?:l(?:l)?|q))|xfrm))|wa(?:b|pon)|y(?:mlink|nc|s(?:conf|tem)))|t(?:an(?:f|h(?:f|l)?|l)?|c(?:getpgrp|setpgrp)|empnam|gamma(?:f|l)?|ime(?:2posix|gm|local)?|mp(?:file|nam)|o(?:ascii|lower|upper)|runc(?:ate|f|l)?|ty(?:name(?:_r)?|slot)|zset(?:wall)?)|u(?:alarm|n(?:delete|getc|l(?:ink|ockpt)|setenv|whiteout)|sleep)|v(?:a(?:lloc|sprintf)|dprintf|f(?:ork|printf|scanf)|printf|s(?:canf|nprintf|printf|scanf))|w(?:ait(?:3|4|id|pid)?|c(?:stombs|tomb)|rite)|y(?:0|1|n)|zopen)\\b)" + "match": "(\\s*)(\\b(?:a(?:64l|b(?:ort|s)|c(?:c(?:ess(?:x_np)?|t)|os(?:f|h(?:f|l)?|l)?)|d(?:d_profil|jtime)|l(?:arm|loca)|rc4random(?:_(?:buf|stir|uniform))?|s(?:ctime(?:_r)?|in(?:f|h(?:f|l)?|l)?|printf)|t(?:an(?:2(?:f|l)?|f|h(?:f|l)?|l)?|exit(?:_b)?|o(?:f|i|l(?:l)?)))|b(?:c(?:mp|opy)|rk|s(?:d_signal|earch(?:_b)?)|zero)|c(?:brt(?:f|l)?|eil(?:f|l)?|get(?:c(?:ap|lose)|ent|first|match|n(?:ext|um)|s(?:et|tr)|ustr)|h(?:dir|own|root)|l(?:earerr|o(?:ck|se))|o(?:nfstr|pysign(?:f|l)?|s(?:f|h(?:f|l)?|l)?)|r(?:eat|ypt)|t(?:ermid_r|ime(?:_r)?))|d(?:evname(?:_r)?|i(?:fftime|spatch_(?:time|walltime)|v)|printf|rand48|up(?:2)?)|e(?:cvt|n(?:crypt|dusershell)|r(?:and48|f(?:c(?:f|l)?|f|l)?)|x(?:changedata|ec(?:l(?:e|p)?|v(?:P|e|p)?)|it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|f(?:abs(?:f|l)?|c(?:h(?:dir|own)|lose|ntl|vt)|d(?:im(?:f|l)?|open)|e(?:of|rror)|f(?:l(?:agstostr|ush)|s(?:ctl|l)?)|get(?:attrlist|c|ln|pos|s)|ile(?:no|sec_(?:dup|free|get_property|init|query_property|set_property|unset_property))|l(?:o(?:ck(?:file)?|or(?:f|l)?)|s(?:l)?)|m(?:a(?:f|l|x(?:f|l)?)?|in(?:f|l)?|od(?:f|l)?|tcheck)|o(?:pen|rk)|p(?:athconf|rintf|u(?:rge|t(?:c|s)))|re(?:ad|open|xp(?:f|l)?)|s(?:c(?:anf|tl)|e(?:ek(?:o)?|t(?:attrlist|pos))|ync)|t(?:ell(?:o)?|r(?:uncate|ylockfile))|u(?:n(?:lockfile|open)|times)|write)|g(?:cvt|et(?:attrlist|bsize|c(?:_unlocked|har(?:_unlocked)?|wd)?|d(?:ate|elim|irentriesattr|omainname|tablesize)|e(?:gid|nv|uid)|g(?:id|roup(?:list|s))|host(?:id|name)|i(?:opolicy_np|timer)|l(?:ine|o(?:adavg|gin(?:_r)?))|mode|opt|p(?:a(?:gesize|ss)|eereid|g(?:id|rp)|id|pid|r(?:iority|ogname))|r(?:limit|usage)|s(?:groups_np|id|ubopt)?|timeofday|u(?:id|sershell)|w(?:d|groups_np)?)|mtime(?:_r)?|rantpt)|h(?:eapsort(?:_b)?|ypot(?:f|l)?)|i(?:logb(?:f|l)?|n(?:dex|it(?:groups|state))|ruserok(?:_sa)?|s(?:atty|setugid))|j(?:0|1|n|rand48)|kill(?:pg)?|l(?:64a|abs|c(?:hown|ong48)|d(?:exp(?:f|l)?|iv)|gamma(?:f|l)?|ink|l(?:abs|div|r(?:int(?:f|l)?|ound(?:f|l)?))|o(?:c(?:al(?:econv|time(?:_r)?)|kf)|g(?:1(?:0(?:f|l)?|p(?:f|l)?)|2(?:f|l)?|b(?:f|l)?|f|l)?|ngjmp(?:error)?)|r(?:and48|int(?:f|l)?|ound(?:f|l)?)|seek|utimes)|m(?:b(?:len|stowcs|towc)|e(?:m(?:c(?:cpy|hr|mp|py)|m(?:em|ove)|set(?:_pattern(?:16|4|8))?)|rgesort(?:_b)?)|k(?:dtemp|nod|stemp(?:_dprotected_np|s)?|t(?:emp|ime))|odf(?:f|l)?|rand48)|n(?:an(?:f|l|osleep)?|e(?:arbyint(?:f|l)?|xt(?:after(?:f|l)?|toward(?:f|l)?))|fssvc|ice|rand48)|open(?:_dprotected_np|x_np)?|p(?:a(?:thconf|use)|close|error|ipe|o(?:pen|six(?:2time|_openpt)|w(?:f|l)?)|r(?:ead|intf|ofil)|s(?:elect|ignal|ort(?:_(?:b|r))?)|t(?:hread_(?:getugid_np|kill|s(?:etugid_np|igmask))|sname)|ut(?:c(?:_unlocked|har(?:_unlocked)?)?|env|s|w)|write)|qsort(?:_(?:b|r))?|r(?:a(?:dixsort|ise|nd(?:_r|om)?)|cmd(?:_af)?|e(?:a(?:d(?:link)?|l(?:locf|path))|boot|m(?:ainder(?:f|l)?|ove|quo(?:f|l)?)|name|voke|wind)|in(?:dex|t(?:f|l)?)|mdir|ound(?:f|l)?|resvport(?:_af)?|userok)|s(?:brk|ca(?:lb(?:ln(?:f|l)?|n(?:f|l)?)?|nf)|e(?:archfs|ed48|lect|t(?:attrlist|buf(?:fer)?|domainname|e(?:gid|nv|uid)|g(?:id|roups)|host(?:id|name)|i(?:opolicy_np|timer)|jmp|key|l(?:inebuf|o(?:cale|gin))|mode|p(?:g(?:id|rp)|r(?:iority|ogname))|r(?:e(?:gid|uid)|gid|limit|uid)|s(?:groups_np|id|tate)|timeofday|u(?:id|sershell)|vbuf|wgroups_np))|i(?:g(?:a(?:ction|ddset|ltstack)|block|delset|emptyset|fillset|hold|i(?:gnore|nterrupt|smember)|longjmp|nal|p(?:ause|ending|rocmask)|relse|s(?:et(?:jmp|mask)?|uspend)|vec|wait)|md_muladd|n(?:f|h(?:f|l)?|l)?)|leep|nprintf|printf|qrt(?:f|l)?|ra(?:dixsort|nd(?:48|dev|om(?:dev)?)?)|scanf|t(?:p(?:cpy|ncpy)|r(?:c(?:a(?:se(?:cmp|str)|t)|hr|mp|oll|py|spn)|dup|error(?:_r)?|ftime|l(?:c(?:at|py)|en)|mode|n(?:c(?:a(?:secmp|t)|mp|py)|dup|len|str)|p(?:brk|time)|rchr|s(?:ep|ignal|pn|tr)|to(?:d|f(?:flags)?|k(?:_r)?|l(?:d|l)?|q|u(?:l(?:l)?|q))|xfrm))|wa(?:b|pon)|y(?:mlink|nc|s(?:conf|tem)))|t(?:an(?:f|h(?:f|l)?|l)?|c(?:getpgrp|setpgrp)|empnam|gamma(?:f|l)?|ime(?:2posix|gm|local)?|mp(?:file|nam)|runc(?:ate|f|l)?|ty(?:name(?:_r)?|slot)|zset(?:wall)?)|u(?:alarm|n(?:delete|getc|l(?:ink|ockpt)|setenv|whiteout)|sleep|times)|v(?:a(?:lloc|sprintf)|dprintf|f(?:ork|printf|scanf)|printf|s(?:canf|nprintf|printf|scanf))|w(?:ait(?:3|4|id|pid)?|c(?:stombs|tomb)|rite)|y(?:0|1|n)|zopen)\\b)" }, { "captures": { @@ -455,6 +999,17 @@ }, "match": "(\\s*)(\\bdispatch_(?:a(?:ctivate|ssert_queue(?:_(?:barrier|not))?)|queue_(?:attr_make_(?:initially_inactive|with_autorelease_frequency)|create_with_target))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.dispatch.10.14.c" + } + }, + "match": "(\\s*)(\\bdispatch_(?:async_and_wait(?:_f)?|barrier_async_and_wait(?:_f)?|set_qos_class_floor|workloop_(?:create(?:_inactive)?|set_autorelease_frequency))\\b)" + }, { "captures": { "1": { @@ -466,17 +1021,6 @@ }, "match": "(\\s*)(\\bdispatch_(?:a(?:fter(?:_f)?|pply(?:_f)?|sync(?:_f)?)|barrier_(?:async(?:_f)?|sync(?:_f)?)|cancel|data_(?:apply|c(?:opy_region|reate(?:_(?:concat|map|subrange))?)|get_size)|g(?:et_(?:context|global_queue|main_queue|specific)|roup_(?:async(?:_f)?|create|enter|leave|notify(?:_f)?|wait))|io_(?:barrier|c(?:lose|reate(?:_with_(?:io|path))?)|get_descriptor|read|set_(?:high_water|interval|low_water)|write)|main|notify|once(?:_f)?|queue_(?:create|get_(?:label|specific)|set_specific)|re(?:ad|lease|sume|tain)|s(?:e(?:maphore_(?:create|signal|wait)|t_(?:context|finalizer_f|target_queue))|ource_(?:c(?:ancel|reate)|get_(?:data|handle|mask)|merge_data|set_(?:cancel_handler(?:_f)?|event_handler(?:_f)?|registration_handler(?:_f)?|timer)|testcancel)|uspend|ync(?:_f)?)|testcancel|w(?:ait|rite))\\b)" }, - { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading" - }, - "2": { - "name": "support.function.mac-classic.c" - } - }, - "match": "(\\s*)(\\bStrLength\\b)" - }, { "captures": { "1": { @@ -488,28 +1032,6 @@ }, "match": "(\\s*)(\\b(?:OS(?:HostByteOrder|ReadSwapInt(?:16|32|64)|WriteSwapInt(?:16|32|64))|gethostuuid)\\b)" }, - { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading" - }, - "2": { - "name": "support.function.pthread.10.10.c" - } - }, - "match": "(\\s*)(\\bpthread_(?:attr_(?:get_qos_class_np|set_qos_class_np)|get_qos_class_np|override_qos_class_(?:end_np|start_np)|set_qos_class_self_np)\\b)" - }, - { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading" - }, - "2": { - "name": "support.function.pthread.c" - } - }, - "match": "(\\s*)(\\b(?:pthread_(?:at(?:fork|tr_(?:destroy|get(?:detachstate|guardsize|inheritsched|s(?:c(?:hedp(?:aram|olicy)|ope)|tack(?:addr|size)?))|init|set(?:detachstate|guardsize|inheritsched|s(?:c(?:hedp(?:aram|olicy)|ope)|tack(?:addr|size)?))))|c(?:ancel|ond(?:_(?:broadcast|destroy|init|signal(?:_thread_np)?|timedwait(?:_relative_np)?|wait)|attr_(?:destroy|getpshared|init|setpshared))|reate(?:_suspended_np)?)|detach|e(?:qual|xit)|from_mach_thread_np|get(?:_stack(?:addr_np|size_np)|concurrency|name_np|s(?:chedparam|pecific))|is_threaded_np|join|k(?:ey_(?:create|delete)|ill)|m(?:a(?:ch_thread_np|in_np)|utex(?:_(?:destroy|getprioceiling|init|lock|setprioceiling|trylock|unlock)|attr_(?:destroy|get(?:p(?:r(?:ioceiling|otocol)|shared)|type)|init|set(?:p(?:r(?:ioceiling|otocol)|shared)|type))))|once|rwlock(?:_(?:destroy|init|rdlock|try(?:rdlock|wrlock)|unlock|wrlock)|attr_(?:destroy|getpshared|init|setpshared))|s(?:e(?:lf|t(?:c(?:ancel(?:state|type)|oncurrency)|name_np|s(?:chedparam|pecific)))|igmask)|t(?:estcancel|hreadid_np)|yield_np)|sched_(?:get_priority_m(?:ax|in)|yield))\\b)" - }, { "captures": { "1": { @@ -530,7 +1052,40 @@ "name": "support.function.quartz.10.12.c" } }, - "match": "(\\s*)(\\bCGColor(?:ConversionInfoCreate(?:FromList)?|Space(?:CopyICCData|IsWideGamutRGB|SupportsOutput))\\b)" + "match": "(\\s*)(\\bCGColor(?:ConversionInfoCreate(?:FromList)?|Space(?:C(?:opy(?:ICCData|PropertyList)|reateWith(?:ICCData|PropertyList))|IsWideGamutRGB|SupportsOutput))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.13.c" + } + }, + "match": "(\\s*)(\\bCG(?:Color(?:ConversionInfoCreateFromListWithArguments|SpaceGetName)|DataProviderGetInfo|EventCreateScrollWheelEvent2|P(?:DF(?:ContextSetOutline|DocumentGet(?:AccessPermissions|Outline))|athApplyWithBlock))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.14.c" + } + }, + "match": "(\\s*)(\\bCG(?:ColorConversionInfoCreateWithOptions|ImageGet(?:ByteOrderInfo|PixelFormatInfo)|PDF(?:ArrayApplyBlock|DictionaryApplyBlock))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.15.c" + } + }, + "match": "(\\s*)(\\bCG(?:ColorCreate(?:GenericGrayGamma2_2|SRGB)|PDF(?:Context(?:BeginTag|EndTag)|TagTypeGetName))\\b)" }, { "captures": { @@ -563,7 +1118,7 @@ "name": "support.function.quartz.c" } }, - "match": "(\\s*)(\\bCG(?:A(?:cquireDisplayFadeReservation|ffineTransform(?:Concat|EqualToTransform|I(?:nvert|sIdentity)|Make(?:Rotation|Scale|Translation)?|Rotate|Scale|Translate)|ssociateMouseAndMouseCursorPosition)|B(?:eginDisplayConfiguration|itmapContext(?:Create(?:Image|WithData)?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|Data|Height|Width)))|C(?:a(?:ncelDisplayConfiguration|ptureAllDisplays(?:WithOptions)?)|o(?:lor(?:C(?:onversionInfoGetTypeID|reate(?:Copy(?:WithAlpha)?|Generic(?:CMYK|Gray|RGB)|WithPattern)?)|EqualToColor|Get(?:Alpha|Co(?:lorSpace|mponents|nstantColor)|NumberOfComponents|Pattern|TypeID)|Re(?:lease|tain)|Space(?:C(?:opy(?:ICCProfile|Name)|reate(?:Calibrated(?:Gray|RGB)|Device(?:CMYK|Gray|RGB)|I(?:CCBased|ndexed)|Lab|Pattern|With(?:ICCProfile|Name|PlatformColorSpace)))|Get(?:BaseColorSpace|ColorTable(?:Count)?|Model|NumberOfComponents|TypeID)|Re(?:lease|tain)))|mpleteDisplayConfiguration|n(?:figureDisplay(?:FadeEffect|MirrorOfDisplay|Origin|StereoOperation|WithDisplayMode)|text(?:Add(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Rect(?:s)?)|Begin(?:Pa(?:ge|th)|TransparencyLayer(?:WithRect)?)|C(?:l(?:earRect|ip(?:To(?:Mask|Rect(?:s)?))?|osePath)|o(?:n(?:catCTM|vert(?:PointTo(?:DeviceSpace|UserSpace)|RectTo(?:DeviceSpace|UserSpace)|SizeTo(?:DeviceSpace|UserSpace)))|pyPath))|Draw(?:Image|L(?:ayer(?:AtPoint|InRect)|inearGradient)|P(?:DFPage|ath)|RadialGradient|Shading|TiledImage)|E(?:O(?:Clip|FillPath)|nd(?:Page|TransparencyLayer))|F(?:ill(?:EllipseInRect|Path|Rect(?:s)?)|lush)|Get(?:C(?:TM|lipBoundingBox)|InterpolationQuality|Path(?:BoundingBox|CurrentPoint)|T(?:ext(?:Matrix|Position)|ypeID)|UserSpaceToDeviceSpaceTransform)|IsPathEmpty|MoveToPoint|PathContainsPoint|R(?:e(?:lease|placePathWithStrokedPath|storeGState|tain)|otateCTM)|S(?:aveGState|caleCTM|et(?:Al(?:lows(?:Antialiasing|FontS(?:moothing|ubpixel(?:Positioning|Quantization)))|pha)|BlendMode|C(?:MYK(?:FillColor|StrokeColor)|haracterSpacing)|F(?:ill(?:Color(?:Space|WithColor)?|Pattern)|latness|ont(?:Size)?)|Gray(?:FillColor|StrokeColor)|InterpolationQuality|Line(?:Cap|Dash|Join|Width)|MiterLimit|PatternPhase|R(?:GB(?:FillColor|StrokeColor)|enderingIntent)|S(?:h(?:adow(?:WithColor)?|ould(?:Antialias|S(?:moothFonts|ubpixel(?:PositionFonts|QuantizeFonts))))|troke(?:Color(?:Space|WithColor)?|Pattern))|Text(?:DrawingMode|Matrix|Position))|howGlyphsAtPositions|troke(?:EllipseInRect|LineSegments|Path|Rect(?:WithWidth)?)|ynchronize)|TranslateCTM))))|D(?:ata(?:Consumer(?:Create(?:With(?:CFData|URL))?|GetTypeID|Re(?:lease|tain))|Provider(?:C(?:opyData|reate(?:Direct|Sequential|With(?:CFData|Data|Filename|URL)))|GetTypeID|Re(?:lease|tain)))|isplay(?:Bounds|C(?:apture(?:WithOptions)?|opy(?:AllDisplayModes|ColorSpace|DisplayMode)|reateImage(?:ForRect)?)|Fade|G(?:ammaTableCapacity|etDrawingContext)|HideCursor|I(?:DToOpenGLDisplayMask|s(?:A(?:ctive|lwaysInMirrorSet|sleep)|Builtin|In(?:HWMirrorSet|MirrorSet)|Main|Online|Stereo))|M(?:irrorsDisplay|o(?:de(?:Get(?:Height|IO(?:DisplayModeID|Flags)|RefreshRate|TypeID|Width)|IsUsableForDesktopGUI|Re(?:lease|tain)|lNumber)|veCursorToPoint))|P(?:ixels(?:High|Wide)|rimaryDisplay)|R(?:e(?:gisterReconfigurationCallback|lease|moveReconfigurationCallback|storeColorSyncSettings)|otation)|S(?:creenSize|e(?:rialNumber|t(?:DisplayMode|StereoOperation))|howCursor)|U(?:nitNumber|sesOpenGLAcceleration)|VendorNumber))|Event(?:Create(?:Copy|Data|FromData|KeyboardEvent|MouseEvent|S(?:crollWheelEvent|ourceFromEvent))?|Get(?:DoubleValueField|Flags|IntegerValueField|Location|T(?:imestamp|ype(?:ID)?)|UnflippedLocation)|Keyboard(?:GetUnicodeString|SetUnicodeString)|Post(?:ToPSN)?|S(?:et(?:DoubleValueField|Flags|IntegerValueField|Location|Source|T(?:imestamp|ype))|ource(?:ButtonState|C(?:ounterForEventType|reate)|FlagsState|Get(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|SourceStateID|TypeID|UserData)|KeyState|Se(?:condsSinceLastEventType|t(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|UserData))))|Tap(?:Create(?:ForPSN)?|Enable|IsEnabled|PostEvent))|F(?:ont(?:C(?:anCreatePostScriptSubset|opy(?:FullName|GlyphNameForGlyph|PostScriptName|Table(?:ForTag|Tags)|Variation(?:Axes|s))|reate(?:CopyWithVariations|PostScript(?:Encoding|Subset)|With(?:DataProvider|FontName)))|Get(?:Ascent|CapHeight|Descent|FontBBox|Glyph(?:Advances|BBoxes|WithGlyphName)|ItalicAngle|Leading|NumberOfGlyphs|StemV|TypeID|UnitsPerEm|XHeight)|Re(?:lease|tain))|unction(?:Create|GetTypeID|Re(?:lease|tain)))|G(?:et(?:ActiveDisplayList|Display(?:TransferBy(?:Formula|Table)|sWith(?:OpenGLDisplayMask|Point|Rect))|EventTapList|LastMouseDelta|OnlineDisplayList)|radient(?:CreateWithColor(?:Components|s)|GetTypeID|Re(?:lease|tain)))|Image(?:Create(?:Copy(?:WithColorSpace)?|With(?:ImageInRect|JPEGDataProvider|Mask(?:ingColors)?|PNGDataProvider))?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|D(?:ataProvider|ecode)|Height|RenderingIntent|ShouldInterpolate|TypeID|Width)|IsMask|MaskCreate|Re(?:lease|tain))|Layer(?:CreateWithContext|Get(?:Context|Size|TypeID)|Re(?:lease|tain))|MainDisplayID|OpenGLDisplayMaskToDisplayID|P(?:DF(?:ArrayGet(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|u(?:ll|mber))|Object|Str(?:eam|ing))|Conte(?:ntStream(?:CreateWith(?:Page|Stream)|Get(?:Resource|Streams)|Re(?:lease|tain))|xt(?:AddD(?:estinationAtPoint|ocumentMetadata)|BeginPage|C(?:lose|reate(?:WithURL)?)|EndPage|Set(?:DestinationForRect|URLForRect)))|D(?:ictionary(?:ApplyFunction|Get(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing)))|ocument(?:Allows(?:Copying|Printing)|CreateWith(?:Provider|URL)|Get(?:Catalog|I(?:D|nfo)|NumberOfPages|Page|TypeID|Version)|Is(?:Encrypted|Unlocked)|Re(?:lease|tain)|UnlockWithPassword))|O(?:bjectGet(?:Type|Value)|peratorTable(?:Create|Re(?:lease|tain)|SetCallback))|Page(?:Get(?:BoxRect|D(?:ictionary|ocument|rawingTransform)|PageNumber|RotationAngle|TypeID)|Re(?:lease|tain))|S(?:canner(?:Create|GetContentStream|Pop(?:Array|Boolean|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing))|Re(?:lease|tain)|Scan)|tr(?:eam(?:CopyData|GetDictionary)|ing(?:Copy(?:Date|TextString)|Get(?:BytePtr|Length)))))|SConverter(?:Abort|C(?:onvert|reate)|GetTypeID|IsConverting)|at(?:h(?:A(?:dd(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Re(?:ct(?:s)?|lativeArc))|pply)|C(?:loseSubpath|ontainsPoint|reate(?:Copy(?:By(?:DashingPath|StrokingPath|TransformingPath))?|Mutable(?:Copy(?:ByTransformingPath)?)?|With(?:EllipseInRect|Rect)))|EqualToPath|Get(?:BoundingBox|CurrentPoint|PathBoundingBox|TypeID)|Is(?:Empty|Rect)|MoveToPoint|Re(?:lease|tain))|tern(?:Create|GetTypeID|Re(?:lease|tain)))|oint(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToPoint|Make(?:WithDictionaryRepresentation)?))|Re(?:ct(?:ApplyAffineTransform|C(?:ontains(?:Point|Rect)|reateDictionaryRepresentation)|Divide|EqualToRect|Get(?:Height|M(?:ax(?:X|Y)|i(?:d(?:X|Y)|n(?:X|Y)))|Width)|I(?:n(?:set|te(?:gral|rsect(?:ion|sRect)))|s(?:Empty|Infinite|Null))|Make(?:WithDictionaryRepresentation)?|Offset|Standardize|Union)|lease(?:AllDisplays|DisplayFadeReservation)|storePermanentDisplayConfiguration)|S(?:e(?:ssionCopyCurrentDictionary|tDisplayTransferBy(?:ByteTable|Formula|Table))|h(?:ading(?:Create(?:Axial|Radial)|GetTypeID|Re(?:lease|tain))|ieldingWindow(?:ID|Level))|ize(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToSize|Make(?:WithDictionaryRepresentation)?))|VectorMake|W(?:arpMouseCursorPosition|indowL(?:evelForKey|istC(?:opyWindowInfo|reate(?:DescriptionFromArray|Image(?:FromArray)?)?))))\\b)" + "match": "(\\s*)(\\bCG(?:A(?:cquireDisplayFadeReservation|ffineTransform(?:Concat|EqualToTransform|I(?:nvert|sIdentity)|Make(?:Rotation|Scale|Translation)?|Rotate|Scale|Translate)|ssociateMouseAndMouseCursorPosition)|B(?:eginDisplayConfiguration|itmapContext(?:Create(?:Image|WithData)?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|Data|Height|Width)))|C(?:a(?:ncelDisplayConfiguration|ptureAllDisplays(?:WithOptions)?)|o(?:lor(?:C(?:onversionInfoGetTypeID|reate(?:Copy(?:WithAlpha)?|Generic(?:CMYK|Gray|RGB)|WithPattern)?)|EqualToColor|Get(?:Alpha|Co(?:lorSpace|mponents|nstantColor)|NumberOfComponents|Pattern|TypeID)|Re(?:lease|tain)|Space(?:C(?:opyName|reate(?:Calibrated(?:Gray|RGB)|Device(?:CMYK|Gray|RGB)|I(?:CCBased|ndexed)|Lab|Pattern|With(?:Name|PlatformColorSpace)))|Get(?:BaseColorSpace|ColorTable(?:Count)?|Model|NumberOfComponents|TypeID)|Re(?:lease|tain)))|mpleteDisplayConfiguration|n(?:figureDisplay(?:FadeEffect|MirrorOfDisplay|Origin|StereoOperation|WithDisplayMode)|text(?:Add(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Rect(?:s)?)|Begin(?:Pa(?:ge|th)|TransparencyLayer(?:WithRect)?)|C(?:l(?:earRect|ip(?:To(?:Mask|Rect(?:s)?))?|osePath)|o(?:n(?:catCTM|vert(?:PointTo(?:DeviceSpace|UserSpace)|RectTo(?:DeviceSpace|UserSpace)|SizeTo(?:DeviceSpace|UserSpace)))|pyPath))|Draw(?:Image|L(?:ayer(?:AtPoint|InRect)|inearGradient)|P(?:DFPage|ath)|RadialGradient|Shading|TiledImage)|E(?:O(?:Clip|FillPath)|nd(?:Page|TransparencyLayer))|F(?:ill(?:EllipseInRect|Path|Rect(?:s)?)|lush)|Get(?:C(?:TM|lipBoundingBox)|InterpolationQuality|Path(?:BoundingBox|CurrentPoint)|T(?:ext(?:Matrix|Position)|ypeID)|UserSpaceToDeviceSpaceTransform)|IsPathEmpty|MoveToPoint|PathContainsPoint|R(?:e(?:lease|placePathWithStrokedPath|s(?:etClip|toreGState)|tain)|otateCTM)|S(?:aveGState|caleCTM|et(?:Al(?:lows(?:Antialiasing|FontS(?:moothing|ubpixel(?:Positioning|Quantization)))|pha)|BlendMode|C(?:MYK(?:FillColor|StrokeColor)|haracterSpacing)|F(?:ill(?:Color(?:Space|WithColor)?|Pattern)|latness|ont(?:Size)?)|Gray(?:FillColor|StrokeColor)|InterpolationQuality|Line(?:Cap|Dash|Join|Width)|MiterLimit|PatternPhase|R(?:GB(?:FillColor|StrokeColor)|enderingIntent)|S(?:h(?:adow(?:WithColor)?|ould(?:Antialias|S(?:moothFonts|ubpixel(?:PositionFonts|QuantizeFonts))))|troke(?:Color(?:Space|WithColor)?|Pattern))|Text(?:DrawingMode|Matrix|Position))|howGlyphsAtPositions|troke(?:EllipseInRect|LineSegments|Path|Rect(?:WithWidth)?)|ynchronize)|TranslateCTM))))|D(?:ata(?:Consumer(?:Create(?:With(?:CFData|URL))?|GetTypeID|Re(?:lease|tain))|Provider(?:C(?:opyData|reate(?:Direct|Sequential|With(?:CFData|Data|Filename|URL)))|GetTypeID|Re(?:lease|tain)))|isplay(?:Bounds|C(?:apture(?:WithOptions)?|opy(?:AllDisplayModes|ColorSpace|DisplayMode)|reateImage(?:ForRect)?)|Fade|G(?:ammaTableCapacity|etDrawingContext)|HideCursor|I(?:DToOpenGLDisplayMask|s(?:A(?:ctive|lwaysInMirrorSet|sleep)|Builtin|In(?:HWMirrorSet|MirrorSet)|Main|Online|Stereo))|M(?:irrorsDisplay|o(?:de(?:Get(?:Height|IO(?:DisplayModeID|Flags)|RefreshRate|TypeID|Width)|IsUsableForDesktopGUI|Re(?:lease|tain)|lNumber)|veCursorToPoint))|P(?:ixels(?:High|Wide)|rimaryDisplay)|R(?:e(?:gisterReconfigurationCallback|lease|moveReconfigurationCallback|storeColorSyncSettings)|otation)|S(?:creenSize|e(?:rialNumber|t(?:DisplayMode|StereoOperation))|howCursor)|U(?:nitNumber|sesOpenGLAcceleration)|VendorNumber))|Event(?:Create(?:Copy|Data|FromData|KeyboardEvent|MouseEvent|S(?:crollWheelEvent|ourceFromEvent))?|Get(?:DoubleValueField|Flags|IntegerValueField|Location|T(?:imestamp|ype(?:ID)?)|UnflippedLocation)|Keyboard(?:GetUnicodeString|SetUnicodeString)|Post(?:ToPSN)?|S(?:et(?:DoubleValueField|Flags|IntegerValueField|Location|Source|T(?:imestamp|ype))|ource(?:ButtonState|C(?:ounterForEventType|reate)|FlagsState|Get(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|SourceStateID|TypeID|UserData)|KeyState|Se(?:condsSinceLastEventType|t(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|UserData))))|Tap(?:Create(?:ForPSN)?|Enable|IsEnabled|PostEvent))|F(?:ont(?:C(?:anCreatePostScriptSubset|opy(?:FullName|GlyphNameForGlyph|PostScriptName|Table(?:ForTag|Tags)|Variation(?:Axes|s))|reate(?:CopyWithVariations|PostScript(?:Encoding|Subset)|With(?:DataProvider|FontName)))|Get(?:Ascent|CapHeight|Descent|FontBBox|Glyph(?:Advances|BBoxes|WithGlyphName)|ItalicAngle|Leading|NumberOfGlyphs|StemV|TypeID|UnitsPerEm|XHeight)|Re(?:lease|tain))|unction(?:Create|GetTypeID|Re(?:lease|tain)))|G(?:et(?:ActiveDisplayList|Display(?:TransferBy(?:Formula|Table)|sWith(?:OpenGLDisplayMask|Point|Rect))|EventTapList|LastMouseDelta|OnlineDisplayList)|radient(?:CreateWithColor(?:Components|s)|GetTypeID|Re(?:lease|tain)))|Image(?:Create(?:Copy(?:WithColorSpace)?|With(?:ImageInRect|JPEGDataProvider|Mask(?:ingColors)?|PNGDataProvider))?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|D(?:ataProvider|ecode)|Height|RenderingIntent|ShouldInterpolate|TypeID|Width)|IsMask|MaskCreate|Re(?:lease|tain))|Layer(?:CreateWithContext|Get(?:Context|Size|TypeID)|Re(?:lease|tain))|MainDisplayID|OpenGLDisplayMaskToDisplayID|P(?:DF(?:ArrayGet(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|u(?:ll|mber))|Object|Str(?:eam|ing))|Conte(?:ntStream(?:CreateWith(?:Page|Stream)|Get(?:Resource|Streams)|Re(?:lease|tain))|xt(?:AddD(?:estinationAtPoint|ocumentMetadata)|BeginPage|C(?:lose|reate(?:WithURL)?)|EndPage|Set(?:DestinationForRect|URLForRect)))|D(?:ictionary(?:ApplyFunction|Get(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing)))|ocument(?:Allows(?:Copying|Printing)|CreateWith(?:Provider|URL)|Get(?:Catalog|I(?:D|nfo)|NumberOfPages|Page|TypeID|Version)|Is(?:Encrypted|Unlocked)|Re(?:lease|tain)|UnlockWithPassword))|O(?:bjectGet(?:Type|Value)|peratorTable(?:Create|Re(?:lease|tain)|SetCallback))|Page(?:Get(?:BoxRect|D(?:ictionary|ocument|rawingTransform)|PageNumber|RotationAngle|TypeID)|Re(?:lease|tain))|S(?:canner(?:Create|GetContentStream|Pop(?:Array|Boolean|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing))|Re(?:lease|tain)|Scan)|tr(?:eam(?:CopyData|GetDictionary)|ing(?:Copy(?:Date|TextString)|Get(?:BytePtr|Length)))))|SConverter(?:Abort|C(?:onvert|reate)|GetTypeID|IsConverting)|at(?:h(?:A(?:dd(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Re(?:ct(?:s)?|lativeArc))|pply)|C(?:loseSubpath|ontainsPoint|reate(?:Copy(?:By(?:DashingPath|StrokingPath|TransformingPath))?|Mutable(?:Copy(?:ByTransformingPath)?)?|With(?:EllipseInRect|Rect)))|EqualToPath|Get(?:BoundingBox|CurrentPoint|PathBoundingBox|TypeID)|Is(?:Empty|Rect)|MoveToPoint|Re(?:lease|tain))|tern(?:Create|GetTypeID|Re(?:lease|tain)))|oint(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToPoint|Make(?:WithDictionaryRepresentation)?))|Re(?:ct(?:ApplyAffineTransform|C(?:ontains(?:Point|Rect)|reateDictionaryRepresentation)|Divide|EqualToRect|Get(?:Height|M(?:ax(?:X|Y)|i(?:d(?:X|Y)|n(?:X|Y)))|Width)|I(?:n(?:set|te(?:gral|rsect(?:ion|sRect)))|s(?:Empty|Infinite|Null))|Make(?:WithDictionaryRepresentation)?|Offset|Standardize|Union)|lease(?:AllDisplays|DisplayFadeReservation)|storePermanentDisplayConfiguration)|S(?:e(?:ssionCopyCurrentDictionary|tDisplayTransferBy(?:ByteTable|Formula|Table))|h(?:ading(?:Create(?:Axial|Radial)|GetTypeID|Re(?:lease|tain))|ieldingWindow(?:ID|Level))|ize(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToSize|Make(?:WithDictionaryRepresentation)?))|VectorMake|W(?:arpMouseCursorPosition|indowL(?:evelForKey|istC(?:opyWindowInfo|reate(?:DescriptionFromArray|Image(?:FromArray)?)?))))\\b)" } ] } diff --git a/extensions/cpp/test/colorize-fixtures/test-92369.cpp b/extensions/cpp/test/colorize-fixtures/test-92369.cpp new file mode 100644 index 00000000000..b91dec67af6 --- /dev/null +++ b/extensions/cpp/test/colorize-fixtures/test-92369.cpp @@ -0,0 +1,3 @@ +std::tuple_element<0, std::pair, pugi::xml_node, std::less >, std::allocator, pugi::xml_node> > > > >::type dnode + +std::_Rb_tree_iterator, pugi::xml_node, std::less >, std::allocator, pugi::xml_node> > > > > > dnode_it = dnodes_.find(uid.position) diff --git a/extensions/cpp/test/colorize-results/test-78769_cpp.json b/extensions/cpp/test/colorize-results/test-78769_cpp.json index 72bb9cdede7..82846880603 100644 --- a/extensions/cpp/test/colorize-results/test-78769_cpp.json +++ b/extensions/cpp/test/colorize-results/test-78769_cpp.json @@ -191,7 +191,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -257,7 +257,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -389,7 +389,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -433,7 +433,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -532,7 +532,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -587,7 +587,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -719,7 +719,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -763,7 +763,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -862,7 +862,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -906,7 +906,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -1027,7 +1027,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -1071,7 +1071,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" diff --git a/extensions/cpp/test/colorize-results/test-92369_cpp.json b/extensions/cpp/test/colorize-results/test-92369_cpp.json new file mode 100644 index 00000000000..fcc9e47435b --- /dev/null +++ b/extensions/cpp/test/colorize-results/test-92369_cpp.json @@ -0,0 +1,1927 @@ +[ + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "tuple_element", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp constant.numeric.decimal.cpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pair", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pugi", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "xml_node", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "map", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basic_string", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "char", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pugi", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "xml_node", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "less", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basic_string", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "char", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "allocator", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pair", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "const", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.modifier.specifier.const.cpp", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basic_string", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "char", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pugi", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "xml_node", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp meta.template.call.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "type dnode", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "_Rb_tree_iterator", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pair", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "const", + "t": "source.cpp storage.modifier.specifier.const.cpp", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "long", + "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ",", + "t": "source.cpp punctuation.separator.delimiter.comma.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pair", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "pugi", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "xml_node", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.cpp punctuation.separator.delimiter.comma.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "map", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basic_string", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "char", + "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ",", + "t": "source.cpp punctuation.separator.delimiter.comma.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pugi", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "xml_node", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.cpp punctuation.separator.delimiter.comma.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "less", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basic_string", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "char", + "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ",", + "t": "source.cpp punctuation.separator.delimiter.comma.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "allocator", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pair", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "const", + "t": "source.cpp storage.modifier.specifier.const.cpp", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "basic_string", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "char", + "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ",", + "t": "source.cpp punctuation.separator.delimiter.comma.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "pugi", + "t": "source.cpp entity.name.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.scope-resolution: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "xml_node", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.cpp keyword.operator.comparison.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " dnode_it ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.cpp keyword.operator.assignment.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "dnodes_", + "t": "source.cpp variable.other.object.access.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.cpp punctuation.separator.dot-access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "find", + "t": "source.cpp entity.name.function.member.cpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "uid", + "t": "source.cpp variable.other.object.access.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.cpp punctuation.separator.dot-access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "position", + "t": "source.cpp variable.other.property.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index 108895f8f65..10f6041bb00 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -2314,7 +2314,7 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.character.escape.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -2430,4 +2430,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/csharp/package.json b/extensions/csharp/package.json index 3841eabd132..74bbb283788 100644 --- a/extensions/csharp/package.json +++ b/extensions/csharp/package.json @@ -37,7 +37,7 @@ ], "snippets": [{ "language": "csharp", - "path": "./snippets/csharp.json" + "path": "./snippets/csharp.code-snippets" }] } -} \ No newline at end of file +} diff --git a/extensions/csharp/snippets/csharp.json b/extensions/csharp/snippets/csharp.code-snippets similarity index 100% rename from extensions/csharp/snippets/csharp.json rename to extensions/csharp/snippets/csharp.code-snippets diff --git a/extensions/css-language-features/.vscodeignore b/extensions/css-language-features/.vscodeignore index fa38a471362..a08d9b8dec7 100644 --- a/extensions/css-language-features/.vscodeignore +++ b/extensions/css-language-features/.vscodeignore @@ -16,4 +16,6 @@ server/.npmignore yarn.lock server/extension.webpack.config.js extension.webpack.config.js -CONTRIBUTING.md \ No newline at end of file +server/extension-browser.webpack.config.js +extension-browser.webpack.config.js +CONTRIBUTING.md diff --git a/extensions/css-language-features/CONTRIBUTING.md b/extensions/css-language-features/CONTRIBUTING.md index 38843f2fbaa..be9c9854b00 100644 --- a/extensions/css-language-features/CONTRIBUTING.md +++ b/extensions/css-language-features/CONTRIBUTING.md @@ -1,13 +1,13 @@ ## Setup -- Clone [Microsoft/vscode](https://github.com/microsoft/vscode) +- Clone [microsoft/vscode](https://github.com/microsoft/vscode) - Run `yarn` at `/`, this will install - Dependencies for `/extension/css-language-features/` - Dependencies for `/extension/css-language-features/server/` - devDependencies such as `gulp` - Open `/extensions/css-language-features/` as the workspace in VS Code -- Run the [`Launch Extension`](https://github.com/Microsoft/vscode/blob/master/extensions/css-language-features/.vscode/launch.json) debug target in the Debug View. This will: +- Run the [`Launch Extension`](https://github.com/microsoft/vscode/blob/master/extensions/css-language-features/.vscode/launch.json) debug target in the Debug View. This will: - Launch the `preLaunchTask` task to compile the extension - Launch a new VS Code instance with the `css-language-features` extension loaded - You should see a notification saying the development version of `css-language-features` overwrites the bundled version of `css-language-features` @@ -16,15 +16,15 @@ ### Contribute to vscode-css-languageservice -[Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) contains the language smarts for CSS/SCSS/Less. +[microsoft/vscode-css-languageservice](https://github.com/microsoft/vscode-css-languageservice) contains the language smarts for CSS/SCSS/Less. This extension wraps the css language service into a Language Server for VS Code. -If you want to fix CSS/SCSS/Less issues or make improvements, you should make changes at [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice). +If you want to fix CSS/SCSS/Less issues or make improvements, you should make changes at [microsoft/vscode-css-languageservice](https://github.com/microsoft/vscode-css-languageservice). However, within this extension, you can run a development version of `vscode-css-languageservice` to debug code or test language features interactively: #### Linking `vscode-css-languageservice` in `css-language-features/server/` -- Clone [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) +- Clone [microsoft/vscode-css-languageservice](https://github.com/microsoft/vscode-css-languageservice) - Run `yarn` in `vscode-css-languageservice` - Run `yarn link` in `vscode-css-languageservice`. This will compile and link `vscode-css-languageservice` - In `css-language-features/server/`, run `yarn link vscode-css-languageservice` diff --git a/extensions/css-language-features/README.md b/extensions/css-language-features/README.md index 5a3fad4948b..e3430c6a178 100644 --- a/extensions/css-language-features/README.md +++ b/extensions/css-language-features/README.md @@ -6,4 +6,4 @@ See [CSS, SCSS and Less in VS Code](https://code.visualstudio.com/docs/languages/css) to learn about the features of this extension. -Please read the [CONTRIBUTING.md](https://github.com/Microsoft/vscode/blob/master/extensions/css-language-features/CONTRIBUTING.md) file to learn how to contribute to this extension. \ No newline at end of file +Please read the [CONTRIBUTING.md](https://github.com/microsoft/vscode/blob/master/extensions/css-language-features/CONTRIBUTING.md) file to learn how to contribute to this extension. diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts new file mode 100644 index 00000000000..2a5e3e1f2c2 --- /dev/null +++ b/extensions/css-language-features/client/src/browser/cssClientMain.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionContext, Uri } from 'vscode'; +import { LanguageClientOptions } from 'vscode-languageclient'; +import { startClient, LanguageClientConstructor } from '../cssClient'; +import { LanguageClient } from 'vscode-languageclient/browser'; + +declare const Worker: { + new(stringUrl: string): any; +}; +declare const TextDecoder: { + new(encoding?: string): { decode(buffer: ArrayBuffer): string; }; +}; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); + try { + const worker = new Worker(serverMain.toString()); + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, clientOptions, worker); + }; + + startClient(context, newLanguageClient, { TextDecoder }); + + } catch (e) { + console.log(e); + } +} diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssClient.ts similarity index 76% rename from extensions/css-language-features/client/src/cssMain.ts rename to extensions/css-language-features/client/src/cssClient.ts index beabfc68cad..4bfa95c8439 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssClient.ts @@ -3,38 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode'; -import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, ProvideCompletionItemsSignature } from 'vscode-languageclient'; +import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode'; +import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, CommonLanguageClient } from 'vscode-languageclient'; import * as nls from 'vscode-nls'; -import { getCustomDataPathsFromAllExtensions, getCustomDataPathsInAllWorkspaces } from './customData'; +import { getCustomDataSource } from './customData'; +import { RequestService, serveFileSystemRequests } from './requests'; + +namespace CustomDataChangedNotification { + export const type: NotificationType = new NotificationType('css/customDataChanged'); +} const localize = nls.loadMessageBundle(); -// this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; - let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main; - let serverModule = context.asAbsolutePath(path.join('server', serverMain)); +export interface Runtime { + TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string; } }; + fs?: RequestService; +} - // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; +export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { - // If the extension is launch in debug mode the debug server options are use - // Otherwise the run options are used - let serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } - }; + const customDataSource = getCustomDataSource(context.subscriptions); let documentSelector = ['css', 'scss', 'less']; - let dataPaths = [ - ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), - ...getCustomDataPathsFromAllExtensions() - ]; - // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -42,7 +35,7 @@ export function activate(context: ExtensionContext) { configurationSection: ['css', 'scss', 'less'] }, initializationOptions: { - dataPaths + handledSchemas: ['file'] }, middleware: { provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { @@ -82,8 +75,17 @@ export function activate(context: ExtensionContext) { }; // Create the language client and start the client. - let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); + let client = newLanguageClient('css', localize('cssserver.name', 'CSS Language Server'), clientOptions); client.registerProposedFeatures(); + client.onReady().then(() => { + + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); + customDataSource.onDidChange(() => { + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); + }); + + serveFileSystemRequests(client, runtime); + }); let disposable = client.start(); // Push the disposable to the context's subscriptions so that the @@ -118,7 +120,7 @@ export function activate(context: ExtensionContext) { const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; return languages.registerCompletionItemProvider(documentSelector, { - provideCompletionItems(doc, pos) { + provideCompletionItems(doc: TextDocument, pos: Position) { let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos)); let match = lineUntilPos.match(regionCompletionRegExpr); if (match) { @@ -162,13 +164,3 @@ export function activate(context: ExtensionContext) { } } } - -function readJSONFile(location: string) { - try { - return JSON.parse(fs.readFileSync(location).toString()); - } catch (e) { - console.log(`Problems reading ${location}: ${e}`); - return {}; - } -} - diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts index 7054d0f1ba9..24b56f12b06 100644 --- a/extensions/css-language-features/client/src/customData.ts +++ b/extensions/css-language-features/client/src/customData.ts @@ -3,54 +3,86 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import { workspace, WorkspaceFolder, extensions } from 'vscode'; +import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode'; +import { resolvePath, joinPath } from './requests'; -interface ExperimentalConfig { - customData?: string[]; - experimental?: { - customData?: string[]; +export function getCustomDataSource(toDispose: Disposable[]) { + let pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); + let pathsInExtensions = getCustomDataPathsFromAllExtensions(); + + const onChange = new EventEmitter(); + + toDispose.push(extensions.onDidChange(_ => { + const newPathsInExtensions = getCustomDataPathsFromAllExtensions(); + if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) { + pathsInExtensions = newPathsInExtensions; + onChange.fire(); + } + })); + toDispose.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('css.customData')) { + pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); + onChange.fire(); + } + })); + + return { + get uris() { + return pathsInWorkspace.concat(pathsInExtensions); + }, + get onDidChange() { + return onChange.event; + } }; } -export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined): string[] { + +function getCustomDataPathsInAllWorkspaces(): string[] { + const workspaceFolders = workspace.workspaceFolders; + const dataPaths: string[] = []; if (!workspaceFolders) { return dataPaths; } - workspaceFolders.forEach(wf => { - const allCssConfig = workspace.getConfiguration(undefined, wf.uri); - const wfCSSConfig = allCssConfig.inspect('css'); - if (wfCSSConfig && wfCSSConfig.workspaceFolderValue && wfCSSConfig.workspaceFolderValue.customData) { - const customData = wfCSSConfig.workspaceFolderValue.customData; - if (Array.isArray(customData)) { - customData.forEach(t => { - if (typeof t === 'string') { - dataPaths.push(path.resolve(wf.uri.fsPath, t)); - } - }); + const collect = (paths: string[] | undefined, rootFolder: Uri) => { + if (Array.isArray(paths)) { + for (const path of paths) { + if (typeof path === 'string') { + dataPaths.push(resolvePath(rootFolder, path).toString()); + } } } - }); + }; + for (let i = 0; i < workspaceFolders.length; i++) { + const folderUri = workspaceFolders[i].uri; + const allCssConfig = workspace.getConfiguration('css', folderUri); + const customDataInspect = allCssConfig.inspect('customData'); + if (customDataInspect) { + collect(customDataInspect.workspaceFolderValue, folderUri); + if (i === 0) { + if (workspace.workspaceFile) { + collect(customDataInspect.workspaceValue, workspace.workspaceFile); + } + collect(customDataInspect.globalValue, folderUri); + } + } + + } return dataPaths; } -export function getCustomDataPathsFromAllExtensions(): string[] { +function getCustomDataPathsFromAllExtensions(): string[] { const dataPaths: string[] = []; - for (const extension of extensions.all) { - const contributes = extension.packageJSON && extension.packageJSON.contributes; - - if (contributes && contributes.css && contributes.css.customData && Array.isArray(contributes.css.customData)) { - const relativePaths: string[] = contributes.css.customData; - relativePaths.forEach(rp => { - dataPaths.push(path.resolve(extension.extensionPath, rp)); - }); + const customData = extension.packageJSON?.contributes?.css?.customData; + if (Array.isArray(customData)) { + for (const rp of customData) { + dataPaths.push(joinPath(extension.extensionUri, rp).toString()); + } } } - return dataPaths; } diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts new file mode 100644 index 00000000000..cc675b494c2 --- /dev/null +++ b/extensions/css-language-features/client/src/node/cssClientMain.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getNodeFSRequestService } from './nodeFs'; +import { ExtensionContext, extensions } from 'vscode'; +import { startClient, LanguageClientConstructor } from '../cssClient'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { TextDecoder } from 'util'; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + + const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || ''; + + const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/cssServerMain`; + const serverModule = context.asAbsolutePath(serverMain); + + // The debug options for the server + const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + + // If the extension is launch in debug mode the debug server options are use + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, serverOptions, clientOptions); + }; + + startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder }); +} diff --git a/extensions/css-language-features/client/src/node/nodeFs.ts b/extensions/css-language-features/client/src/node/nodeFs.ts new file mode 100644 index 00000000000..c13ef2e1c08 --- /dev/null +++ b/extensions/css-language-features/client/src/node/nodeFs.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { Uri } from 'vscode'; +import { getScheme, RequestService, FileType } from '../requests'; + +export function getNodeFSRequestService(): RequestService { + function ensureFileUri(location: string) { + if (getScheme(location) !== 'file') { + throw new Error('fileRequestService can only handle file URLs'); + } + } + return { + getContent(location: string, encoding?: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.readFile(uri.fsPath, encoding, (err, buf) => { + if (err) { + return e(err); + } + c(buf.toString()); + + }); + }); + }, + stat(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.stat(uri.fsPath, (err, stats) => { + if (err) { + if (err.code === 'ENOENT') { + return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }); + } else { + return e(err); + } + } + + let type = FileType.Unknown; + if (stats.isFile()) { + type = FileType.File; + } else if (stats.isDirectory()) { + type = FileType.Directory; + } else if (stats.isSymbolicLink()) { + type = FileType.SymbolicLink; + } + + c({ + type, + ctime: stats.ctime.getTime(), + mtime: stats.mtime.getTime(), + size: stats.size + }); + }); + }); + }, + readDirectory(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const path = Uri.parse(location).fsPath; + + fs.readdir(path, { withFileTypes: true }, (err, children) => { + if (err) { + return e(err); + } + c(children.map(stat => { + if (stat.isSymbolicLink()) { + return [stat.name, FileType.SymbolicLink]; + } else if (stat.isDirectory()) { + return [stat.name, FileType.Directory]; + } else if (stat.isFile()) { + return [stat.name, FileType.File]; + } else { + return [stat.name, FileType.Unknown]; + } + })); + }); + }); + } + }; +} diff --git a/extensions/css-language-features/client/src/requests.ts b/extensions/css-language-features/client/src/requests.ts new file mode 100644 index 00000000000..1b1e70b2d88 --- /dev/null +++ b/extensions/css-language-features/client/src/requests.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Uri, workspace } from 'vscode'; +import { RequestType, CommonLanguageClient } from 'vscode-languageclient'; +import { Runtime } from './cssClient'; + +export namespace FsContentRequest { + export const type: RequestType<{ uri: string; encoding?: string; }, string, any, any> = new RequestType('fs/content'); +} +export namespace FsStatRequest { + export const type: RequestType = new RequestType('fs/stat'); +} + +export namespace FsReadDirRequest { + export const type: RequestType = new RequestType('fs/readDir'); +} + +export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) { + client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => { + const uri = Uri.parse(param.uri); + if (uri.scheme === 'file' && runtime.fs) { + return runtime.fs.getContent(param.uri); + } + return workspace.fs.readFile(uri).then(buffer => { + return new runtime.TextDecoder(param.encoding).decode(buffer); + }); + }); + client.onRequest(FsReadDirRequest.type, (uriString: string) => { + const uri = Uri.parse(uriString); + if (uri.scheme === 'file' && runtime.fs) { + return runtime.fs.readDirectory(uriString); + } + return workspace.fs.readDirectory(uri); + }); + client.onRequest(FsStatRequest.type, (uriString: string) => { + const uri = Uri.parse(uriString); + if (uri.scheme === 'file' && runtime.fs) { + return runtime.fs.stat(uriString); + } + return workspace.fs.stat(uri); + }); +} + +export enum FileType { + /** + * The file type is unknown. + */ + Unknown = 0, + /** + * A regular file. + */ + File = 1, + /** + * A directory. + */ + Directory = 2, + /** + * A symbolic link to a file. + */ + SymbolicLink = 64 +} +export interface FileStat { + /** + * The type of the file, e.g. is a regular file, a directory, or symbolic link + * to a file. + */ + type: FileType; + /** + * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + */ + ctime: number; + /** + * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + */ + mtime: number; + /** + * The size in bytes. + */ + size: number; +} + +export interface RequestService { + getContent(uri: string, encoding?: string): Promise; + + stat(uri: string): Promise; + readDirectory(uri: string): Promise<[string, FileType][]>; +} + +export function getScheme(uri: string) { + return uri.substr(0, uri.indexOf(':')); +} + +export function dirname(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; +} + +export function basename(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return uri.substr(lastIndexOfSlash + 1); +} + +const Slash = '/'.charCodeAt(0); +const Dot = '.'.charCodeAt(0); + +export function isAbsolutePath(path: string) { + return path.charCodeAt(0) === Slash; +} + +export function resolvePath(uri: Uri, path: string): Uri { + if (isAbsolutePath(path)) { + return uri.with({ path: normalizePath(path.split('/')) }); + } + return joinPath(uri, path); +} + +export function normalizePath(parts: string[]): string { + const newParts: string[] = []; + for (const part of parts) { + if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { + // ignore + } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { + newParts.pop(); + } else { + newParts.push(part); + } + } + if (parts.length > 1 && parts[parts.length - 1].length === 0) { + newParts.push(''); + } + let res = newParts.join('/'); + if (parts[0].length === 0) { + res = '/' + res; + } + return res; +} + + +export function joinPath(uri: Uri, ...paths: string[]): Uri { + const parts = uri.path.split('/'); + for (let path of paths) { + parts.push(...path.split('/')); + } + return uri.with({ path: normalizePath(parts) }); +} diff --git a/extensions/css-language-features/extension-browser.webpack.config.js b/extensions/css-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..cb2e13c7ed3 --- /dev/null +++ b/extensions/css-language-features/extension-browser.webpack.config.js @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: path.join(__dirname, 'client'), + entry: { + extension: './src/browser/cssClientMain.ts' + }, + output: { + filename: 'cssClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'browser') + } +}); diff --git a/extensions/css-language-features/extension.webpack.config.js b/extensions/css-language-features/extension.webpack.config.js index dec7ad5afb4..a931210ab32 100644 --- a/extensions/css-language-features/extension.webpack.config.js +++ b/extensions/css-language-features/extension.webpack.config.js @@ -13,10 +13,10 @@ const path = require('path'); module.exports = withDefaults({ context: path.join(__dirname, 'client'), entry: { - extension: './src/cssMain.ts', + extension: './src/node/cssClientMain.ts', }, output: { - filename: 'cssMain.js', - path: path.join(__dirname, 'client', 'dist') + filename: 'cssClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'node') } }); diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index ba1c6f95918..29f056e4137 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -15,7 +15,8 @@ "onLanguage:scss", "onCommand:_css.applyCodeAction" ], - "main": "./client/out/cssMain", + "main": "./client/out/node/cssClientMain", + "browser": "./client/dist/browser/cssClientMain", "enableProposedApi": true, "scripts": { "compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server", @@ -797,7 +798,7 @@ "jsonValidation": [ { "fileMatch": "*.css-data.json", - "url": "https://raw.githubusercontent.com/Microsoft/vscode-css-languageservice/master/docs/customData.schema.json" + "url": "https://raw.githubusercontent.com/microsoft/vscode-css-languageservice/master/docs/customData.schema.json" }, { "fileMatch": "package.json", @@ -806,11 +807,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^6.0.0-next.3", - "vscode-nls": "^4.1.1" + "vscode-languageclient": "7.0.0-next.5.1", + "vscode-nls": "^4.1.2" }, "devDependencies": { "@types/node": "^12.11.7", - "mocha": "^6.1.4" + "mocha": "^7.0.1" } } diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index 1517384b652..7f2ffb5c1d9 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -2,7 +2,7 @@ "displayName": "CSS Language Features", "description": "Provides rich language support for CSS, LESS and SCSS files.", "css.title": "CSS", - "css.customData.desc": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/Microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", + "css.customData.desc": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", "css.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", "css.completion.completePropertyWithSemicolon.desc": "Insert semicolon at end of line when completing CSS properties", "css.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", diff --git a/extensions/css-language-features/schemas/package.schema.json b/extensions/css-language-features/schemas/package.schema.json index cf4193008ec..831149caa9e 100644 --- a/extensions/css-language-features/schemas/package.schema.json +++ b/extensions/css-language-features/schemas/package.schema.json @@ -8,7 +8,7 @@ "properties": { "css.customData": { "type": "array", - "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/Microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", + "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", "items": { "type": "string", "description": "Relative path to a CSS custom data file" diff --git a/extensions/css-language-features/server/extension-browser.webpack.config.js b/extensions/css-language-features/server/extension-browser.webpack.config.js new file mode 100644 index 00000000000..38816259ddf --- /dev/null +++ b/extensions/css-language-features/server/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/browser/cssServerMain.ts', + }, + output: { + filename: 'cssServerMain.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var' + } +}); diff --git a/extensions/css-language-features/server/extension.webpack.config.js b/extensions/css-language-features/server/extension.webpack.config.js index 68b850b3773..531035f636c 100644 --- a/extensions/css-language-features/server/extension.webpack.config.js +++ b/extensions/css-language-features/server/extension.webpack.config.js @@ -13,10 +13,10 @@ const path = require('path'); module.exports = withDefaults({ context: path.join(__dirname), entry: { - extension: './src/cssServerMain.ts', + extension: './src/node/cssServerMain.ts', }, output: { filename: 'cssServerMain.js', - path: path.join(__dirname, 'dist') + path: path.join(__dirname, 'dist', 'node'), } }); diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 66c7e087dcc..f249a512a5a 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -7,17 +7,19 @@ "engines": { "node": "*" }, - "main": "./out/cssServerMain", + "main": "./out/node/cssServerMain", + "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.24", - "vscode-languageserver": "^6.0.0-next.3" + "vscode-css-languageservice": "^4.3.5", + "vscode-languageserver": "7.0.0-next.3", + "vscode-uri": "^2.1.2" }, "devDependencies": { - "@types/mocha": "2.2.33", + "@types/mocha": "7.0.2", "@types/node": "^12.11.7", - "glob": "^7.1.4", - "mocha": "^6.1.4", - "mocha-junit-reporter": "^1.23.1", + "glob": "^7.1.6", + "mocha": "^7.1.2", + "mocha-junit-reporter": "^1.23.3", "mocha-multi-reporters": "^1.1.7" }, "scripts": { @@ -27,6 +29,6 @@ "install-service-local": "npm install ../../../../vscode-css-languageservice -f", "install-server-next": "yarn add vscode-languageserver@next", "install-server-local": "npm install ../../../../vscode-languageserver-node/server -f", - "test": "../../../node_modules/.bin/mocha" + "test": "node ./test/index.js" } } diff --git a/extensions/css-language-features/server/src/browser/cssServerMain.ts b/extensions/css-language-features/server/src/browser/cssServerMain.ts new file mode 100644 index 00000000000..13284fadcd9 --- /dev/null +++ b/extensions/css-language-features/server/src/browser/cssServerMain.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser'; +import { startServer } from '../cssServer'; + +declare let self: any; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +startServer(connection, {}); diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts new file mode 100644 index 00000000000..0b30955bd3f --- /dev/null +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -0,0 +1,373 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType +} from 'vscode-languageserver'; +import { URI } from 'vscode-uri'; +import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position } from 'vscode-css-languageservice'; +import { getLanguageModelCache } from './languageModelCache'; +import { formatError, runSafeAsync } from './utils/runner'; +import { getDocumentContext } from './utils/documentContext'; +import { fetchDataProviders } from './customData'; +import { RequestService, getRequestService } from './requests'; + +namespace CustomDataChangedNotification { + export const type: NotificationType = new NotificationType('css/customDataChanged'); +} + +export interface Settings { + css: LanguageSettings; + less: LanguageSettings; + scss: LanguageSettings; +} + +export interface RuntimeEnvironment { + file?: RequestService; + http?: RequestService +} + +export function startServer(connection: Connection, runtime: RuntimeEnvironment) { + + // Create a text document manager. + const documents = new TextDocuments(TextDocument); + // Make the text document manager listen on the connection + // for open, change and close text document events + documents.listen(connection); + + const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document)); + documents.onDidClose(e => { + stylesheets.onDocumentRemoved(e.document); + }); + connection.onShutdown(() => { + stylesheets.dispose(); + }); + + let scopedSettingsSupport = false; + let foldingRangeLimit = Number.MAX_VALUE; + let workspaceFolders: WorkspaceFolder[]; + + let dataProvidersReady: Promise = Promise.resolve(); + + const languageServices: { [id: string]: LanguageService } = {}; + + const notReady = () => Promise.reject('Not Ready'); + let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady }; + + // After the server has started the client sends an initialize request. The server receives + // in the passed params the rootPath of the workspace plus the client capabilities. + connection.onInitialize((params: InitializeParams): InitializeResult => { + workspaceFolders = (params).workspaceFolders; + if (!Array.isArray(workspaceFolders)) { + workspaceFolders = []; + if (params.rootPath) { + workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() }); + } + } + + requestService = getRequestService(params.initializationOptions?.handledSchemas || ['file'], connection, runtime); + + function getClientCapability(name: string, def: T) { + const keys = name.split('.'); + let c: any = params.capabilities; + for (let i = 0; c && i < keys.length; i++) { + if (!c.hasOwnProperty(keys[i])) { + return def; + } + c = c[keys[i]]; + } + return c; + } + const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); + scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); + foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + + languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + + const capabilities: ServerCapabilities = { + textDocumentSync: TextDocumentSyncKind.Incremental, + completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined, + hoverProvider: true, + documentSymbolProvider: true, + referencesProvider: true, + definitionProvider: true, + documentHighlightProvider: true, + documentLinkProvider: { + resolveProvider: false + }, + codeActionProvider: true, + renameProvider: true, + colorProvider: {}, + foldingRangeProvider: true, + selectionRangeProvider: true + }; + return { capabilities }; + }); + + function getLanguageService(document: TextDocument) { + let service = languageServices[document.languageId]; + if (!service) { + connection.console.log('Document type is ' + document.languageId + ', using css instead.'); + service = languageServices['css']; + } + return service; + } + + let documentSettings: { [key: string]: Thenable } = {}; + // remove document settings on close + documents.onDidClose(e => { + delete documentSettings[e.document.uri]; + }); + function getDocumentSettings(textDocument: TextDocument): Thenable { + if (scopedSettingsSupport) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; + promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return Promise.resolve(undefined); + } + + // The settings have changed. Is send on server activation as well. + connection.onDidChangeConfiguration(change => { + updateConfiguration(change.settings); + }); + + function updateConfiguration(settings: Settings) { + for (const languageId in languageServices) { + languageServices[languageId].configure((settings as any)[languageId]); + } + // reset all document settings + documentSettings = {}; + // Revalidate any open text documents + documents.all().forEach(triggerValidation); + } + + const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {}; + const validationDelayMs = 500; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + clearTimeout(request); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + pendingValidationRequests[textDocument.uri] = setTimeout(() => { + delete pendingValidationRequests[textDocument.uri]; + validateTextDocument(textDocument); + }, validationDelayMs); + } + + function validateTextDocument(textDocument: TextDocument): void { + const settingsPromise = getDocumentSettings(textDocument); + Promise.all([settingsPromise, dataProvidersReady]).then(async ([settings]) => { + const stylesheet = stylesheets.get(textDocument); + const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + }, e => { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + }); + } + + + function updateDataProviders(dataPaths: string[]) { + dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => { + for (const lang in languageServices) { + languageServices[lang].setDataProviders(true, customDataProviders); + } + }); + } + + connection.onCompletion((textDocumentPosition, token) => { + return runSafeAsync(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (document) { + await dataProvidersReady; + const styleSheet = stylesheets.get(document); + const documentContext = getDocumentContext(document.uri, workspaceFolders); + return getLanguageService(document).doComplete2(document, textDocumentPosition.position, styleSheet, documentContext); + } + return null; + }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onHover((textDocumentPosition, token) => { + return runSafeAsync(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (document) { + await dataProvidersReady; + const styleSheet = stylesheets.get(document); + return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet); + } + return null; + }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onDocumentSymbol((documentSymbolParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentSymbolParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentSymbols(document, stylesheet); + } + return []; + }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); + }); + + connection.onDefinition((documentDefinitionParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentDefinitionParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet); + } + return null; + }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token); + }); + + connection.onDocumentHighlight((documentHighlightParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentHighlightParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet); + } + return []; + }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); + }); + + + connection.onDocumentLinks(async (documentLinkParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentLinkParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const documentContext = getDocumentContext(document.uri, workspaceFolders); + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext); + } + return []; + }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token); + }); + + + connection.onReferences((referenceParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(referenceParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet); + } + return []; + }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); + }); + + connection.onCodeAction((codeActionParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(codeActionParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet); + } + return []; + }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token); + }); + + connection.onDocumentColor((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentColors(document, stylesheet); + } + return []; + }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); + }); + + connection.onColorPresentation((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range); + } + return []; + }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); + }); + + connection.onRenameRequest((renameParameters, token) => { + return runSafeAsync(async () => { + const document = documents.get(renameParameters.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet); + } + return null; + }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token); + }); + + connection.onFoldingRanges((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + await dataProvidersReady; + return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); + } + return null; + }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); + }); + + connection.onSelectionRanges((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + const positions: Position[] = params.positions; + + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); + }); + + connection.onNotification(CustomDataChangedNotification.type, updateDataProviders); + + // Listen on the connection + connection.listen(); + +} + + diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts deleted file mode 100644 index 6a9db0b77e7..00000000000 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ /dev/null @@ -1,390 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { - createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind -} from 'vscode-languageserver'; -import { URI } from 'vscode-uri'; -import { stat as fsStat } from 'fs'; -import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, FileSystemProvider, FileType, TextDocument, CompletionList, Position } from 'vscode-css-languageservice'; -import { getLanguageModelCache } from './languageModelCache'; -import { getPathCompletionParticipant } from './pathCompletion'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { getDocumentContext } from './utils/documentContext'; -import { getDataProviders } from './customData'; - -export interface Settings { - css: LanguageSettings; - less: LanguageSettings; - scss: LanguageSettings; -} - -// Create a connection for the server. -const connection: IConnection = createConnection(); - -console.log = connection.console.log.bind(connection.console); -console.error = connection.console.error.bind(connection.console); - -process.on('unhandledRejection', (e: any) => { - connection.console.error(formatError(`Unhandled exception`, e)); -}); - -// Create a text document manager. -const documents = new TextDocuments(TextDocument); -// Make the text document manager listen on the connection -// for open, change and close text document events -documents.listen(connection); - -const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document)); -documents.onDidClose(e => { - stylesheets.onDocumentRemoved(e.document); -}); -connection.onShutdown(() => { - stylesheets.dispose(); -}); - -let scopedSettingsSupport = false; -let foldingRangeLimit = Number.MAX_VALUE; -let workspaceFolders: WorkspaceFolder[]; - -const languageServices: { [id: string]: LanguageService } = {}; - -const fileSystemProvider: FileSystemProvider = { - stat(documentUri: string) { - const filePath = URI.parse(documentUri).fsPath; - - return new Promise((c, e) => { - fsStat(filePath, (err, stats) => { - if (err) { - if (err.code === 'ENOENT') { - return c({ - type: FileType.Unknown, - ctime: -1, - mtime: -1, - size: -1 - }); - } else { - return e(err); - } - } - - let type = FileType.Unknown; - if (stats.isFile()) { - type = FileType.File; - } else if (stats.isDirectory()) { - type = FileType.Directory; - } else if (stats.isSymbolicLink()) { - type = FileType.SymbolicLink; - } - - c({ - type, - ctime: stats.ctime.getTime(), - mtime: stats.mtime.getTime(), - size: stats.size - }); - }); - }); - } -}; - -// After the server has started the client sends an initialize request. The server receives -// in the passed params the rootPath of the workspace plus the client capabilities. -connection.onInitialize((params: InitializeParams): InitializeResult => { - workspaceFolders = (params).workspaceFolders; - if (!Array.isArray(workspaceFolders)) { - workspaceFolders = []; - if (params.rootPath) { - workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() }); - } - } - - const dataPaths: string[] = params.initializationOptions.dataPaths || []; - const customDataProviders = getDataProviders(dataPaths); - - function getClientCapability(name: string, def: T) { - const keys = name.split('.'); - let c: any = params.capabilities; - for (let i = 0; c && i < keys.length; i++) { - if (!c.hasOwnProperty(keys[i])) { - return def; - } - c = c[keys[i]]; - } - return c; - } - const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); - foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - - languageServices.css = getCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities }); - languageServices.scss = getSCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities }); - languageServices.less = getLESSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities }); - - const capabilities: ServerCapabilities = { - textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined, - hoverProvider: true, - documentSymbolProvider: true, - referencesProvider: true, - definitionProvider: true, - documentHighlightProvider: true, - documentLinkProvider: { - resolveProvider: false - }, - codeActionProvider: true, - renameProvider: true, - colorProvider: {}, - foldingRangeProvider: true, - selectionRangeProvider: true - }; - return { capabilities }; -}); - -function getLanguageService(document: TextDocument) { - let service = languageServices[document.languageId]; - if (!service) { - connection.console.log('Document type is ' + document.languageId + ', using css instead.'); - service = languageServices['css']; - } - return service; -} - -let documentSettings: { [key: string]: Thenable } = {}; -// remove document settings on close -documents.onDidClose(e => { - delete documentSettings[e.document.uri]; -}); -function getDocumentSettings(textDocument: TextDocument): Thenable { - if (scopedSettingsSupport) { - let promise = documentSettings[textDocument.uri]; - if (!promise) { - const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; - promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]); - documentSettings[textDocument.uri] = promise; - } - return promise; - } - return Promise.resolve(undefined); -} - -// The settings have changed. Is send on server activation as well. -connection.onDidChangeConfiguration(change => { - updateConfiguration(change.settings); -}); - -function updateConfiguration(settings: Settings) { - for (const languageId in languageServices) { - languageServices[languageId].configure((settings as any)[languageId]); - } - // reset all document settings - documentSettings = {}; - // Revalidate any open text documents - documents.all().forEach(triggerValidation); -} - -const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {}; -const validationDelayMs = 500; - -// The content of a text document has changed. This event is emitted -// when the text document first opened or when its content has changed. -documents.onDidChangeContent(change => { - triggerValidation(change.document); -}); - -// a document has closed: clear all diagnostics -documents.onDidClose(event => { - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); -}); - -function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - clearTimeout(request); - delete pendingValidationRequests[textDocument.uri]; - } -} - -function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); -} - -function validateTextDocument(textDocument: TextDocument): void { - const settingsPromise = getDocumentSettings(textDocument); - settingsPromise.then(settings => { - const stylesheet = stylesheets.get(textDocument); - const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); - }, e => { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); - }); -} - -connection.onCompletion((textDocumentPosition, token) => { - return runSafe(() => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (!document) { - return null; - } - const cssLS = getLanguageService(document); - const pathCompletionList: CompletionList = { - isIncomplete: false, - items: [] - }; - cssLS.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, pathCompletionList)]); - const result = cssLS.doComplete(document, textDocumentPosition.position, stylesheets.get(document)); - return { - isIncomplete: pathCompletionList.isIncomplete, - items: [...pathCompletionList.items, ...result.items] - }; - }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onHover((textDocumentPosition, token) => { - return runSafe(() => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (document) { - const styleSheet = stylesheets.get(document); - return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet); - } - return null; - }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onDocumentSymbol((documentSymbolParams, token) => { - return runSafe(() => { - const document = documents.get(documentSymbolParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentSymbols(document, stylesheet); - } - return []; - }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); -}); - -connection.onDefinition((documentDefinitionParams, token) => { - return runSafe(() => { - const document = documents.get(documentDefinitionParams.textDocument.uri); - if (document) { - - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet); - } - return null; - }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token); -}); - -connection.onDocumentHighlight((documentHighlightParams, token) => { - return runSafe(() => { - const document = documents.get(documentHighlightParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet); - } - return []; - }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); -}); - - -connection.onDocumentLinks(async (documentLinkParams, token) => { - return runSafeAsync(async () => { - const document = documents.get(documentLinkParams.textDocument.uri); - if (document) { - const documentContext = getDocumentContext(document.uri, workspaceFolders); - const stylesheet = stylesheets.get(document); - return await getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext); - } - return []; - }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token); -}); - - -connection.onReferences((referenceParams, token) => { - return runSafe(() => { - const document = documents.get(referenceParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet); - } - return []; - }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); -}); - -connection.onCodeAction((codeActionParams, token) => { - return runSafe(() => { - const document = documents.get(codeActionParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet); - } - return []; - }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token); -}); - -connection.onDocumentColor((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentColors(document, stylesheet); - } - return []; - }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); -}); - -connection.onColorPresentation((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range); - } - return []; - }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); -}); - -connection.onRenameRequest((renameParameters, token) => { - return runSafe(() => { - const document = documents.get(renameParameters.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet); - } - return null; - }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token); -}); - -connection.onFoldingRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); - } - return null; - }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); -}); - -connection.onSelectionRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - const positions: Position[] = params.positions; - - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); - } - return []; - }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); -}); - - -// Listen on the connection -connection.listen(); diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts index f173d884a2b..ccfc706452c 100644 --- a/extensions/css-language-features/server/src/customData.ts +++ b/extensions/css-language-features/server/src/customData.ts @@ -3,48 +3,36 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CSSDataV1, ICSSDataProvider } from 'vscode-css-languageservice'; -import * as fs from 'fs'; +import { ICSSDataProvider, newCSSDataProvider } from 'vscode-css-languageservice'; +import { RequestService } from './requests'; -export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] { - const providers = dataPaths.map(p => { - if (fs.existsSync(p)) { - const data = parseCSSData(fs.readFileSync(p, 'utf-8')); - return { - provideProperties: () => data.properties || [], - provideAtDirectives: () => data.atDirectives || [], - providePseudoClasses: () => data.pseudoClasses || [], - providePseudoElements: () => data.pseudoElements || [] - }; - } else { - return { - provideProperties: () => [], - provideAtDirectives: () => [], - providePseudoClasses: () => [], - providePseudoElements: () => [] - }; +export function fetchDataProviders(dataPaths: string[], requestService: RequestService): Promise { + const providers = dataPaths.map(async p => { + try { + const content = await requestService.getContent(p); + return parseCSSData(content); + } catch (e) { + return newCSSDataProvider({ version: 1 }); } }); - return providers; + return Promise.all(providers); } -function parseCSSData(source: string): CSSDataV1 { +function parseCSSData(source: string): ICSSDataProvider { let rawData: any; try { rawData = JSON.parse(source); } catch (err) { - return { - version: 1 - }; + return newCSSDataProvider({ version: 1 }); } - return { - version: 1, + return newCSSDataProvider({ + version: rawData.version || 1, properties: rawData.properties || [], atDirectives: rawData.atDirectives || [], pseudoClasses: rawData.pseudoClasses || [], pseudoElements: rawData.pseudoElements || [] - }; + }); } diff --git a/extensions/css-language-features/server/src/node/cssServerMain.ts b/extensions/css-language-features/server/src/node/cssServerMain.ts new file mode 100644 index 00000000000..9e145398ff1 --- /dev/null +++ b/extensions/css-language-features/server/src/node/cssServerMain.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, Connection } from 'vscode-languageserver/node'; +import { formatError } from '../utils/runner'; +import { startServer } from '../cssServer'; +import { getNodeFSRequestService } from './nodeFs'; + +// Create a connection for the server. +const connection: Connection = createConnection(); + +console.log = connection.console.log.bind(connection.console); +console.error = connection.console.error.bind(connection.console); + +process.on('unhandledRejection', (e: any) => { + connection.console.error(formatError(`Unhandled exception`, e)); +}); + +startServer(connection, { file: getNodeFSRequestService() }); diff --git a/extensions/css-language-features/server/src/node/nodeFs.ts b/extensions/css-language-features/server/src/node/nodeFs.ts new file mode 100644 index 00000000000..c7b1301296d --- /dev/null +++ b/extensions/css-language-features/server/src/node/nodeFs.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RequestService, getScheme } from '../requests'; +import { URI as Uri } from 'vscode-uri'; + +import * as fs from 'fs'; +import { FileType } from 'vscode-css-languageservice'; + +export function getNodeFSRequestService(): RequestService { + function ensureFileUri(location: string) { + if (getScheme(location) !== 'file') { + throw new Error('fileRequestService can only handle file URLs'); + } + } + return { + getContent(location: string, encoding?: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.readFile(uri.fsPath, encoding, (err, buf) => { + if (err) { + return e(err); + } + c(buf.toString()); + + }); + }); + }, + stat(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.stat(uri.fsPath, (err, stats) => { + if (err) { + if (err.code === 'ENOENT') { + return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }); + } else { + return e(err); + } + } + + let type = FileType.Unknown; + if (stats.isFile()) { + type = FileType.File; + } else if (stats.isDirectory()) { + type = FileType.Directory; + } else if (stats.isSymbolicLink()) { + type = FileType.SymbolicLink; + } + + c({ + type, + ctime: stats.ctime.getTime(), + mtime: stats.mtime.getTime(), + size: stats.size + }); + }); + }); + }, + readDirectory(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const path = Uri.parse(location).fsPath; + + fs.readdir(path, { withFileTypes: true }, (err, children) => { + if (err) { + return e(err); + } + c(children.map(stat => { + if (stat.isSymbolicLink()) { + return [stat.name, FileType.SymbolicLink]; + } else if (stat.isDirectory()) { + return [stat.name, FileType.Directory]; + } else if (stat.isFile()) { + return [stat.name, FileType.File]; + } else { + return [stat.name, FileType.Unknown]; + } + })); + }); + }); + } + }; +} diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts deleted file mode 100644 index 6862f28e034..00000000000 --- a/extensions/css-language-features/server/src/pathCompletion.ts +++ /dev/null @@ -1,213 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import * as fs from 'fs'; -import { URI } from 'vscode-uri'; - -import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types'; -import { WorkspaceFolder } from 'vscode-languageserver'; -import { ICompletionParticipant } from 'vscode-css-languageservice'; - -import { startsWith, endsWith } from './utils/strings'; - -export function getPathCompletionParticipant( - document: TextDocument, - workspaceFolders: WorkspaceFolder[], - result: CompletionList -): ICompletionParticipant { - return { - onCssURILiteralValue: ({ position, range, uriValue }) => { - const fullValue = stripQuotes(uriValue); - if (!shouldDoPathCompletion(uriValue, workspaceFolders)) { - if (fullValue === '.' || fullValue === '..') { - result.isIncomplete = true; - } - return; - } - - let suggestions = providePathSuggestions(uriValue, position, range, document, workspaceFolders); - result.items = [...suggestions, ...result.items]; - }, - onCssImportPath: ({ position, range, pathValue }) => { - const fullValue = stripQuotes(pathValue); - if (!shouldDoPathCompletion(pathValue, workspaceFolders)) { - if (fullValue === '.' || fullValue === '..') { - result.isIncomplete = true; - } - return; - } - - let suggestions = providePathSuggestions(pathValue, position, range, document, workspaceFolders); - - if (document.languageId === 'scss') { - suggestions.forEach(s => { - if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) { - if (s.textEdit) { - s.textEdit.newText = s.label.slice(1, -5); - } else { - s.label = s.label.slice(1, -5); - } - } - }); - } - - result.items = [...suggestions, ...result.items]; - } - }; -} - -function providePathSuggestions(pathValue: string, position: Position, range: Range, document: TextDocument, workspaceFolders: WorkspaceFolder[]) { - const fullValue = stripQuotes(pathValue); - const isValueQuoted = startsWith(pathValue, `'`) || startsWith(pathValue, `"`); - const valueBeforeCursor = isValueQuoted - ? fullValue.slice(0, position.character - (range.start.character + 1)) - : fullValue.slice(0, position.character - range.start.character); - const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders); - const currentDocFsPath = URI.parse(document.uri).fsPath; - - const paths = providePaths(valueBeforeCursor, currentDocFsPath, workspaceRoot) - .filter(p => { - // Exclude current doc's path - return path.resolve(currentDocFsPath, '../', p) !== currentDocFsPath; - }) - .filter(p => { - // Exclude paths that start with `.` - return p[0] !== '.'; - }); - - const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range; - const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange); - - const suggestions = paths.map(p => pathToSuggestion(p, replaceRange)); - return suggestions; -} - -function shouldDoPathCompletion(pathValue: string, workspaceFolders: WorkspaceFolder[]): boolean { - const fullValue = stripQuotes(pathValue); - if (fullValue === '.' || fullValue === '..') { - return false; - } - - if (!workspaceFolders || workspaceFolders.length === 0) { - return false; - } - - return true; -} - -function stripQuotes(fullValue: string) { - if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) { - return fullValue.slice(1, -1); - } else { - return fullValue; - } -} - -/** - * Get a list of path suggestions. Folder suggestions are suffixed with a slash. - */ -function providePaths(valueBeforeCursor: string, activeDocFsPath: string, root?: string): string[] { - const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); - const valueBeforeLastSlash = valueBeforeCursor.slice(0, lastIndexOfSlash + 1); - - const startsWithSlash = startsWith(valueBeforeCursor, '/'); - let parentDir: string; - if (startsWithSlash) { - if (!root) { - return []; - } - parentDir = path.resolve(root, '.' + valueBeforeLastSlash); - } else { - parentDir = path.resolve(activeDocFsPath, '..', valueBeforeLastSlash); - } - - try { - return fs.readdirSync(parentDir).map(f => { - return isDir(path.resolve(parentDir, f)) - ? f + '/' - : f; - }); - } catch (e) { - return []; - } -} - -const isDir = (p: string) => { - try { - return fs.statSync(p).isDirectory(); - } catch (e) { - return false; - } -}; - -function pathToReplaceRange(valueBeforeCursor: string, fullValue: string, fullValueRange: Range) { - let replaceRange: Range; - const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); - if (lastIndexOfSlash === -1) { - replaceRange = fullValueRange; - } else { - // For cases where cursor is in the middle of attribute value, like ', { + test('HTML JavaScript Completions', async () => { + await testCompletionFor('', { items: [ { label: 'location', resultText: '' }, ] }); - testCompletionFor('', { + await testCompletionFor('', { items: [ { label: 'getJSON', resultText: '' }, ] }); + await testCompletionFor('', { + items: [ + { label: 'a', resultText: '' }, + ] + }, 'test://test/test2.html'); }); }); @@ -96,8 +105,8 @@ suite('HTML Path Completion', () => { const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString(); const aboutHtmlUri = URI.file(path.resolve(fixtureRoot, 'about/about.html')).toString(); - test('Basics - Correct label/kind/result/command', () => { - testCompletionFor('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ||', '\n '); + test('HTML & Scripts', async () => { + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('\n ', '\n\n\n \n\n\n'); + await assertFormat('\n ', '\n\n\n \n\n\n'); + await assertFormat('\n ||', '\n '); }); - test('HTLM & Scripts - Fixtures', function () { + test('HTLM & Scripts - Fixtures', async () => { assertFormatWithFixture('19813.html', '19813.html'); assertFormatWithFixture('19813.html', '19813-4spaces.html', undefined, FormattingOptions.create(4, true)); assertFormatWithFixture('19813.html', '19813-tab.html', undefined, FormattingOptions.create(1, false)); assertFormatWithFixture('21634.html', '21634.html'); }); - test('Script end tag', function (): any { - assertFormat('\n\n ', '\n\n\n \n\n\n'); + test('Script end tag', async () => { + await assertFormat('\n\n ', '\n\n\n \n\n\n'); }); - test('HTML & Multiple Scripts', function (): any { - assertFormat('\n', '\n\n\n \n \n\n\n'); + test('HTML & Multiple Scripts', async () => { + await assertFormat('\n', '\n\n\n \n \n\n\n'); }); - test('HTML & Styles', function (): any { - assertFormat('\n', '\n\n\n \n\n\n'); + test('HTML & Styles', async () => { + await assertFormat('\n', '\n\n\n \n\n\n'); }); - test('EndWithNewline', function (): any { + test('EndWithNewline', async () => { let options = { html: { format: { @@ -91,26 +92,26 @@ suite('HTML Embedded Formatting', () => { } } }; - assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', options); - assertFormat('|

Hello

|', '\n

Hello

\n', options); - assertFormat('', '\n\n\n \n\n\n\n', options); + await assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', options); + await assertFormat('|

Hello

|', '\n

Hello

\n', options); + await assertFormat('', '\n\n\n \n\n\n\n', options); }); - test('Inside script', function (): any { - assertFormat('\n ', '\n '); - assertFormat('\n ', '\n '); + test('Inside script', async () => { + await assertFormat('\n ', '\n '); + await assertFormat('\n ', '\n '); }); - test('Range after new line', function (): any { - assertFormat('\n |\n|', '\n \n'); + test('Range after new line', async () => { + await assertFormat('\n |\n|', '\n \n'); }); - test('bug 36574', function (): any { - assertFormat('', ''); + test('bug 36574', async () => { + await assertFormat('', ''); }); - test('bug 48049', function (): any { - assertFormat( + test('bug 48049', async () => { + await assertFormat( [ '', '', @@ -158,7 +159,7 @@ suite('HTML Embedded Formatting', () => { ].join('\n') ); }); - test('#58435', () => { + test('#58435', async () => { let options = { html: { format: { @@ -189,7 +190,7 @@ suite('HTML Embedded Formatting', () => { '', ].join('\n'); - assertFormat(content, expected, options); + await assertFormat(content, expected, options); }); }); /* diff --git a/extensions/html-language-features/server/src/test/selectionRanges.test.ts b/extensions/html-language-features/server/src/test/selectionRanges.test.ts index 98bd73bc9bc..9f9937bcaf7 100644 --- a/extensions/html-language-features/server/src/test/selectionRanges.test.ts +++ b/extensions/html-language-features/server/src/test/selectionRanges.test.ts @@ -7,8 +7,9 @@ import 'mocha'; import * as assert from 'assert'; import { getLanguageModes, ClientCapabilities, TextDocument, SelectionRange} from '../modes/languageModes'; import { getSelectionRanges } from '../modes/selectionRanges'; +import { getNodeFSRequestService } from '../node/nodeFs'; -function assertRanges(content: string, expected: (number | string)[][]): void { +async function assertRanges(content: string, expected: (number | string)[][]): Promise { let message = `${content} gives selection range:\n`; const offset = content.indexOf('|'); @@ -18,10 +19,10 @@ function assertRanges(content: string, expected: (number | string)[][]): void { settings: {}, folders: [{ name: 'foo', uri: 'test://foo' }] }; - let languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); const document = TextDocument.create('test://foo.html', 'html', 1, content); - const actualRanges = getSelectionRanges(languageModes, document, [document.positionAt(offset)]); + const actualRanges = await getSelectionRanges(languageModes, document, [document.positionAt(offset)]); assert.equal(actualRanges.length, 1); const offsetPairs: [number, string][] = []; let curr: SelectionRange | undefined = actualRanges[0]; @@ -35,8 +36,8 @@ function assertRanges(content: string, expected: (number | string)[][]): void { } suite('HTML SelectionRange', () => { - test('Embedded JavaScript', () => { - assertRanges('', [ + test('Embedded JavaScript', async () => { + await assertRanges('', [ [48, '1'], [48, '1+2'], [47, '(1+2)'], @@ -51,11 +52,12 @@ suite('HTML SelectionRange', () => { ]); }); - test('Embedded CSS', () => { - assertRanges('', [ + test('Embedded CSS', async () => { + await assertRanges('', [ [34, 'none'], [25, 'display: none'], [24, ' display: none; '], + [23, '{ display: none; }'], [19, 'foo { display: none; }'], [19, 'foo { display: none; } '], [12, ''], @@ -64,8 +66,8 @@ suite('HTML SelectionRange', () => { ]); }); - test('Embedded style', () => { - assertRanges('
', [ + test('Embedded style', async () => { + await assertRanges('
', [ [19, 'red'], [12, 'color: red'], [11, '"color: red"'], diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index ea3777e84e9..6d815748e5e 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -7,6 +7,7 @@ import 'mocha'; import * as assert from 'assert'; import { TextDocument, getLanguageModes, ClientCapabilities, Range, Position } from '../modes/languageModes'; import { newSemanticTokenProvider } from '../modes/semanticTokens'; +import { getNodeFSRequestService } from '../node/nodeFs'; interface ExpectedToken { startLine: number; @@ -15,17 +16,17 @@ interface ExpectedToken { tokenClassifiction: string; } -function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): void { +async function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): Promise { const document = TextDocument.create('test://foo/bar.html', 'html', 1, lines.join('\n')); const workspace = { settings: {}, folders: [{ name: 'foo', uri: 'test://foo' }] }; - const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); const semanticTokensProvider = newSemanticTokenProvider(languageModes); const legend = semanticTokensProvider.legend; - const actual = semanticTokensProvider.getSemanticTokens(document, ranges); + const actual = await semanticTokensProvider.getSemanticTokens(document, ranges); let actualRanges = []; let lastLine = 0; @@ -48,7 +49,7 @@ function t(startLine: number, character: number, length: number, tokenClassifict suite('HTML Semantic Tokens', () => { - test('Variables', () => { + test('Variables', async () => { const input = [ /*0*/'', /*1*/'', @@ -63,7 +64,7 @@ suite('HTML Semantic Tokens', () => { /*10*/'', /*11*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 6, 1, 'variable.declaration'), t(3, 13, 2, 'variable.declaration'), t(3, 19, 1, 'variable'), t(5, 15, 1, 'variable.declaration.readonly'), t(5, 20, 2, 'variable'), t(5, 26, 1, 'variable'), t(5, 30, 1, 'variable.readonly'), t(6, 11, 1, 'variable.declaration'), @@ -71,7 +72,7 @@ suite('HTML Semantic Tokens', () => { ]); }); - test('Functions', () => { + test('Functions', async () => { const input = [ /*0*/'', /*1*/'', @@ -84,14 +85,14 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 11, 3, 'function.declaration'), t(3, 15, 2, 'parameter.declaration'), t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'member'), t(4, 24, 2, 'parameter'), t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'member'), t(6, 35, 7, 'member'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') ]); }); - test('Members', () => { + test('Members', async () => { const input = [ /*0*/'', /*1*/'', @@ -110,7 +111,7 @@ suite('HTML Semantic Tokens', () => { ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 8, 1, 'class.declaration'), t(4, 11, 1, 'property.declaration.static'), t(5, 4, 1, 'property.declaration'), @@ -120,7 +121,7 @@ suite('HTML Semantic Tokens', () => { ]); }); - test('Interfaces', () => { + test('Interfaces', async () => { const input = [ /*0*/'', /*1*/'', @@ -132,14 +133,14 @@ suite('HTML Semantic Tokens', () => { /*7*/'', /*8*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 12, 8, 'interface.declaration'), t(3, 23, 1, 'property.declaration'), t(3, 34, 1, 'property.declaration'), t(4, 8, 1, 'variable.declaration.readonly'), t(4, 30, 8, 'interface'), t(5, 8, 3, 'variable.declaration.readonly'), t(5, 15, 1, 'parameter.declaration'), t(5, 18, 8, 'interface'), t(5, 31, 1, 'parameter'), t(5, 33, 1, 'property'), t(5, 37, 1, 'parameter'), t(5, 39, 1, 'property') ]); }); - test('Readonly', () => { + test('Readonly', async () => { const input = [ /*0*/'', /*1*/'', @@ -152,7 +153,7 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), @@ -161,7 +162,7 @@ suite('HTML Semantic Tokens', () => { }); - test('Type aliases and type parameters', () => { + test('Type aliases and type parameters', async () => { const input = [ /*0*/'', /*1*/'', @@ -174,14 +175,14 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 7, 5, 'type.declaration'), t(3, 15, 3, 'interface') /* to investiagte */, t(4, 11, 1, 'function.declaration'), t(4, 13, 1, 'typeParameter.declaration'), t(4, 23, 5, 'type'), t(4, 30, 1, 'parameter.declaration'), t(4, 33, 1, 'typeParameter'), t(4, 47, 1, 'typeParameter'), t(5, 12, 1, 'typeParameter'), t(5, 29, 3, 'interface'), t(5, 41, 5, 'type'), ]); }); - test('TS and JS', () => { + test('TS and JS', async () => { const input = [ /*0*/'', /*1*/'', @@ -194,13 +195,13 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 11, 1, 'function.declaration'), t(3, 13, 1, 'typeParameter.declaration'), t(3, 16, 2, 'parameter.declaration'), t(3, 20, 1, 'typeParameter'), t(3, 24, 1, 'typeParameter'), t(3, 39, 2, 'parameter'), t(6, 2, 6, 'variable'), t(6, 9, 5, 'member') ]); }); - test('Ranges', () => { + test('Ranges', async () => { const input = [ /*0*/'', /*1*/'', @@ -213,11 +214,11 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 2, 6, 'variable'), t(3, 9, 5, 'member') ], [Range.create(Position.create(2, 0), Position.create(4, 0))]); - assertTokens(input, [ + await assertTokens(input, [ t(6, 2, 6, 'variable'), ], [Range.create(Position.create(6, 2), Position.create(6, 8))]); }); diff --git a/extensions/html-language-features/server/src/utils/documentContext.ts b/extensions/html-language-features/server/src/utils/documentContext.ts index 0f728860de8..f8bc67f8b10 100644 --- a/extensions/html-language-features/server/src/utils/documentContext.ts +++ b/extensions/html-language-features/server/src/utils/documentContext.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DocumentContext, WorkspaceFolder } from '../modes/languageModes'; +import { DocumentContext } from 'vscode-css-languageservice'; import { endsWith, startsWith } from '../utils/strings'; -import * as url from 'url'; +import { WorkspaceFolder } from 'vscode-languageserver'; +import { resolvePath } from '../requests'; export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext { function getRootFolder(): string | undefined { @@ -22,20 +23,15 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp } return { - resolveReference: (ref, base = documentUri) => { + resolveReference: (ref: string, base = documentUri) => { if (ref[0] === '/') { // resolve absolute path against the current workspace folder - if (startsWith(base, 'file://')) { - let folderUri = getRootFolder(); - if (folderUri) { - return folderUri + ref.substr(1); - } + let folderUri = getRootFolder(); + if (folderUri) { + return folderUri + ref.substr(1); } } - try { - return url.resolve(base, ref); - } catch { - return ''; - } + base = base.substr(0, base.lastIndexOf('/') + 1); + return resolvePath(base, ref); }, }; } diff --git a/extensions/html-language-features/server/src/utils/runner.ts b/extensions/html-language-features/server/src/utils/runner.ts index 98a7a96f9aa..6bf8a88d0c6 100644 --- a/extensions/html-language-features/server/src/utils/runner.ts +++ b/extensions/html-language-features/server/src/utils/runner.ts @@ -17,7 +17,7 @@ export function formatError(message: string, err: any): string { return message; } -export function runSafeAsync(func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { +export function runSafe(func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { return new Promise>((resolve) => { setImmediate(() => { if (token.isCancellationRequested) { @@ -38,29 +38,7 @@ export function runSafeAsync(func: () => Thenable, errorVal: T, errorMessa }); } -export function runSafe(func: () => T, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { - return new Promise>((resolve) => { - setImmediate(() => { - if (token.isCancellationRequested) { - resolve(cancelValue()); - } else { - try { - let result = func(); - if (token.isCancellationRequested) { - resolve(cancelValue()); - return; - } else { - resolve(result); - } - } catch (e) { - console.error(formatError(errorMessage, e)); - resolve(errorVal); - } - } - }); - }); -} function cancelValue() { return new ResponseError(ErrorCodes.RequestCancelled, 'Request cancelled'); diff --git a/extensions/html-language-features/server/test/index.js b/extensions/html-language-features/server/test/index.js index d177599c624..5f7aa21e58a 100644 --- a/extensions/html-language-features/server/test/index.js +++ b/extensions/html-language-features/server/test/index.js @@ -21,7 +21,7 @@ if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { reporterEnabled: 'spec, mocha-junit-reporter', mochaJunitReporterReporterOptions: { testsuitesTitle: `${suite} ${process.platform}`, - mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) } }; } diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index c42d15d833e..2bb5bba8e08 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -2,20 +2,25 @@ # yarn lockfile v1 -"@types/mocha@2.2.33": - version "2.2.33" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" - integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/mocha@^8.0.3": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.0.3.tgz#51b21b6acb6d1b923bbdc7725c38f9f455166402" + integrity sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg== "@types/node@^12.11.7": version "12.11.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== -ansi-colors@3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-regex@^3.0.0: version "3.0.0" @@ -27,13 +32,29 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -41,11 +62,26 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +array.prototype.map@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.2.tgz#9a4159f416458a23e9483078de1106b2ef68f8ec" + integrity sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.4" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -54,30 +90,51 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -camelcase@^5.0.0: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -chalk@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + ansi-styles "^4.1.0" + supports-color "^7.1.0" charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +chokidar@3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -94,11 +151,23 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -109,10 +178,10 @@ crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -debug@3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" @@ -142,52 +211,104 @@ define-properties@^1.1.2, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== -es-abstract@^1.5.1: - version "1.14.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" - integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== +es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== dependencies: - es-to-primitive "^1.2.0" + es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" object-keys "^1.1.1" - string.prototype.trimleft "^2.0.0" - string.prototype.trimright "^2.0.0" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== +es-abstract@^1.18.0-next.0: + version "1.18.0-next.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" + integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" is-symbol "^1.0.2" -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -find-up@3.0.0, find-up@^3.0.0: +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== @@ -206,6 +327,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -216,22 +342,17 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -glob@7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== dependencies: - 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" + is-glob "^4.0.1" -glob@^7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== +glob@7.1.6, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -245,17 +366,22 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= -has@^1.0.1, has@^1.0.3: +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -280,6 +406,18 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -295,22 +433,69 @@ is-callable@^1.1.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.1.tgz#4d1e21a4f437509d25ce55f8184350771421c96d" + integrity sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg== + is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: - has "^1.0.1" + is-extglob "^2.1.1" + +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-regex@^1.1.0, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + +is-string@^1.0.4, is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== is-symbol@^1.0.2: version "1.0.2" @@ -319,15 +504,33 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -js-yaml@3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== +iterate-iterator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" + integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw== + +iterate-value@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -340,22 +543,24 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash@^4.16.4: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== - -lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: - chalk "^2.0.1" + p-locate "^5.0.0" + +lodash@^4.16.4: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" md5@^2.1.0: version "2.2.1" @@ -378,17 +583,17 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -mkdirp@0.5.1, mkdirp@~0.5.1: +mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -mocha-junit-reporter@^1.23.1: - version "1.23.1" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.1.tgz#ba11519c0b967f404e4123dd69bc4ba022ab0f12" - integrity sha512-qeDvKlZyAH2YJE1vhryvjUQ06t2hcnwwu4k5Ddwn0GQINhgEYFhlGM0DwYCVUHq5cuo32qAW6HDsTHt7zz99Ng== +mocha-junit-reporter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-2.0.0.tgz#3bf990fce7a42c0d2b718f188553a25d9f24b9a2" + integrity sha512-20HoWh2HEfhqmigfXOKUhZQyX23JImskc37ZOhIjBKoBEsb+4cAFRJpAVhFpnvsztLklW/gFVzsrobjLwmX4lA== dependencies: debug "^2.2.0" md5 "^2.1.0" @@ -404,62 +609,56 @@ mocha-multi-reporters@^1.1.7: debug "^3.1.0" lodash "^4.16.4" -mocha@^6.1.4: - version "6.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.1.tgz#da941c99437da9bac412097859ff99543969f94c" - integrity sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A== +mocha@^8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.3.tgz#5e93f873e35dfdd69617ea75f9c68c2ca61c2ac5" + integrity sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw== dependencies: - ansi-colors "3.2.3" + ansi-colors "4.1.1" 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" + chokidar "3.4.2" + debug "4.1.1" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" growl "1.10.5" he "1.2.0" - js-yaml "3.13.1" - log-symbols "2.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" minimatch "3.0.4" - mkdirp "0.5.1" - ms "2.1.1" - node-environment-flags "1.0.5" + ms "2.1.2" object.assign "4.1.0" - strip-json-comments "2.0.1" - supports-color "6.0.0" - which "1.3.1" + promise.allsettled "1.0.2" + serialize-javascript "4.0.0" + strip-json-comments "3.0.1" + supports-color "7.1.0" + which "2.0.2" wide-align "1.1.3" - yargs "13.3.0" - yargs-parser "13.1.1" - yargs-unparser "1.6.0" + workerpool "6.0.0" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.1" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -object-inspect@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== +object-inspect@^1.7.0, object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -476,13 +675,15 @@ object.assign@4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= +object.assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" once@^1.3.0: version "1.4.0" @@ -498,6 +699,13 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -505,6 +713,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -515,11 +730,51 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +picomatch@^2.0.4: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + +picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +promise.allsettled@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" + integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== + dependencies: + array.prototype.map "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + iterate-value "^1.0.0" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -530,10 +785,17 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -semver@^5.7.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +serialize-javascript@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" set-blocking@^2.0.0: version "2.0.0" @@ -562,21 +824,21 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.trimleft@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" - integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" -string.prototype.trimright@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" - integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" strip-ansi@^4.0.0: version "4.0.0" @@ -592,99 +854,101 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-json-comments@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-json-comments@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -supports-color@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" - integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== +supports-color@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== dependencies: - has-flag "^3.0.0" + has-flag "^4.0.0" -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: - has-flag "^3.0.0" + has-flag "^4.0.0" -vscode-css-languageservice@^4.0.3-next.24: - version "4.0.3-next.24" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.24.tgz#6190fb5af4621b7efc1a0772c1a178d996ffb42d" - integrity sha512-7CPBmaQnvkgL7MXZK9vt5R5CwFaQ9TCqWt9aCi9dGm1g0uAgFOwk8+K5+fV2pasI+aoOvb/kLcFjxSSRD03KdA== +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: - vscode-languageserver-textdocument "^1.0.0-next.4" - vscode-languageserver-types "^3.15.0-next.6" - vscode-nls "^4.1.1" - vscode-uri "^2.1.1" + is-number "^7.0.0" -vscode-html-languageservice@^3.0.4-next.11: - version "3.0.4-next.11" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.11.tgz#4bbc30af204b72a29f23334fb5785a5eca763cae" - integrity sha512-Uj7+WM60y3ptnYxuyWTuy03egAVXHUhN0y7vcGW+0EFAF/MXfRhVYZNQdQAK5BVhA/RjJ8bSfEPWXEzpWShY+w== +vscode-css-languageservice@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.5.tgz#92f8817057dee7c381df2289aad539c7b553548a" + integrity sha512-g9Pjxt9T32jhY0nTOo7WRFm0As27IfdaAxcFa8c7Rml1ZqBn3XXbkExjzxY7sBWYm7I1Tp4dK6UHXHoUQHGwig== dependencies: - vscode-languageserver-textdocument "^1.0.0-next.4" - vscode-languageserver-types "^3.15.0-next.6" - vscode-nls "^4.1.1" - vscode-uri "^2.0.3" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "3.16.0-next.2" + vscode-nls "^5.0.0" + vscode-uri "^2.1.2" -vscode-jsonrpc@^5.0.0-next.2: - version "5.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" - integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== - -vscode-languageserver-protocol@^3.15.0-next.10: - version "3.15.0-next.10" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.10.tgz#f1382f0c270ae5d0c2c7e552483285fb75810914" - integrity sha512-TmbBhKrBoYNX+/pQGwoXmy2qlOfjGBUhwUGIzQoWpj8qtDzYuLof8bi19rGLZ9sVuSHh3anvIyVpGJEqT0QODQ== +vscode-html-languageservice@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.4.tgz#0316dff77ee38dc176f40560cbf55e4f64f4f433" + integrity sha512-3M+bm+hNvwQcScVe5/ok9BXvctOiGJ4nlOkkFf+WKSDrYNkarZ/RByKOa1/iylbvZxJUPzbeziembWPe/dMvhw== dependencies: - vscode-jsonrpc "^5.0.0-next.2" - vscode-languageserver-types "^3.15.0-next.6" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "3.16.0-next.2" + vscode-nls "^5.0.0" + vscode-uri "^2.1.2" -vscode-languageserver-textdocument@^1.0.0-next.4: - version "1.0.0-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.0-next.4.tgz#8f7afdfe3e81411f57baaa29bb3214d1907160cd" - integrity sha512-LJ5WfoBO54nqinjlLJKnjoo2Im4bIvPJ8bFT7R0C84ZI36iK8M29ddslfe5jUeWNSTtCda7YuKdKsDIq38HpgA== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageserver-types@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.6.tgz#7a990d00c39ad4e744335afb4cc422a3e687ff25" - integrity sha512-+4jfvmZ26oFMSX6EgPRB75PWHoT8pzyWuSSWk0erC4hTzmJq2gWxVLh20bZutZjMmiivawvPshtM3XZhX2SttA== - -vscode-languageserver@^6.0.0-next.3: - version "6.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.0-next.3.tgz#41e2fda6417939792f6a19fc19ecbb2f080e2072" - integrity sha512-Q6T+KwYuoXV9KRHD6x7RfTU13pV0xAX2BtcuvSC/LBCiVAnEIOe7jKZjzya+B9gDvSk4hpfvhPefy5IdQK1mpQ== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-languageserver-protocol "^3.15.0-next.10" + vscode-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" -vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +vscode-languageserver-textdocument@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f" + integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== -vscode-uri@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" - integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw== +vscode-languageserver-types@3.16.0-next.2: + version "3.16.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" + integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-uri@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90" - integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A== +vscode-languageserver@7.0.0-next.3: + version "7.0.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0-next.3.tgz#3833bd09259a4a085baeba90783f1e4d06d81095" + integrity sha512-qSt8eb546iFuoFIN+9MPl4Avru6Iz2/JP0UmS/3djf40ICa31Np/yJ7anX2j0Az5rCzb0fak8oeKwDioGeVOYg== + dependencies: + vscode-languageserver-protocol "3.16.0-next.4" + +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + +vscode-uri@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" + integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== +which@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" @@ -695,6 +959,11 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" +workerpool@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" + integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== + wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -719,27 +988,37 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yargs-parser@13.1.1, yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-unparser@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" - integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== +yargs-parser@^15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" + integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== dependencies: - flat "^4.1.0" - lodash "^4.17.15" - yargs "^13.3.0" + camelcase "^5.0.0" + decamelize "^1.2.0" -yargs@13.3.0, yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== +yargs-unparser@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.1.tgz#bd4b0ee05b4c94d058929c32cb09e3fce71d3c5f" + integrity sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA== + dependencies: + camelcase "^5.3.1" + decamelize "^1.2.0" + flat "^4.1.0" + is-plain-obj "^1.1.0" + yargs "^14.2.3" + +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: cliui "^5.0.0" find-up "^3.0.0" @@ -750,4 +1029,21 @@ yargs@13.3.0, yargs@^13.3.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^13.1.2" + +yargs@^14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + 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 "^15.0.1" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index f3528ff867a..0d4b9507c21 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -45,36 +45,36 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^5.0.0-next.2: - version "5.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" - integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageclient@^6.0.0-next.3: - version "6.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.0-next.3.tgz#41b701d963fc7affc01e9279532a747fcd4f3810" - integrity sha512-SuSaG9xjqkROm4Ie0jQig0CFDuU/WxHERegl3kRsFHDbhMSK4dH45ZeBY5zMWUgZ+LrIrEbwf8qWNlrTRBlUgg== +vscode-languageclient@7.0.0-next.5.1: + version "7.0.0-next.5.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0-next.5.1.tgz#ed93f14e4c2cdccedf15002c7bf8ef9cb638f36c" + integrity sha512-OONvbk3IFpubwF8/Y5uPQaq5J5CEskpeET3SfK4iGlv5OUK+44JawH/SEW5wXuEPpfdMLEMZLuGLU5v5d7N7PQ== dependencies: semver "^6.3.0" - vscode-languageserver-protocol "^3.15.0-next.10" + vscode-languageserver-protocol "3.16.0-next.4" -vscode-languageserver-protocol@^3.15.0-next.10: - version "3.15.0-next.10" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.10.tgz#f1382f0c270ae5d0c2c7e552483285fb75810914" - integrity sha512-TmbBhKrBoYNX+/pQGwoXmy2qlOfjGBUhwUGIzQoWpj8qtDzYuLof8bi19rGLZ9sVuSHh3anvIyVpGJEqT0QODQ== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.0-next.2" - vscode-languageserver-types "^3.15.0-next.6" + vscode-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" -vscode-languageserver-types@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.6.tgz#7a990d00c39ad4e744335afb4cc422a3e687ff25" - integrity sha512-+4jfvmZ26oFMSX6EgPRB75PWHoT8pzyWuSSWk0erC4hTzmJq2gWxVLh20bZutZjMmiivawvPshtM3XZhX2SttA== +vscode-languageserver-types@3.16.0-next.2: + version "3.16.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" + integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +vscode-nls@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== zone.js@0.7.6: version "0.7.6" diff --git a/extensions/html/package.json b/extensions/html/package.json index 065eb26669b..6b1eac2d702 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -20,6 +20,7 @@ ".htm", ".shtml", ".xhtml", + ".xht", ".mdoc", ".jsp", ".asp", diff --git a/extensions/html/test/colorize-results/test-embedding_html.json b/extensions/html/test/colorize-results/test-embedding_html.json index 1931950a02c..cd3af42bf2c 100644 --- a/extensions/html/test/colorize-results/test-embedding_html.json +++ b/extensions/html/test/colorize-results/test-embedding_html.json @@ -34,13 +34,13 @@ }, { "c": "console", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.class.console.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js variable.other.object.js", "r": { - "dark_plus": "support.class: #4EC9B0", - "light_plus": "support.class: #267F99", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.class: #4EC9B0" + "hc_black": "variable: #9CDCFE" } }, { @@ -56,13 +56,13 @@ }, { "c": "log", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.function.console.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { diff --git a/extensions/image-preview/.vscodeignore b/extensions/image-preview/.vscodeignore index 30d948fbc66..bcb886a094d 100644 --- a/extensions/image-preview/.vscodeignore +++ b/extensions/image-preview/.vscodeignore @@ -4,6 +4,7 @@ tsconfig.json out/test/** out/** extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json yarn.lock preview-src/** diff --git a/extensions/image-preview/extension-browser.webpack.config.js b/extensions/image-preview/extension-browser.webpack.config.js new file mode 100644 index 00000000000..9a1bb4d3c8e --- /dev/null +++ b/extensions/image-preview/extension-browser.webpack.config.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts' + }, +}); diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index 394511aa8ef..d344b99fbaf 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -279,6 +279,9 @@ image.classList.add('scale-to-fit'); image.addEventListener('load', () => { + if (hasLoadedImage) { + return; + } hasLoadedImage = true; vscode.postMessage({ @@ -297,7 +300,11 @@ } }); - image.addEventListener('error', () => { + image.addEventListener('error', e => { + if (hasLoadedImage) { + return; + } + hasLoadedImage = true; document.body.classList.add('error'); document.body.classList.remove('loading'); diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index a0664f98e3d..064ada94858 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -2,7 +2,11 @@ "name": "image-preview", "displayName": "%displayName%", "description": "%description%", - "extensionKind": ["ui", "workspace"], + "extensionKind": [ + "ui", + "workspace", + "web" + ], "version": "1.0.0", "publisher": "vscode", "icon": "icon.png", @@ -13,19 +17,20 @@ "vscode": "^1.39.0" }, "main": "./out/extension", + "browser": "./dist/browser/extension.js", "categories": [ "Other" ], "activationEvents": [ - "onWebviewEditor:imagePreview.previewEditor", + "onCustomEditor:imagePreview.previewEditor", "onCommand:imagePreview.zoomIn", "onCommand:imagePreview.zoomOut" ], "contributes": { - "webviewEditors": [ + "customEditors": [ { "viewType": "imagePreview.previewEditor", - "displayName": "%webviewEditors.displayName%", + "displayName": "%customEditors.displayName%", "priority": "builtin", "selector": [ { @@ -65,7 +70,9 @@ "compile": "gulp compile-extension:image-preview", "watch": "npm run build-preview && gulp watch-extension:image-preview", "vscode:prepublish": "npm run build-ext", - "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json" + "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json", + "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", + "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { "vscode-extension-telemetry": "0.1.1", diff --git a/extensions/image-preview/package.nls.json b/extensions/image-preview/package.nls.json index 304b1df9a3d..d1860bc2fb5 100644 --- a/extensions/image-preview/package.nls.json +++ b/extensions/image-preview/package.nls.json @@ -1,7 +1,7 @@ { "displayName": "Image Preview", "description": "Provides VS Code's built-in image preview", - "webviewEditors.displayName": "Image Preview", + "customEditors.displayName": "Image Preview", "command.zoomIn": "Zoom in", "command.zoomOut": "Zoom out" } diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index dc0d5c33956..10722360dd5 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -4,14 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { PreviewManager } from './preview'; import { SizeStatusBarEntry } from './sizeStatusBarEntry'; -import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { ZoomStatusBarEntry } from './zoomStatusBarEntry'; export function activate(context: vscode.ExtensionContext) { - const extensionRoot = vscode.Uri.file(context.extensionPath); - const sizeStatusBarEntry = new SizeStatusBarEntry(); context.subscriptions.push(sizeStatusBarEntry); @@ -21,9 +19,11 @@ export function activate(context: vscode.ExtensionContext) { const zoomStatusBarEntry = new ZoomStatusBarEntry(); context.subscriptions.push(zoomStatusBarEntry); - const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); + const previewManager = new PreviewManager(context.extensionUri, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); - context.subscriptions.push(vscode.window.registerWebviewCustomEditorProvider(PreviewManager.viewType, previewManager)); + context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager, { + supportsMultipleEditorsPerDocument: true, + })); context.subscriptions.push(vscode.commands.registerCommand('imagePreview.zoomIn', () => { previewManager.activePreview?.zoomIn(); @@ -33,4 +33,3 @@ export function activate(context: vscode.ExtensionContext) { previewManager.activePreview?.zoomOut(); })); } - diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 5e733b13cc2..f4b589e0fb8 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -13,7 +13,7 @@ import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; const localize = nls.loadMessageBundle(); -export class PreviewManager implements vscode.WebviewCustomEditorProvider { +export class PreviewManager implements vscode.CustomReadonlyEditorProvider { public static readonly viewType = 'imagePreview.previewEditor'; @@ -27,11 +27,15 @@ export class PreviewManager implements vscode.WebviewCustomEditorProvider { private readonly zoomStatusBarEntry: ZoomStatusBarEntry, ) { } - public async resolveWebviewEditor( - resource: vscode.Uri, + public async openCustomDocument(uri: vscode.Uri) { + return { uri, dispose: () => { } }; + } + + public async resolveCustomEditor( + document: vscode.CustomDocument, webviewEditor: vscode.WebviewPanel, ): Promise { - const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry); + const preview = new Preview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry); this._previews.add(preview); this.setActivePreview(preview); @@ -175,7 +179,7 @@ class Preview extends Disposable { private async render() { if (this._previewState !== PreviewState.Disposed) { - this.webviewEditor.webview.html = await this.getWebiewContents(); + this.webviewEditor.webview.html = await this.getWebviewContents(); } } @@ -199,7 +203,7 @@ class Preview extends Disposable { } } - private async getWebiewContents(): Promise { + private async getWebviewContents(): Promise { const version = Date.now().toString(); const settings = { isMac: process.platform === 'darwin', @@ -245,9 +249,9 @@ class Preview extends Disposable { // Avoid adding cache busting if there is already a query string if (resource.query) { - return webviewEditor.webview.asWebviewUri(resource).toString(true); + return webviewEditor.webview.asWebviewUri(resource).toString(); } - return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true); + return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(); } private extensionResource(path: string) { diff --git a/extensions/ini/package.json b/extensions/ini/package.json index cc5ca5da606..24d86072749 100644 --- a/extensions/ini/package.json +++ b/extensions/ini/package.json @@ -18,8 +18,8 @@ }, { "id": "properties", - "extensions": [ ".properties", ".cfg", ".conf", ".directory" ], - "filenames": [ ".gitattributes", ".gitconfig", "gitconfig", ".gitmodules", ".editorconfig" ], + "extensions": [ ".properties", ".cfg", ".conf", ".directory", ".gitattributes", ".gitconfig", ".gitmodules", ".editorconfig" ], + "filenames": [ "gitconfig" ], "filenamePatterns": [ "**/.config/git/config", "**/.git/config" ], "aliases": [ "Properties", "properties" ], "configuration": "./properties.language-configuration.json" diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index b8c6d5e7344..93fa1aa285b 100644 --- a/extensions/jake/src/main.ts +++ b/extensions/jake/src/main.ts @@ -269,7 +269,7 @@ class TaskDetector { private updateProvider(): void { if (!this.taskProvider && this.detectors.size > 0) { const thisCapture = this; - this.taskProvider = vscode.workspace.registerTaskProvider('jake', { + this.taskProvider = vscode.tasks.registerTaskProvider('jake', { provideTasks(): Promise { return thisCapture.getTasks(); }, diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json index b7090d4a45c..88521b95258 100644 --- a/extensions/java/cgmanifest.json +++ b/extensions/java/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-java", "repositoryUrl": "https://github.com/atom/language-java", - "commitHash": "0facf7cbe02cda460db1160fd730f2e57bf15c36" + "commitHash": "2bd3e55a72b08e171f811a2445343e2df9d89b71" } }, "license": "MIT", - "version": "0.31.4" + "version": "0.32.0" } ], "version": 1 diff --git a/extensions/java/package.json b/extensions/java/package.json index 9d3310681b9..96f8c8c4adf 100644 --- a/extensions/java/package.json +++ b/extensions/java/package.json @@ -23,7 +23,7 @@ }], "snippets": [{ "language": "java", - "path": "./snippets/java.snippets.json" + "path": "./snippets/java.code-snippets" }] } -} \ No newline at end of file +} diff --git a/extensions/java/snippets/java.snippets.json b/extensions/java/snippets/java.code-snippets similarity index 100% rename from extensions/java/snippets/java.snippets.json rename to extensions/java/snippets/java.code-snippets diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index 7dad7d72837..91716ded5f8 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-java/commit/0facf7cbe02cda460db1160fd730f2e57bf15c36", + "version": "https://github.com/atom/language-java/commit/2bd3e55a72b08e171f811a2445343e2df9d89b71", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -40,7 +40,7 @@ "name": "invalid.deprecated.package_name_not_lowercase.java" }, { - "match": "(?x)\\b(?)?(\\()", + "beginCaptures": { + "1": { + "name": "storage.modifier.java" + }, + "2": { + "name": "entity.name.type.record.java" + }, + "3": { + "patterns": [ + { + "include": "#generics" + } + ] + }, + "4": { + "name": "punctuation.definition.parameters.begin.bracket.round.java" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.end.bracket.round.java" + } + }, + "name": "meta.record.identifier.java", + "patterns": [ + { + "include": "#code" + } + ] + }, + { + "begin": "(implements)\\s", + "beginCaptures": { + "1": { + "name": "storage.modifier.implements.java" + } + }, + "end": "(?=\\s*\\{)", + "name": "meta.definition.class.implemented.interfaces.java", + "patterns": [ + { + "include": "#object-types-inherited" + }, + { + "include": "#comments" + } + ] + }, + { + "include": "#record-body" + } + ] + }, + "record-body": { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.class.begin.bracket.curly.java" + } + }, + "end": "(?=})", + "name": "meta.record.body.java", + "patterns": [ + { + "include": "#record-constructor" + }, + { + "include": "#class-body" + } + ] + }, + "record-constructor": { + "begin": "(?!new)(?=[\\w<].*\\s+)(?=([^\\(=/]|/(?!/))+(?={))", + "end": "(})|(?=;)", + "endCaptures": { + "1": { + "name": "punctuation.section.method.end.bracket.curly.java" + } + }, + "name": "meta.method.java", + "patterns": [ + { + "include": "#storage-modifiers" + }, + { + "begin": "(\\w+)", + "beginCaptures": { + "1": { + "name": "entity.name.function.java" + } + }, + "end": "(?=\\s*{)", + "name": "meta.method.identifier.java", + "patterns": [ + { + "include": "#comments" + } + ] + }, + { + "include": "#comments" + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.method.begin.bracket.curly.java" + } + }, + "end": "(?=})", + "contentName": "meta.method.body.java", + "patterns": [ + { + "include": "#code" + } + ] + } + ] + }, "static-initializer": { "patterns": [ { @@ -1360,7 +1560,7 @@ ] }, "storage-modifiers": { - "match": "\\b(public|private|protected|static|final|native|synchronized|abstract|threadsafe|transient|volatile|default|strictfp)\\b", + "match": "\\b(public|private|protected|static|final|native|synchronized|abstract|threadsafe|transient|volatile|default|strictfp|sealed|non-sealed)\\b", "name": "storage.modifier.java" }, "strings": { @@ -1603,7 +1803,7 @@ ] }, "variables": { - "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z_]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n \\s*\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n \\s*\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", + "begin": "(?x)\n(?=\n \\b\n (\n (void|boolean|byte|char|short|int|float|long|double)\n |\n (?>(\\w+\\.)*[A-Z_]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n \\b\n \\s*\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n \\s*\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", "end": "(?=\\=|:|;)", "name": "meta.definition.variable.java", "patterns": [ diff --git a/extensions/javascript/cgmanifest.json b/extensions/javascript/cgmanifest.json index 7053443efe1..2d5d904f01c 100644 --- a/extensions/javascript/cgmanifest.json +++ b/extensions/javascript/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "Microsoft/TypeScript-TmLanguage", - "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", + "name": "microsoft/TypeScript-TmLanguage", + "repositoryUrl": "https://github.com/microsoft/TypeScript-TmLanguage", "commitHash": "3133e3d914db9a2bb8812119f9273727a305f16b" } }, "license": "MIT", "version": "0.0.1", - "description": "The file syntaxes/JavaScript.tmLanguage.json was derived from TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage." + "description": "The file syntaxes/JavaScript.tmLanguage.json was derived from TypeScriptReact.tmLanguage in https://github.com/microsoft/TypeScript-TmLanguage." }, { "component": { diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index b7303520549..d63bebded16 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -90,14 +90,42 @@ "path": "./syntaxes/Regular Expressions (JavaScript).tmLanguage" } ], - "snippets": [ + "semanticTokenScopes": [ { "language": "javascript", - "path": "./snippets/javascript.json" + "scopes": { + "property": ["variable.other.property.js"], + "property.readonly": ["variable.other.constant.property.js"], + "variable": ["variable.other.readwrite.js"], + "variable.readonly": ["variable.other.constant.object.js"], + "function": ["entity.name.function.js"], + "namespace": ["entity.name.type.module.js"], + "variable.defaultLibrary": ["support.variable.js"], + "function.defaultLibrary": ["support.function.js"] + } }, { "language": "javascriptreact", - "path": "./snippets/javascript.json" + "scopes": { + "property": ["variable.other.property.jsx"], + "property.readonly": ["variable.other.constant.property.jsx"], + "variable": ["variable.other.readwrite.jsx"], + "variable.readonly": ["variable.other.constant.object.jsx"], + "function": ["entity.name.function.jsx"], + "namespace": ["entity.name.type.module.jsx"], + "variable.defaultLibrary": ["support.variable.js"], + "function.defaultLibrary": ["support.function.js"] + } + } + ], + "snippets": [ + { + "language": "javascript", + "path": "./snippets/javascript.code-snippets" + }, + { + "language": "javascriptreact", + "path": "./snippets/javascript.code-snippets" } ] } diff --git a/extensions/javascript/snippets/javascript.json b/extensions/javascript/snippets/javascript.code-snippets similarity index 97% rename from extensions/javascript/snippets/javascript.json rename to extensions/javascript/snippets/javascript.code-snippets index 5da4ebe0c18..b005c80c844 100644 --- a/extensions/javascript/snippets/javascript.json +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -173,24 +173,21 @@ "Log to the console": { "prefix": "log", "body": [ - "console.log($1);", - "$0" + "console.log($1);" ], "description": "Log to the console" }, "Log warning to console": { "prefix": "warn", "body": [ - "console.warn($1);", - "$0" + "console.warn($1);" ], "description": "Log warning to the console" }, "Log error to console": { "prefix": "error", "body": [ - "console.error($1);", - "$0" + "console.error($1);" ], "description": "Log error to the console" } diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index c18fb6cfef7..9a991cde03d 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage", + "This file has been converted from https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -15,16 +15,19 @@ "include": "#statements" }, { - "name": "comment.line.shebang.ts", - "match": "\\A(#!).*(?=$)", - "captures": { - "1": { - "name": "punctuation.definition.comment.ts" - } - } + "include": "#shebang" } ], "repository": { + "shebang": { + "name": "comment.line.shebang.js", + "match": "\\A(#!).*(?=$)", + "captures": { + "1": { + "name": "punctuation.definition.comment.js" + } + } + }, "statements": { "patterns": [ { @@ -426,7 +429,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -484,7 +487,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js variable.other.constant.js entity.name.function.js" @@ -868,7 +871,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -1108,7 +1111,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js entity.name.function.js" @@ -1431,7 +1434,7 @@ }, { "name": "meta.arrow.js", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2625,7 +2628,7 @@ }, { "name": "meta.object.member.js", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2688,7 +2691,7 @@ "name": "keyword.control.as.js" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.js", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\())", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\())", "patterns": [ { - "include": "#support-function-call-identifiers" + "name": "meta.function-call.js", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\())", + "patterns": [ + { + "include": "#function-call-target" + } + ] }, { - "name": "entity.name.function.js", - "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + }, + { + "include": "#paren-expression" } ] }, { - "include": "#comment" + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "end": "(?<=\\>)(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "name": "meta.function-call.js", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "include": "#function-call-target" + } + ] + }, + { + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + } + ] + } + ] + }, + "function-call-target": { + "patterns": [ + { + "include": "#support-function-call-identifiers" }, + { + "name": "entity.name.function.js", + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + "function-call-optionals": { + "patterns": [ { "name": "meta.function-call.js punctuation.accessor.optional.js", "match": "\\?\\." @@ -2863,12 +2915,6 @@ { "name": "meta.function-call.js keyword.operator.definiteassignment.js", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2900,7 +2946,7 @@ "name": "keyword.operator.new.js" } }, - "end": "(?<=\\))|(?=[;),}\\]:\\-\\+]|\\|\\||\\&\\&|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|(([\\&\\~\\^\\|]\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s+instanceof(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -3178,7 +3224,7 @@ "name": "keyword.control.as.js" } }, - "end": "(?=$|^|[;,:})\\]]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?=|<>|<|>" }, { - "match": "(\\!)\\s*(/)(?![/*])", + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", "captures": { "1": { "name": "keyword.operator.logical.js" @@ -3486,23 +3532,6 @@ } } }, - { - "match": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -4021,6 +4022,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.js", + "match": "\\.\\.\\." + }, + { + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", "end": "(?=`)", "patterns": [ { "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "end": "(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", "patterns": [ { "include": "#support-function-call-identifiers" @@ -4565,7 +4588,7 @@ }, { "name": "string.template.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js" @@ -5714,4 +5737,4 @@ "match": "\\S+" } } -} \ No newline at end of file +} diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 29cd81e4ab3..2ec5ce9d225 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage", + "This file has been converted from https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -15,16 +15,19 @@ "include": "#statements" }, { - "name": "comment.line.shebang.ts", - "match": "\\A(#!).*(?=$)", - "captures": { - "1": { - "name": "punctuation.definition.comment.ts" - } - } + "include": "#shebang" } ], "repository": { + "shebang": { + "name": "comment.line.shebang.js.jsx", + "match": "\\A(#!).*(?=$)", + "captures": { + "1": { + "name": "punctuation.definition.comment.js.jsx" + } + } + }, "statements": { "patterns": [ { @@ -426,7 +429,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" @@ -484,7 +487,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx variable.other.constant.js.jsx entity.name.function.js.jsx" @@ -868,7 +871,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -1108,7 +1111,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx" @@ -1431,7 +1434,7 @@ }, { "name": "meta.arrow.js.jsx", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2625,7 +2628,7 @@ }, { "name": "meta.object.member.js.jsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js.jsx" @@ -2688,7 +2691,7 @@ "name": "keyword.control.as.js.jsx" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.js.jsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\())", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\())", "patterns": [ { - "include": "#support-function-call-identifiers" + "name": "meta.function-call.js.jsx", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\())", + "patterns": [ + { + "include": "#function-call-target" + } + ] }, { - "name": "entity.name.function.js.jsx", - "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + }, + { + "include": "#paren-expression" } ] }, { - "include": "#comment" + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "end": "(?<=\\>)(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "name": "meta.function-call.js.jsx", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "include": "#function-call-target" + } + ] + }, + { + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + } + ] + } + ] + }, + "function-call-target": { + "patterns": [ + { + "include": "#support-function-call-identifiers" }, + { + "name": "entity.name.function.js.jsx", + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + "function-call-optionals": { + "patterns": [ { "name": "meta.function-call.js.jsx punctuation.accessor.optional.js.jsx", "match": "\\?\\." @@ -2863,12 +2915,6 @@ { "name": "meta.function-call.js.jsx keyword.operator.definiteassignment.js.jsx", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2900,7 +2946,7 @@ "name": "keyword.operator.new.js.jsx" } }, - "end": "(?<=\\))|(?=[;),}\\]:\\-\\+]|\\|\\||\\&\\&|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|(([\\&\\~\\^\\|]\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s+instanceof(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -3178,7 +3224,7 @@ "name": "keyword.control.as.js.jsx" } }, - "end": "(?=$|^|[;,:})\\]]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?=|<>|<|>" }, { - "match": "(\\!)\\s*(/)(?![/*])", + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", "captures": { "1": { "name": "keyword.operator.logical.js.jsx" @@ -3486,23 +3532,6 @@ } } }, - { - "match": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -4021,6 +4022,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.js.jsx", + "match": "\\.\\.\\." + }, + { + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", "end": "(?=`)", "patterns": [ { "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "end": "(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", "patterns": [ { "include": "#support-function-call-identifiers" @@ -4565,7 +4588,7 @@ }, { "name": "string.template.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js.jsx" @@ -5714,4 +5737,4 @@ "match": "\\S+" } } -} \ No newline at end of file +} diff --git a/extensions/javascript/syntaxes/Readme.md b/extensions/javascript/syntaxes/Readme.md index 3457a1f6334..bc29199fd73 100644 --- a/extensions/javascript/syntaxes/Readme.md +++ b/extensions/javascript/syntaxes/Readme.md @@ -1,4 +1,4 @@ -The file `JavaScript.tmLanguage.json` is derived from [TypeScriptReact.tmLanguage](https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage). +The file `JavaScript.tmLanguage.json` is derived from [TypeScriptReact.tmLanguage](https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage). To update to the latest version: - `cd extensions/typescript` and run `npm run update-grammars` diff --git a/extensions/json-language-features/.vscodeignore b/extensions/json-language-features/.vscodeignore index 3011e9dcd0a..4b7f857a0e6 100644 --- a/extensions/json-language-features/.vscodeignore +++ b/extensions/json-language-features/.vscodeignore @@ -15,4 +15,6 @@ server/.npmignore yarn.lock CONTRIBUTING.md server/extension.webpack.config.js -extension.webpack.config.js \ No newline at end of file +extension.webpack.config.js +server/extension-browser.webpack.config.js +extension-browser.webpack.config.js diff --git a/extensions/json-language-features/CONTRIBUTING.md b/extensions/json-language-features/CONTRIBUTING.md index 90367dec71e..7203d02e6f2 100644 --- a/extensions/json-language-features/CONTRIBUTING.md +++ b/extensions/json-language-features/CONTRIBUTING.md @@ -1,12 +1,12 @@ ## Setup -- Clone [Microsoft/vscode](https://github.com/microsoft/vscode) +- Clone [microsoft/vscode](https://github.com/microsoft/vscode) - Run `yarn` at `/`, this will install - Dependencies for `/extension/json-language-features/` - Dependencies for `/extension/json-language-features/server/` - devDependencies such as `gulp` - Open `/extensions/json-language-features/` as the workspace in VS Code -- Run the [`Launch Extension`](https://github.com/Microsoft/vscode/blob/master/extensions/json-language-features/.vscode/launch.json) debug target in the Debug View. This will: +- Run the [`Launch Extension`](https://github.com/microsoft/vscode/blob/master/extensions/json-language-features/.vscode/launch.json) debug target in the Debug View. This will: - Launch the `preLaunchTask` task to compile the extension - Launch a new VS Code instance with the `json-language-features` extension loaded - You should see a notification saying the development version of `json-language-features` overwrites the bundled version of `json-language-features` @@ -18,15 +18,15 @@ ### Contribute to vscode-json-languageservice -[Microsoft/vscode-json-languageservice](https://github.com/Microsoft/vscode-json-languageservice) is the library that implements the language smarts for JSON. +[microsoft/vscode-json-languageservice](https://github.com/microsoft/vscode-json-languageservice) is the library that implements the language smarts for JSON. The JSON language server forwards most the of requests to the service library. -If you want to fix JSON issues or make improvements, you should make changes at [Microsoft/vscode-json-languageservice](https://github.com/Microsoft/vscode-json-languageservice). +If you want to fix JSON issues or make improvements, you should make changes at [microsoft/vscode-json-languageservice](https://github.com/microsoft/vscode-json-languageservice). However, within this extension, you can run a development version of `vscode-json-languageservice` to debug code or test language features interactively: #### Linking `vscode-json-languageservice` in `json-language-features/server/` -- Clone [Microsoft/vscode-json-languageservice](https://github.com/Microsoft/vscode-json-languageservice) +- Clone [microsoft/vscode-json-languageservice](https://github.com/microsoft/vscode-json-languageservice) - Run `npm install` in `vscode-json-languageservice` - Run `npm link` in `vscode-json-languageservice`. This will compile and link `vscode-json-languageservice` - In `json-language-features/server/`, run `yarn link vscode-json-languageservice` @@ -36,4 +36,4 @@ However, within this extension, you can run a development version of `vscode-jso - Open both `vscode-json-languageservice` and this extension in a single workspace with [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature - Run `yarn watch` at `json-languagefeatures/server/` to recompile this extension with the linked version of `vscode-json-languageservice` - Make some changes in `vscode-json-languageservice` -- Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-json-languageservice`. You can interactively test the language features. \ No newline at end of file +- Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-json-languageservice`. You can interactively test the language features. diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts new file mode 100644 index 00000000000..6389dafb5ba --- /dev/null +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionContext, Uri } from 'vscode'; +import { LanguageClientOptions } from 'vscode-languageclient'; +import { startClient, LanguageClientConstructor } from '../jsonClient'; +import { LanguageClient } from 'vscode-languageclient/browser'; +import { RequestService } from '../requests'; + +declare const Worker: { + new(stringUrl: string): any; +}; + +declare function fetch(uri: string, options: any): any; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/jsonServerMain.js'); + try { + const worker = new Worker(serverMain.toString()); + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, clientOptions, worker); + }; + + const http: RequestService = { + getContent(uri: string) { + return fetch(uri, { mode: 'cors' }) + .then(function (response: any) { + return response.text(); + }); + } + }; + startClient(context, newLanguageClient, { http }); + + } catch (e) { + console.log(e); + } +} diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts new file mode 100644 index 00000000000..18aec8ef32b --- /dev/null +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -0,0 +1,509 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +import { + workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, + Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, +} from 'vscode'; +import { + LanguageClientOptions, RequestType, NotificationType, + DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient +} from 'vscode-languageclient'; + +import { hash } from './utils/hash'; +import { RequestService, joinPath } from './requests'; + +namespace VSCodeContentRequest { + export const type: RequestType = new RequestType('vscode/content'); +} + +namespace SchemaContentChangeNotification { + export const type: NotificationType = new NotificationType('json/schemaContent'); +} + +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + +export interface ISchemaAssociations { + [pattern: string]: string[]; +} + +export interface ISchemaAssociation { + fileMatch: string[]; + uri: string; +} + +namespace SchemaAssociationNotification { + export const type: NotificationType = new NotificationType('json/schemaAssociations'); +} + +namespace ResultLimitReachedNotification { + export const type: NotificationType = new NotificationType('json/resultLimitReached'); +} + +interface Settings { + json?: { + schemas?: JSONSchemaSettings[]; + format?: { enable: boolean; }; + resultLimit?: number; + }; + http?: { + proxy?: string; + proxyStrictSSL?: boolean; + }; +} + +interface JSONSchemaSettings { + fileMatch?: string[]; + url?: string; + schema?: any; +} + +namespace SettingIds { + export const enableFormatter = 'json.format.enable'; + export const enableSchemaDownload = 'json.schemaDownload.enable'; + export const maxItemsComputed = 'json.maxItemsComputed'; +} + +export interface TelemetryReporter { + sendTelemetryEvent(eventName: string, properties?: { + [key: string]: string; + }, measurements?: { + [key: string]: number; + }): void; +} + +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; + +export interface Runtime { + http: RequestService; + telemetry?: TelemetryReporter +} + +export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { + + const toDispose = context.subscriptions; + + let rangeFormatting: Disposable | undefined = undefined; + + + const documentSelector = ['json', 'jsonc']; + + const schemaResolutionErrorStatusBarItem = window.createStatusBarItem({ + id: 'status.json.resolveError', + name: localize('json.resolveError', "JSON: Schema Resolution Error"), + alignment: StatusBarAlignment.Right, + priority: 0, + }); + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + toDispose.push(schemaResolutionErrorStatusBarItem); + + const fileSchemaErrors = new Map(); + let schemaDownloadEnabled = true; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for json documents + documentSelector, + initializationOptions: { + handledSchemaProtocols: ['file'], // language server only loads file-URI. Fetching schemas with other protocols ('http'...) are made on the client. + provideFormatter: false, // tell the server to not provide formatting capability and ignore the `json.format.enable` setting. + customCapabilities: { rangeFormatting: { editLimit: 10000 } } + }, + synchronize: { + // Synchronize the setting section 'json' to the server + configurationSection: ['json', 'http'], + fileEvents: workspace.createFileSystemWatcher('**/*.json') + }, + middleware: { + workspace: { + didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() }) + }, + handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { + const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); + + if (schemaErrorIndex === -1) { + fileSchemaErrors.delete(uri.toString()); + return next(uri, diagnostics); + } + + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(uri.toString(), schemaResolveDiagnostic.message); + + if (!schemaDownloadEnabled) { + diagnostics = diagnostics.filter(d => !isSchemaResolveError(d)); + } + + if (window.activeTextEditor && window.activeTextEditor.document.uri.toString() === uri.toString()) { + schemaResolutionErrorStatusBarItem.show(); + } + + next(uri, diagnostics); + }, + // testing the replace / insert mode + provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { + function update(item: CompletionItem) { + const range = item.range; + if (range instanceof Range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) { + item.range = { inserting: new Range(range.start, position), replacing: range }; + } + if (item.documentation instanceof MarkdownString) { + item.documentation = updateMarkdownString(item.documentation); + } + + } + function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined { + if (r) { + (Array.isArray(r) ? r : r.items).forEach(update); + } + return r; + } + + const r = next(document, position, context, token); + if (isThenable(r)) { + return r.then(updateProposals); + } + return updateProposals(r); + }, + provideHover(document: TextDocument, position: Position, token: CancellationToken, next: ProvideHoverSignature) { + function updateHover(r: Hover | null | undefined): Hover | null | undefined { + if (r && Array.isArray(r.contents)) { + r.contents = r.contents.map(h => h instanceof MarkdownString ? updateMarkdownString(h) : h); + } + return r; + } + const r = next(document, position, token); + if (isThenable(r)) { + return r.then(updateHover); + } + return updateHover(r); + } + } + }; + + // Create the language client and start the client. + const client = newLanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), clientOptions); + client.registerProposedFeatures(); + + const disposable = client.start(); + toDispose.push(disposable); + client.onReady().then(() => { + const schemaDocuments: { [uri: string]: boolean } = {}; + + // handle content request + client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { + const uri = Uri.parse(uriPath); + if (uri.scheme === 'untitled') { + return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString()))); + } + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return workspace.openTextDocument(uri).then(doc => { + schemaDocuments[uri.toString()] = true; + return doc.getText(); + }, error => { + return Promise.reject(new ResponseError(2, error.toString())); + }); + } else if (schemaDownloadEnabled) { + if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { + /* __GDPR__ + "json.schema" : { + "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); + } + return runtime.http.getContent(uriPath); + } else { + return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); + } + }); + + const handleContentChange = (uriString: string) => { + if (schemaDocuments[uriString]) { + client.sendNotification(SchemaContentChangeNotification.type, uriString); + return true; + } + return false; + }; + + const handleActiveEditorChange = (activeEditor?: TextEditor) => { + if (!activeEditor) { + return; + } + + const activeDocUri = activeEditor.document.uri.toString(); + + if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { + schemaResolutionErrorStatusBarItem.show(); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + }; + + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); + toDispose.push(workspace.onDidCloseTextDocument(d => { + const uriString = d.uri.toString(); + if (handleContentChange(uriString)) { + delete schemaDocuments[uriString]; + } + fileSchemaErrors.delete(uriString); + })); + toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); + + const handleRetryResolveSchemaCommand = () => { + if (window.activeTextEditor) { + schemaResolutionErrorStatusBarItem.text = '$(watch)'; + const activeDocUri = window.activeTextEditor.document.uri.toString(); + client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { + const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); + if (schemaErrorIndex !== -1) { + // Show schema resolution errors in status bar only; ref: #51032 + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + }); + } + }; + + toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); + + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + + extensions.onDidChange(_ => { + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + }); + + // manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652. + updateFormatterRegistration(); + toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); + + updateSchemaDownloadSetting(); + + toDispose.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(SettingIds.enableFormatter)) { + updateFormatterRegistration(); + } else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) { + updateSchemaDownloadSetting(); + } + })); + + client.onNotification(ResultLimitReachedNotification.type, message => { + window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`); + }); + + function updateFormatterRegistration() { + const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter); + if (!formatEnabled && rangeFormatting) { + rangeFormatting.dispose(); + rangeFormatting = undefined; + } else if (formatEnabled && !rangeFormatting) { + rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { + provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { + const params: DocumentRangeFormattingParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + range: client.code2ProtocolConverter.asRange(range), + options: client.code2ProtocolConverter.asFormattingOptions(options) + }; + return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( + client.protocol2CodeConverter.asTextEdits, + (error) => { + client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []); + return Promise.resolve([]); + } + ); + } + }); + } + } + + function updateSchemaDownloadSetting() { + schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false; + if (schemaDownloadEnabled) { + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.'); + schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; + handleRetryResolveSchemaCommand(); + } else { + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.'); + schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' }; + } + } + + }); + + const languageConfiguration: LanguageConfiguration = { + wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, + indentationRules: { + increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/, + decreaseIndentPattern: /^\s*[}\]],?\s*$/ + } + }; + languages.setLanguageConfiguration('json', languageConfiguration); + languages.setLanguageConfiguration('jsonc', languageConfiguration); + +} + +function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] { + const associations: ISchemaAssociation[] = []; + extensions.all.forEach(extension => { + const packageJSON = extension.packageJSON; + if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) { + const jsonValidation = packageJSON.contributes.jsonValidation; + if (Array.isArray(jsonValidation)) { + jsonValidation.forEach(jv => { + let { fileMatch, url } = jv; + if (typeof fileMatch === 'string') { + fileMatch = [fileMatch]; + } + if (Array.isArray(fileMatch) && typeof url === 'string') { + let uri: string = url; + if (uri[0] === '.' && uri[1] === '/') { + uri = joinPath(extension.extensionUri, uri).toString(); + } + fileMatch = fileMatch.map(fm => { + if (fm[0] === '%') { + fm = fm.replace(/%APP_SETTINGS_HOME%/, '/User'); + fm = fm.replace(/%MACHINE_SETTINGS_HOME%/, '/Machine'); + fm = fm.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces'); + } else if (!fm.match(/^(\w+:\/\/|\/|!)/)) { + fm = '/' + fm; + } + return fm; + }); + associations.push({ fileMatch, uri }); + } + }); + } + } + }); + return associations; +} + +function getSettings(): Settings { + const httpSettings = workspace.getConfiguration('http'); + + const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000; + + const settings: Settings = { + http: { + proxy: httpSettings.get('proxy'), + proxyStrictSSL: httpSettings.get('proxyStrictSSL') + }, + json: { + schemas: [], + resultLimit + } + }; + const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null); + const collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], folderUri?: Uri, isMultiRoot?: boolean) => { + + let fileMatchPrefix = undefined; + if (folderUri && isMultiRoot) { + fileMatchPrefix = folderUri.toString(); + if (fileMatchPrefix[fileMatchPrefix.length - 1] === '/') { + fileMatchPrefix = fileMatchPrefix.substr(0, fileMatchPrefix.length - 1); + } + } + for (const setting of schemaSettings) { + const url = getSchemaId(setting, folderUri); + if (!url) { + continue; + } + let schemaSetting = schemaSettingsById[url]; + if (!schemaSetting) { + schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] }; + settings.json!.schemas!.push(schemaSetting); + } + const fileMatches = setting.fileMatch; + if (Array.isArray(fileMatches)) { + const resultingFileMatches = schemaSetting.fileMatch || []; + schemaSetting.fileMatch = resultingFileMatches; + const addMatch = (pattern: string) => { // filter duplicates + if (resultingFileMatches.indexOf(pattern) === -1) { + resultingFileMatches.push(pattern); + } + }; + for (const fileMatch of fileMatches) { + if (fileMatchPrefix) { + if (fileMatch[0] === '/') { + addMatch(fileMatchPrefix + fileMatch); + addMatch(fileMatchPrefix + '/*' + fileMatch); + } else { + addMatch(fileMatchPrefix + '/' + fileMatch); + addMatch(fileMatchPrefix + '/*/' + fileMatch); + } + } else { + addMatch(fileMatch); + } + } + } + if (setting.schema && !schemaSetting.schema) { + schemaSetting.schema = setting.schema; + } + } + }; + + const folders = workspace.workspaceFolders; + + // merge global and folder settings. Qualify all file matches with the folder path. + const globalSettings = workspace.getConfiguration('json', null).get('schemas'); + if (Array.isArray(globalSettings)) { + if (!folders) { + collectSchemaSettings(globalSettings); + } + } + if (folders) { + const isMultiRoot = folders.length > 1; + for (const folder of folders) { + const folderUri = folder.uri; + + const schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect('schemas'); + + const folderSchemas = schemaConfigInfo!.workspaceFolderValue; + if (Array.isArray(folderSchemas)) { + collectSchemaSettings(folderSchemas, folderUri, isMultiRoot); + } + if (Array.isArray(globalSettings)) { + collectSchemaSettings(globalSettings, folderUri, isMultiRoot); + } + + } + } + return settings; +} + +function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri): string | undefined { + let url = schema.url; + if (!url) { + if (schema.schema) { + url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`; + } + } else if (folderUri && (url[0] === '.' || url[0] === '/')) { + url = joinPath(folderUri, url).toString(); + } + return url; +} + +function isThenable(obj: ProviderResult): obj is Thenable { + return obj && (obj)['then']; +} + +function updateMarkdownString(h: MarkdownString): MarkdownString { + const n = new MarkdownString(h.value, true); + n.isTrusted = h.isTrusted; + return n; +} + +function isSchemaResolveError(d: Diagnostic) { + return d.code === /* SchemaResolveError */ 0x300; +} diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts deleted file mode 100644 index b52141b947c..00000000000 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ /dev/null @@ -1,468 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import * as fs from 'fs'; -import * as nls from 'vscode-nls'; -import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light'; - -const localize = nls.loadMessageBundle(); - -import { - workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, - Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext -} from 'vscode'; -import { - LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, - DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature -} from 'vscode-languageclient'; -import TelemetryReporter from 'vscode-extension-telemetry'; - -import { hash } from './utils/hash'; - -namespace VSCodeContentRequest { - export const type: RequestType = new RequestType('vscode/content'); -} - -namespace SchemaContentChangeNotification { - export const type: NotificationType = new NotificationType('json/schemaContent'); -} - -namespace ForceValidateRequest { - export const type: RequestType = new RequestType('json/validate'); -} - -export interface ISchemaAssociations { - [pattern: string]: string[]; -} - -namespace SchemaAssociationNotification { - export const type: NotificationType = new NotificationType('json/schemaAssociations'); -} - -namespace ResultLimitReachedNotification { - export const type: NotificationType = new NotificationType('json/resultLimitReached'); -} - -interface IPackageInfo { - name: string; - version: string; - aiKey: string; -} - -interface Settings { - json?: { - schemas?: JSONSchemaSettings[]; - format?: { enable: boolean; }; - resultLimit?: number; - }; - http?: { - proxy?: string; - proxyStrictSSL?: boolean; - }; -} - -interface JSONSchemaSettings { - fileMatch?: string[]; - url?: string; - schema?: any; -} - -let telemetryReporter: TelemetryReporter | undefined; - -export function activate(context: ExtensionContext) { - - let toDispose = context.subscriptions; - - let rangeFormatting: Disposable | undefined = undefined; - - let packageInfo = getPackageInfo(context); - telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - - let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main; - let serverModule = context.asAbsolutePath(path.join('server', serverMain)); - - // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--inspect=' + (9000 + Math.round(Math.random() * 10000))] }; - - // If the extension is launch in debug mode the debug server options are use - // Otherwise the run options are used - let serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } - }; - - let documentSelector = ['json', 'jsonc']; - - let schemaResolutionErrorStatusBarItem = window.createStatusBarItem({ - id: 'status.json.resolveError', - name: localize('json.resolveError', "JSON: Schema Resolution Error"), - alignment: StatusBarAlignment.Right, - priority: 0 - }); - schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; - schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema.') + ' ' + localize('json.clickToRetry', 'Click to retry.'); - schemaResolutionErrorStatusBarItem.text = '$(alert)'; - toDispose.push(schemaResolutionErrorStatusBarItem); - - let fileSchemaErrors = new Map(); - - // Options to control the language client - let clientOptions: LanguageClientOptions = { - // Register the server for json documents - documentSelector, - initializationOptions: { - handledSchemaProtocols: ['file'], // language server only loads file-URI. Fetching schemas with other protocols ('http'...) are made on the client. - provideFormatter: false, // tell the server to not provide formatting capability and ignore the `json.format.enable` setting. - customCapabilities: { rangeFormatting: { editLimit: 1000 } } - }, - synchronize: { - // Synchronize the setting section 'json' to the server - configurationSection: ['json', 'http'], - fileEvents: workspace.createFileSystemWatcher('**/*.json') - }, - middleware: { - workspace: { - didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() }) - }, - handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { - const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); - - if (schemaErrorIndex === -1) { - fileSchemaErrors.delete(uri.toString()); - return next(uri, diagnostics); - } - - const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; - fileSchemaErrors.set(uri.toString(), schemaResolveDiagnostic.message); - - if (window.activeTextEditor && window.activeTextEditor.document.uri.toString() === uri.toString()) { - schemaResolutionErrorStatusBarItem.show(); - } - - next(uri, diagnostics); - }, - // testing the replace / insert mode - provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { - function updateRanges(item: CompletionItem) { - const range = item.range; - if (range instanceof Range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) { - item.range = { inserting: new Range(range.start, position), replacing: range }; - } - } - function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined { - if (r) { - (Array.isArray(r) ? r : r.items).forEach(updateRanges); - } - return r; - } - const isThenable = (obj: ProviderResult): obj is Thenable => obj && (obj)['then']; - - const r = next(document, position, context, token); - if (isThenable(r)) { - return r.then(updateProposals); - } - return updateProposals(r); - } - } - }; - - // Create the language client and start the client. - let client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions); - client.registerProposedFeatures(); - - let disposable = client.start(); - toDispose.push(disposable); - client.onReady().then(() => { - const schemaDocuments: { [uri: string]: boolean } = {}; - - // handle content request - client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { - let uri = Uri.parse(uriPath); - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return workspace.openTextDocument(uri).then(doc => { - schemaDocuments[uri.toString()] = true; - return doc.getText(); - }, error => { - return Promise.reject(error); - }); - } else { - if (telemetryReporter && uri.authority === 'schema.management.azure.com') { - /* __GDPR__ - "json.schema" : { - "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - telemetryReporter.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); - } - const headers = { 'Accept-Encoding': 'gzip, deflate' }; - return xhr({ url: uriPath, followRedirects: 5, headers }).then(response => { - return response.responseText; - }, (error: XHRResponse) => { - let extraInfo = error.responseText || error.toString(); - if (extraInfo.length > 256) { - extraInfo = `${extraInfo.substr(0, 256)}...`; - } - return Promise.reject(new ResponseError(error.status, getErrorStatusDescription(error.status) + '\n' + extraInfo)); - }); - } - }); - - let handleContentChange = (uriString: string) => { - if (schemaDocuments[uriString]) { - client.sendNotification(SchemaContentChangeNotification.type, uriString); - return true; - } - return false; - }; - - let handleActiveEditorChange = (activeEditor?: TextEditor) => { - if (!activeEditor) { - return; - } - - const activeDocUri = activeEditor.document.uri.toString(); - - if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { - schemaResolutionErrorStatusBarItem.show(); - } else { - schemaResolutionErrorStatusBarItem.hide(); - } - }; - - toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); - toDispose.push(workspace.onDidCloseTextDocument(d => { - const uriString = d.uri.toString(); - if (handleContentChange(uriString)) { - delete schemaDocuments[uriString]; - } - fileSchemaErrors.delete(uriString); - })); - toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); - - let handleRetryResolveSchemaCommand = () => { - if (window.activeTextEditor) { - schemaResolutionErrorStatusBarItem.text = '$(watch)'; - const activeDocUri = window.activeTextEditor.document.uri.toString(); - client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { - const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); - if (schemaErrorIndex !== -1) { - // Show schema resolution errors in status bar only; ref: #51032 - const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; - fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); - } else { - schemaResolutionErrorStatusBarItem.hide(); - } - schemaResolutionErrorStatusBarItem.text = '$(alert)'; - }); - } - }; - - toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); - - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); - - extensions.onDidChange(_ => { - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); - }); - - // manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652. - updateFormatterRegistration(); - toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); - toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration('html.format.enable') && updateFormatterRegistration())); - - - client.onNotification(ResultLimitReachedNotification.type, message => { - window.showInformationMessage(`${message}\nUse setting 'json.maxItemsComputed' to configure the limit.`); - }); - - }); - - let languageConfiguration: LanguageConfiguration = { - wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, - indentationRules: { - increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/, - decreaseIndentPattern: /^\s*[}\]],?\s*$/ - } - }; - languages.setLanguageConfiguration('json', languageConfiguration); - languages.setLanguageConfiguration('jsonc', languageConfiguration); - - function updateFormatterRegistration() { - const formatEnabled = workspace.getConfiguration().get('json.format.enable'); - if (!formatEnabled && rangeFormatting) { - rangeFormatting.dispose(); - rangeFormatting = undefined; - } else if (formatEnabled && !rangeFormatting) { - rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { - provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { - let params: DocumentRangeFormattingParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - range: client.code2ProtocolConverter.asRange(range), - options: client.code2ProtocolConverter.asFormattingOptions(options) - }; - return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( - client.protocol2CodeConverter.asTextEdits, - (error) => { - client.logFailedRequest(DocumentRangeFormattingRequest.type, error); - return Promise.resolve([]); - } - ); - } - }); - } - } -} - - - -export function deactivate(): Promise { - return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null); -} - -function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations { - let associations: ISchemaAssociations = {}; - extensions.all.forEach(extension => { - let packageJSON = extension.packageJSON; - if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) { - let jsonValidation = packageJSON.contributes.jsonValidation; - if (Array.isArray(jsonValidation)) { - jsonValidation.forEach(jv => { - let { fileMatch, url } = jv; - if (fileMatch && url) { - if (url[0] === '.' && url[1] === '/') { - url = Uri.file(path.join(extension.extensionPath, url)).toString(); - } - if (fileMatch[0] === '%') { - fileMatch = fileMatch.replace(/%APP_SETTINGS_HOME%/, '/User'); - fileMatch = fileMatch.replace(/%MACHINE_SETTINGS_HOME%/, '/Machine'); - fileMatch = fileMatch.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces'); - } else if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:\/\//)) { - fileMatch = '/' + fileMatch; - } - let association = associations[fileMatch]; - if (!association) { - association = []; - associations[fileMatch] = association; - } - association.push(url); - } - }); - } - } - }); - return associations; -} - -function getSettings(): Settings { - let httpSettings = workspace.getConfiguration('http'); - - let resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get('json.maxItemsComputed')))) || 5000; - - let settings: Settings = { - http: { - proxy: httpSettings.get('proxy'), - proxyStrictSSL: httpSettings.get('proxyStrictSSL') - }, - json: { - schemas: [], - resultLimit - } - }; - let schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null); - let collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], rootPath?: string, fileMatchPrefix?: string) => { - for (let setting of schemaSettings) { - let url = getSchemaId(setting, rootPath); - if (!url) { - continue; - } - let schemaSetting = schemaSettingsById[url]; - if (!schemaSetting) { - schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] }; - settings.json!.schemas!.push(schemaSetting); - } - let fileMatches = setting.fileMatch; - let resultingFileMatches = schemaSetting.fileMatch!; - if (Array.isArray(fileMatches)) { - if (fileMatchPrefix) { - for (let fileMatch of fileMatches) { - if (fileMatch[0] === '/') { - resultingFileMatches.push(fileMatchPrefix + fileMatch); - resultingFileMatches.push(fileMatchPrefix + '/*' + fileMatch); - } else { - resultingFileMatches.push(fileMatchPrefix + '/' + fileMatch); - resultingFileMatches.push(fileMatchPrefix + '/*/' + fileMatch); - } - } - } else { - resultingFileMatches.push(...fileMatches); - } - - } - if (setting.schema) { - schemaSetting.schema = setting.schema; - } - } - }; - - // merge global and folder settings. Qualify all file matches with the folder path. - let globalSettings = workspace.getConfiguration('json', null).get('schemas'); - if (Array.isArray(globalSettings)) { - collectSchemaSettings(globalSettings, workspace.rootPath); - } - let folders = workspace.workspaceFolders; - if (folders) { - for (let folder of folders) { - let folderUri = folder.uri; - - let schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect('schemas'); - - let folderSchemas = schemaConfigInfo!.workspaceFolderValue; - if (Array.isArray(folderSchemas)) { - let folderPath = folderUri.toString(); - if (folderPath[folderPath.length - 1] === '/') { - folderPath = folderPath.substr(0, folderPath.length - 1); - } - collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath); - } - } - } - return settings; -} - -function getSchemaId(schema: JSONSchemaSettings, rootPath?: string) { - let url = schema.url; - if (!url) { - if (schema.schema) { - url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`; - } - } else if (rootPath && (url[0] === '.' || url[0] === '/')) { - url = Uri.file(path.normalize(path.join(rootPath, url))).toString(); - } - return url; -} - -function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined { - let extensionPackage = readJSONFile(context.asAbsolutePath('./package.json')); - if (extensionPackage) { - return { - name: extensionPackage.name, - version: extensionPackage.version, - aiKey: extensionPackage.aiKey - }; - } - return undefined; -} - -function readJSONFile(location: string) { - try { - return JSON.parse(fs.readFileSync(location).toString()); - } catch (e) { - console.log(`Problems reading ${location}: ${e}`); - return {}; - } -} diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts new file mode 100644 index 00000000000..6e9aed7f105 --- /dev/null +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionContext } from 'vscode'; +import { startClient, LanguageClientConstructor } from '../jsonClient'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; + +import * as fs from 'fs'; +import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light'; + +import TelemetryReporter from 'vscode-extension-telemetry'; +import { RequestService } from '../requests'; + +let telemetry: TelemetryReporter | undefined; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + + const clientPackageJSON = getPackageInfo(context); + telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey); + + const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`; + const serverModule = context.asAbsolutePath(serverMain); + + // The debug options for the server + const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + + // If the extension is launch in debug mode the debug server options are use + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, serverOptions, clientOptions); + }; + + startClient(context, newLanguageClient, { http: getHTTPRequestService(), telemetry }); +} + +export function deactivate(): Promise { + return telemetry ? telemetry.dispose() : Promise.resolve(null); +} + +interface IPackageInfo { + name: string; + version: string; + aiKey: string; + main: string; +} + +function getPackageInfo(context: ExtensionContext): IPackageInfo { + const location = context.asAbsolutePath('./package.json'); + try { + return JSON.parse(fs.readFileSync(location).toString()); + } catch (e) { + console.log(`Problems reading ${location}: ${e}`); + return { name: '', version: '', aiKey: '', main: '' }; + } +} + +function getHTTPRequestService(): RequestService { + return { + getContent(uri: string, _encoding?: string) { + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uri, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } + }; +} diff --git a/extensions/json-language-features/client/src/requests.ts b/extensions/json-language-features/client/src/requests.ts new file mode 100644 index 00000000000..be05fae6aef --- /dev/null +++ b/extensions/json-language-features/client/src/requests.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Uri } from 'vscode'; + +export interface RequestService { + getContent(uri: string, encoding?: string): Thenable; +} + +export function getScheme(uri: string) { + return uri.substr(0, uri.indexOf(':')); +} + +export function dirname(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; +} + +export function basename(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return uri.substr(lastIndexOfSlash + 1); +} + +const Slash = '/'.charCodeAt(0); +const Dot = '.'.charCodeAt(0); + +export function isAbsolutePath(path: string) { + return path.charCodeAt(0) === Slash; +} + +export function resolvePath(uri: Uri, path: string): Uri { + if (isAbsolutePath(path)) { + return uri.with({ path: normalizePath(path.split('/')) }); + } + return joinPath(uri, path); +} + +export function normalizePath(parts: string[]): string { + const newParts: string[] = []; + for (const part of parts) { + if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { + // ignore + } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { + newParts.pop(); + } else { + newParts.push(part); + } + } + if (parts.length > 1 && parts[parts.length - 1].length === 0) { + newParts.push(''); + } + let res = newParts.join('/'); + if (parts[0].length === 0) { + res = '/' + res; + } + return res; +} + + +export function joinPath(uri: Uri, ...paths: string[]): Uri { + const parts = uri.path.split('/'); + for (let path of paths) { + parts.push(...path.split('/')); + } + return uri.with({ path: normalizePath(parts) }); +} diff --git a/extensions/json-language-features/extension-browser.webpack.config.js b/extensions/json-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..900ef6a3b12 --- /dev/null +++ b/extensions/json-language-features/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + target: 'webworker', + context: path.join(__dirname, 'client'), + entry: { + extension: './src/browser/jsonClientMain.ts' + }, + output: { + filename: 'jsonClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'browser') + } +}); diff --git a/extensions/json-language-features/extension.webpack.config.js b/extensions/json-language-features/extension.webpack.config.js index 39e5e567a7a..13a05952995 100644 --- a/extensions/json-language-features/extension.webpack.config.js +++ b/extensions/json-language-features/extension.webpack.config.js @@ -14,11 +14,11 @@ const webpack = require('webpack'); const config = withDefaults({ context: path.join(__dirname, 'client'), entry: { - extension: './src/jsonMain.ts', + extension: './src/node/jsonClientMain.ts' }, output: { - filename: 'jsonMain.js', - path: path.join(__dirname, 'client', 'dist') + filename: 'jsonClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'node') } }); diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index dfceebce8e7..db003f05121 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -14,7 +14,8 @@ "onLanguage:json", "onLanguage:jsonc" ], - "main": "./client/out/jsonMain", + "main": "./client/out/node/jsonClientMain", + "browser": "./client/dist/browser/jsonClientMain", "enableProposedApi": true, "scripts": { "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", @@ -95,33 +96,41 @@ "type": "number", "default": 5000, "description": "%json.maxItemsComputed.desc%" - } + }, + "json.schemaDownload.enable": { + "type": "boolean", + "default": true, + "description": "%json.enableSchemaDownload.desc%", + "tags": ["usesOnlineServices"] + } } }, "configurationDefaults": { "[json]": { - "editor.quickSuggestions": { - "strings": true - }, - "editor.suggest.insertMode": "replace" + "editor.quickSuggestions": { + "strings": true + }, + "editor.suggest.insertMode": "replace" }, "[jsonc]": { - "editor.quickSuggestions": { - "strings": true - }, - "editor.suggest.insertMode": "replace" - } + "editor.quickSuggestions": { + "strings": true + }, + "editor.suggest.insertMode": "replace" + } }, - "jsonValidation": [{ - "fileMatch": "*.schema.json", - "url": "http://json-schema.org/draft-07/schema#" - }] + "jsonValidation": [ + { + "fileMatch": "*.schema.json", + "url": "http://json-schema.org/draft-07/schema#" + } + ] }, "dependencies": { - "request-light": "^0.2.5", + "request-light": "^0.3.0", "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^6.0.1", - "vscode-nls": "^4.1.1" + "vscode-languageclient": "7.0.0-next.5.1", + "vscode-nls": "^4.1.2" }, "devDependencies": { "@types/node": "^12.11.7" diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 5d132ccd776..59729a0ee99 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -3,7 +3,7 @@ "description": "Provides rich language support for JSON files.", "json.schemas.desc": "Associate schemas to JSON files in the current project", "json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory", - "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas.", + "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.", "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", @@ -12,5 +12,6 @@ "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", "json.schemaResolutionErrorMessage": "Unable to resolve schema.", "json.clickToRetry": "Click to retry.", - "json.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons)." + "json.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).", + "json.enableSchemaDownload.desc": "When enabled, JSON schemas can be fetched from http and https locations." } diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index 43cc04837d5..2e8745c7d8a 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -23,6 +23,7 @@ The server implements the following capabilities of the language server protocol - [Code Formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting) supporting ranges and formatting the whole document. - [Folding Ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange) for all folding ranges in the document. - Semantic Selection for semantic selection for one or multiple cursor positions. +- [Goto Definition](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) for $ref references in JSON schemas - [Diagnostics (Validation)](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics) are pushed for all open documents - syntax errors - structural validation based on the document's [JSON schema](http://json-schema.org/). @@ -61,14 +62,14 @@ The server supports the following settings: - json - `format` - `enable`: Whether the server should register the formatting support. This option is only applicable if the client supports *dynamicRegistration* for *rangeFormatting* and `initializationOptions.provideFormatter` is not defined. - - `schema`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. - - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. - - `url`: The URL of the schema, optional when also a schema is provided. - - `schema`: The schema content. - - `resultLimit`: The max number foldig ranges and otline symbols to be computed (for performance reasons) + - `schemas`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. + - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern. + - `url`: The URL of the schema, optional when also a schema is provided. + - `schema`: The schema content. + - `resultLimit`: The max number folding ranges and outline symbols to be computed (for performance reasons) ```json - { + { "http": { "proxy": "", "proxyStrictSSL": true @@ -85,7 +86,7 @@ The server supports the following settings: ], "url": "http://json.schemastore.org/foo", "schema": { - "type": "array" + "type": "array" } } ] @@ -142,13 +143,40 @@ In addition to the settings, schemas associations can also be provided through a Notification: - method: 'json/schemaAssociations' -- params: `ISchemaAssociations` defined as follows +- params: `ISchemaAssociations` or `ISchemaAssociation[]` defined as follows ```ts interface ISchemaAssociations { - [pattern: string]: string[]; + /** + * An object where: + * - keys are file names or file paths (using `/` as path separator). `*` can be used as a wildcard. + * - values are an arrays of schema URIs + */ + [pattern: string]: string[]; } + +interface ISchemaAssociation { + /** + * The URI of the schema, which is also the identifier of the schema. + */ + uri: string; + + /** + * A list of file path patterns that are associated to the schema. The '*' wildcard can be used. Exclusion patterns starting with '!'. + * For example '*.schema.json', 'package.json', '!foo*.schema.json'. + * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'. + */ + fileMatch: string[]; + + /* + * The schema for the given URI. + * If no schema is provided, the schema will be fetched with the schema request service (if available). + */ + schema?: JSONSchema; +} + ``` +`ISchemaAssociations` - keys: a file names or file path (separated by `/`). `*` can be used as a wildcard. - values: An array of schema URLs @@ -159,7 +187,7 @@ Notification: ### Item Limit If the setting `resultLimit` is set, the JSON language server will limit the number of folding ranges and document symbols computed. -When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that camn be shown to the user. +When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that can be shown to the user. Notification: - method: 'json/resultLimitReached' @@ -179,7 +207,7 @@ For that, install the `json-language-server` npm module: `npm install -g json-language-server` -Start the language server with the `json-language-server` command. Use a command line argument to specify the prefered communication channel: +Start the language server with the `json-language-server` command. Use a command line argument to specify the preferred communication channel: ``` json-language-server --node-ipc @@ -191,14 +219,14 @@ To connect to the server from NodeJS, see Remy Suen's great write-up on [how to ## Participate -The source code of the JSON language server can be found in the [VSCode repository](https://github.com/Microsoft/vscode) at [extensions/json-language-features/server](https://github.com/Microsoft/vscode/tree/master/extensions/json-language-features/server). +The source code of the JSON language server can be found in the [VSCode repository](https://github.com/microsoft/vscode) at [extensions/json-language-features/server](https://github.com/microsoft/vscode/tree/master/extensions/json-language-features/server). -File issues and pull requests in the [VSCode GitHub Issues](https://github.com/Microsoft/vscode/issues). See the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute) on how to build and run from source. +File issues and pull requests in the [VSCode GitHub Issues](https://github.com/microsoft/vscode/issues). See the document [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) on how to build and run from source. Most of the functionality of the server is located in libraries: -- [jsonc-parser](https://github.com/Microsoft/node-jsonc-parser) contains the JSON parser and scanner. -- [vscode-json-languageservice](https://github.com/Microsoft/vscode-json-languageservice) contains the implementation of all features as a re-usable library. -- [vscode-languageserver-node](https://github.com/Microsoft/vscode-languageserver-node) contains the implementation of language server for NodeJS. +- [jsonc-parser](https://github.com/microsoft/node-jsonc-parser) contains the JSON parser and scanner. +- [vscode-json-languageservice](https://github.com/microsoft/vscode-json-languageservice) contains the implementation of all features as a re-usable library. +- [vscode-languageserver-node](https://github.com/microsoft/vscode-languageserver-node) contains the implementation of language server for NodeJS. Help on any of these projects is very welcome. diff --git a/extensions/json-language-features/server/bin/vscode-json-languageserver b/extensions/json-language-features/server/bin/vscode-json-languageserver index a80d7d55b47..129768aef0e 100644 --- a/extensions/json-language-features/server/bin/vscode-json-languageserver +++ b/extensions/json-language-features/server/bin/vscode-json-languageserver @@ -3,4 +3,4 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -require("../out/jsonServerMain"); \ No newline at end of file +require('../out/node/jsonServerMain'); diff --git a/extensions/json-language-features/server/extension-browser.webpack.config.js b/extensions/json-language-features/server/extension-browser.webpack.config.js new file mode 100644 index 00000000000..b1ae74c7b60 --- /dev/null +++ b/extensions/json-language-features/server/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/browser/jsonServerMain.ts', + }, + output: { + filename: 'jsonServerMain.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var' + } +}); diff --git a/extensions/json-language-features/server/extension.webpack.config.js b/extensions/json-language-features/server/extension.webpack.config.js index 2c90a0b0751..50a7c3d545f 100644 --- a/extensions/json-language-features/server/extension.webpack.config.js +++ b/extensions/json-language-features/server/extension.webpack.config.js @@ -14,11 +14,11 @@ const webpack = require('webpack'); const config = withDefaults({ context: path.join(__dirname), entry: { - extension: './src/jsonServerMain.ts', + extension: './src/node/jsonServerMain.ts', }, output: { filename: 'jsonServerMain.js', - path: path.join(__dirname, 'dist') + path: path.join(__dirname, 'dist', 'node'), } }); diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 0c26777f01f..0c1c76358db 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-json-languageserver", "description": "JSON language server", - "version": "1.2.2", + "version": "1.3.1", "author": "Microsoft Corporation", "license": "MIT", "engines": { @@ -10,13 +10,13 @@ "bin": { "vscode-json-languageserver": "./bin/vscode-json-languageserver" }, - "main": "./out/jsonServerMain", + "main": "./out/node/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.2.0", - "request-light": "^0.2.5", - "vscode-json-languageservice": "^3.4.12", - "vscode-languageserver": "^6.0.1", - "vscode-uri": "^2.1.1" + "jsonc-parser": "^2.2.1", + "request-light": "^0.3.0", + "vscode-json-languageservice": "^3.10.0", + "vscode-languageserver": "7.0.0-next.3", + "vscode-uri": "^2.1.2" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/json-language-features/server/src/browser/jsonServerMain.ts b/extensions/json-language-features/server/src/browser/jsonServerMain.ts new file mode 100644 index 00000000000..5394412877c --- /dev/null +++ b/extensions/json-language-features/server/src/browser/jsonServerMain.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser'; +import { startServer } from '../jsonServer'; + +declare let self: any; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +startServer(connection, {}); diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts new file mode 100644 index 00000000000..e7d2526f1e5 --- /dev/null +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -0,0 +1,497 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + Connection, + TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit +} from 'vscode-languageserver'; + +import { formatError, runSafe, runSafeAsync } from './utils/runner'; +import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Diagnostic, Range, Position } from 'vscode-json-languageservice'; +import { getLanguageModelCache } from './languageModelCache'; +import { RequestService, basename, resolvePath } from './requests'; + +type ISchemaAssociations = Record; + +namespace SchemaAssociationNotification { + export const type: NotificationType = new NotificationType('json/schemaAssociations'); +} + +namespace VSCodeContentRequest { + export const type: RequestType = new RequestType('vscode/content'); +} + +namespace SchemaContentChangeNotification { + export const type: NotificationType = new NotificationType('json/schemaContent'); +} + +namespace ResultLimitReachedNotification { + export const type: NotificationType = new NotificationType('json/resultLimitReached'); +} + +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + + +const workspaceContext = { + resolveRelativePath: (relativePath: string, resource: string) => { + const base = resource.substr(0, resource.lastIndexOf('/') + 1); + return resolvePath(base, relativePath); + } +}; + +export interface RuntimeEnvironment { + file?: RequestService; + http?: RequestService + configureHttpRequests?(proxy: string, strictSSL: boolean): void; +} + +export function startServer(connection: Connection, runtime: RuntimeEnvironment) { + + function getSchemaRequestService(handledSchemas: string[] = ['https', 'http', 'file']) { + const builtInHandlers: { [protocol: string]: RequestService | undefined } = {}; + for (let protocol of handledSchemas) { + if (protocol === 'file') { + builtInHandlers[protocol] = runtime.file; + } else if (protocol === 'http' || protocol === 'https') { + builtInHandlers[protocol] = runtime.http; + } + } + return (uri: string): Thenable => { + const protocol = uri.substr(0, uri.indexOf(':')); + + const builtInHandler = builtInHandlers[protocol]; + if (builtInHandler) { + return builtInHandler.getContent(uri); + } + return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => { + return responseText; + }, error => { + return Promise.reject(error.message); + }); + }; + } + + // create the JSON language service + let languageService = getLanguageService({ + workspaceContext, + contributions: [], + clientCapabilities: ClientCapabilities.LATEST + }); + + // Create a text document manager. + const documents = new TextDocuments(TextDocument); + + // Make the text document manager listen on the connection + // for open, change and close text document events + documents.listen(connection); + + let clientSnippetSupport = false; + let dynamicFormatterRegistration = false; + let hierarchicalDocumentSymbolSupport = false; + + let foldingRangeLimitDefault = Number.MAX_VALUE; + let foldingRangeLimit = Number.MAX_VALUE; + let resultLimit = Number.MAX_VALUE; + let formatterMaxNumberOfEdits = Number.MAX_VALUE; + + // After the server has started the client sends an initialize request. The server receives + // in the passed params the rootPath of the workspace plus the client capabilities. + connection.onInitialize((params: InitializeParams): InitializeResult => { + + const handledProtocols = params.initializationOptions?.handledSchemaProtocols; + + languageService = getLanguageService({ + schemaRequestService: getSchemaRequestService(handledProtocols), + workspaceContext, + contributions: [], + clientCapabilities: params.capabilities + }); + + function getClientCapability(name: string, def: T) { + const keys = name.split('.'); + let c: any = params.capabilities; + for (let i = 0; c && i < keys.length; i++) { + if (!c.hasOwnProperty(keys[i])) { + return def; + } + c = c[keys[i]]; + } + return c; + } + + clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); + dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean'); + foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); + formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + const capabilities: ServerCapabilities = { + textDocumentSync: TextDocumentSyncKind.Incremental, + completionProvider: clientSnippetSupport ? { + resolveProvider: false, // turn off resolving as the current language service doesn't do anything on resolve. Also fixes #91747 + triggerCharacters: ['"', ':'] + } : undefined, + hoverProvider: true, + documentSymbolProvider: true, + documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true, + colorProvider: {}, + foldingRangeProvider: true, + selectionRangeProvider: true, + documentLinkProvider: {} + }; + + return { capabilities }; + }); + + + + // The settings interface describes the server relevant settings part + interface Settings { + json: { + schemas: JSONSchemaSettings[]; + format: { enable: boolean; }; + resultLimit?: number; + }; + http: { + proxy: string; + proxyStrictSSL: boolean; + }; + } + + interface JSONSchemaSettings { + fileMatch?: string[]; + url?: string; + schema?: JSONSchema; + } + + + const limitExceededWarnings = function () { + const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: NodeJS.Timeout; } } = {}; + + return { + cancel(uri: string) { + const warning = pendingWarnings[uri]; + if (warning && warning.timeout) { + clearTimeout(warning.timeout); + delete pendingWarnings[uri]; + } + }, + + onResultLimitExceeded(uri: string, resultLimit: number, name: string) { + return () => { + let warning = pendingWarnings[uri]; + if (warning) { + if (!warning.timeout) { + // already shown + return; + } + warning.features[name] = name; + warning.timeout.refresh(); + } else { + warning = { features: { [name]: name } }; + warning.timeout = setTimeout(() => { + connection.sendNotification(ResultLimitReachedNotification.type, `${basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); + warning.timeout = undefined; + }, 2000); + pendingWarnings[uri] = warning; + } + }; + } + }; + }(); + + let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; + let schemaAssociations: ISchemaAssociations | SchemaConfiguration[] | undefined = undefined; + let formatterRegistration: Thenable | null = null; + + // The settings have changed. Is send on server activation as well. + connection.onDidChangeConfiguration((change) => { + let settings = change.settings; + if (runtime.configureHttpRequests) { + runtime.configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL); + } + jsonConfigurationSettings = settings.json && settings.json.schemas; + updateConfiguration(); + + foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0)); + resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0)); + + // dynamically enable & disable the formatter + if (dynamicFormatterRegistration) { + const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable; + if (enableFormatter) { + if (!formatterRegistration) { + formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }, { language: 'jsonc' }] }); + } + } else if (formatterRegistration) { + formatterRegistration.then(r => r.dispose()); + formatterRegistration = null; + } + } + }); + + // The jsonValidation extension configuration has changed + connection.onNotification(SchemaAssociationNotification.type, associations => { + schemaAssociations = associations; + updateConfiguration(); + }); + + // A schema has changed + connection.onNotification(SchemaContentChangeNotification.type, uri => { + languageService.resetSchema(uri); + }); + + // Retry schema validation on all open documents + connection.onRequest(ForceValidateRequest.type, uri => { + return new Promise(resolve => { + const document = documents.get(uri); + if (document) { + updateConfiguration(); + validateTextDocument(document, diagnostics => { + resolve(diagnostics); + }); + } else { + resolve([]); + } + }); + }); + + function updateConfiguration() { + const languageSettings = { + validate: true, + allowComments: true, + schemas: new Array() + }; + if (schemaAssociations) { + if (Array.isArray(schemaAssociations)) { + Array.prototype.push.apply(languageSettings.schemas, schemaAssociations); + } else { + for (const pattern in schemaAssociations) { + const association = schemaAssociations[pattern]; + if (Array.isArray(association)) { + association.forEach(uri => { + languageSettings.schemas.push({ uri, fileMatch: [pattern] }); + }); + } + } + } + } + if (jsonConfigurationSettings) { + jsonConfigurationSettings.forEach((schema, index) => { + let uri = schema.url; + if (!uri && schema.schema) { + uri = schema.schema.id || `vscode://schemas/custom/${index}`; + } + if (uri) { + languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema }); + } + }); + } + languageService.configure(languageSettings); + + // Revalidate any open text documents + documents.all().forEach(triggerValidation); + } + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent((change) => { + limitExceededWarnings.cancel(change.document.uri); + triggerValidation(change.document); + }); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + limitExceededWarnings.cancel(event.document.uri); + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }); + + const pendingValidationRequests: { [uri: string]: NodeJS.Timer; } = {}; + const validationDelayMs = 300; + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + clearTimeout(request); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + pendingValidationRequests[textDocument.uri] = setTimeout(() => { + delete pendingValidationRequests[textDocument.uri]; + validateTextDocument(textDocument); + }, validationDelayMs); + } + + function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { + const respond = (diagnostics: Diagnostic[]) => { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + if (callback) { + callback(diagnostics); + } + }; + if (textDocument.getText().length === 0) { + respond([]); // ignore empty documents + return; + } + const jsonDocument = getJSONDocument(textDocument); + const version = textDocument.version; + + const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' }; + languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => { + setImmediate(() => { + const currDocument = documents.get(textDocument.uri); + if (currDocument && currDocument.version === version) { + respond(diagnostics); // Send the computed diagnostics to VSCode. + } + }); + }, error => { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error)); + }); + } + + connection.onDidChangeWatchedFiles((change) => { + // Monitored files have changed in VSCode + let hasChanges = false; + change.changes.forEach(c => { + if (languageService.resetSchema(c.uri)) { + hasChanges = true; + } + }); + if (hasChanges) { + documents.all().forEach(triggerValidation); + } + }); + + const jsonDocuments = getLanguageModelCache(10, 60, document => languageService.parseJSONDocument(document)); + documents.onDidClose(e => { + jsonDocuments.onDocumentRemoved(e.document); + }); + connection.onShutdown(() => { + jsonDocuments.dispose(); + }); + + function getJSONDocument(document: TextDocument): JSONDocument { + return jsonDocuments.get(document); + } + + connection.onCompletion((textDocumentPosition, token) => { + return runSafeAsync(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.doComplete(document, textDocumentPosition.position, jsonDocument); + } + return null; + }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onHover((textDocumentPositionParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(textDocumentPositionParams.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument); + } + return null; + }, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`, token); + }); + + connection.onDocumentSymbol((documentSymbolParams, token) => { + return runSafe(() => { + const document = documents.get(documentSymbolParams.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols'); + if (hierarchicalDocumentSymbolSupport) { + return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded }); + } else { + return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded }); + } + } + return []; + }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); + }); + + connection.onDocumentRangeFormatting((formatParams, token) => { + return runSafe(() => { + const document = documents.get(formatParams.textDocument.uri); + if (document) { + const edits = languageService.format(document, formatParams.range, formatParams.options); + if (edits.length > formatterMaxNumberOfEdits) { + const newText = TextDocument.applyEdits(document, edits); + return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length)), newText)]; + } + return edits; + } + return []; + }, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token); + }); + + connection.onDocumentColor((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors'); + const jsonDocument = getJSONDocument(document); + return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded }); + } + return []; + }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); + }); + + connection.onColorPresentation((params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.getColorPresentations(document, jsonDocument, params.color, params.range); + } + return []; + }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); + }); + + connection.onFoldingRanges((params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const onRangeLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges'); + return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded }); + } + return null; + }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); + }); + + + connection.onSelectionRanges((params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.getSelectionRanges(document, params.positions, jsonDocument); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); + }); + + connection.onDocumentLinks((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.findLinks(document, jsonDocument); + } + return []; + }, [], `Error while computing links for ${params.textDocument.uri}`, token); + }); + + // Listen on the connection + connection.listen(); +} diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts deleted file mode 100644 index fb8a93aab2b..00000000000 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ /dev/null @@ -1,508 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { - createConnection, IConnection, - TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit -} from 'vscode-languageserver'; - -import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; -import * as fs from 'fs'; -import { URI } from 'vscode-uri'; -import * as URL from 'url'; -import { posix } from 'path'; -import { setTimeout, clearTimeout } from 'timers'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, SchemaRequestService, Diagnostic, Range, Position } from 'vscode-json-languageservice'; -import { getLanguageModelCache } from './languageModelCache'; - -interface ISchemaAssociations { - [pattern: string]: string[]; -} - -namespace SchemaAssociationNotification { - export const type: NotificationType = new NotificationType('json/schemaAssociations'); -} - -namespace VSCodeContentRequest { - export const type: RequestType = new RequestType('vscode/content'); -} - -namespace SchemaContentChangeNotification { - export const type: NotificationType = new NotificationType('json/schemaContent'); -} - -namespace ResultLimitReachedNotification { - export const type: NotificationType = new NotificationType('json/resultLimitReached'); -} - -namespace ForceValidateRequest { - export const type: RequestType = new RequestType('json/validate'); -} - -// Create a connection for the server -const connection: IConnection = createConnection(); - -process.on('unhandledRejection', (e: any) => { - console.error(formatError(`Unhandled exception`, e)); -}); -process.on('uncaughtException', (e: any) => { - console.error(formatError(`Unhandled exception`, e)); -}); - - -console.log = connection.console.log.bind(connection.console); -console.error = connection.console.error.bind(connection.console); - -const workspaceContext = { - resolveRelativePath: (relativePath: string, resource: string) => { - return URL.resolve(resource, relativePath); - } -}; - -const fileRequestService: SchemaRequestService = (uri: string) => { - const fsPath = URI.parse(uri).fsPath; - return new Promise((c, e) => { - fs.readFile(fsPath, 'UTF-8', (err, result) => { - err ? e(err.message || err.toString()) : c(result.toString()); - }); - }); -}; - -const httpRequestService: SchemaRequestService = (uri: string) => { - const headers = { 'Accept-Encoding': 'gzip, deflate' }; - return xhr({ url: uri, followRedirects: 5, headers }).then(response => { - return response.responseText; - }, (error: XHRResponse) => { - return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); - }); -}; - -function getSchemaRequestService(handledSchemas: string[] = ['https', 'http', 'file']) { - const builtInHandlers: { [protocol: string]: SchemaRequestService } = {}; - for (let protocol of handledSchemas) { - if (protocol === 'file') { - builtInHandlers[protocol] = fileRequestService; - } else if (protocol === 'http' || protocol === 'https') { - builtInHandlers[protocol] = httpRequestService; - } - } - return (uri: string): Thenable => { - const protocol = uri.substr(0, uri.indexOf(':')); - - const builtInHandler = builtInHandlers[protocol]; - if (builtInHandler) { - return builtInHandler(uri); - } - return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => { - return responseText; - }, error => { - return Promise.reject(error.message); - }); - }; -} - -// create the JSON language service -let languageService = getLanguageService({ - workspaceContext, - contributions: [], - clientCapabilities: ClientCapabilities.LATEST -}); - -// Create a text document manager. -const documents = new TextDocuments(TextDocument); - -// Make the text document manager listen on the connection -// for open, change and close text document events -documents.listen(connection); - -let clientSnippetSupport = false; -let dynamicFormatterRegistration = false; -let hierarchicalDocumentSymbolSupport = false; - -let foldingRangeLimitDefault = Number.MAX_VALUE; -let foldingRangeLimit = Number.MAX_VALUE; -let resultLimit = Number.MAX_VALUE; -let formatterMaxNumberOfEdits = Number.MAX_VALUE; - -// After the server has started the client sends an initialize request. The server receives -// in the passed params the rootPath of the workspace plus the client capabilities. -connection.onInitialize((params: InitializeParams): InitializeResult => { - - const handledProtocols = params.initializationOptions?.handledSchemaProtocols; - - languageService = getLanguageService({ - schemaRequestService: getSchemaRequestService(handledProtocols), - workspaceContext, - contributions: [], - clientCapabilities: params.capabilities - }); - - function getClientCapability(name: string, def: T) { - const keys = name.split('.'); - let c: any = params.capabilities; - for (let i = 0; c && i < keys.length; i++) { - if (!c.hasOwnProperty(keys[i])) { - return def; - } - c = c[keys[i]]; - } - return c; - } - - clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean'); - foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); - formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; - const capabilities: ServerCapabilities = { - textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : undefined, - hoverProvider: true, - documentSymbolProvider: true, - documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true, - colorProvider: {}, - foldingRangeProvider: true, - selectionRangeProvider: true - }; - - return { capabilities }; -}); - - - -// The settings interface describes the server relevant settings part -interface Settings { - json: { - schemas: JSONSchemaSettings[]; - format: { enable: boolean; }; - resultLimit?: number; - }; - http: { - proxy: string; - proxyStrictSSL: boolean; - }; -} - -interface JSONSchemaSettings { - fileMatch?: string[]; - url?: string; - schema?: JSONSchema; -} - -namespace LimitExceededWarnings { - const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: NodeJS.Timeout; } } = {}; - - export function cancel(uri: string) { - const warning = pendingWarnings[uri]; - if (warning && warning.timeout) { - clearTimeout(warning.timeout); - delete pendingWarnings[uri]; - } - } - - export function onResultLimitExceeded(uri: string, resultLimit: number, name: string) { - return () => { - let warning = pendingWarnings[uri]; - if (warning) { - if (!warning.timeout) { - // already shown - return; - } - warning.features[name] = name; - warning.timeout.refresh(); - } else { - warning = { features: { [name]: name } }; - warning.timeout = setTimeout(() => { - connection.sendNotification(ResultLimitReachedNotification.type, `${posix.basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); - warning.timeout = undefined; - }, 2000); - pendingWarnings[uri] = warning; - } - }; - } -} - -let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; -let schemaAssociations: ISchemaAssociations | undefined = undefined; -let formatterRegistration: Thenable | null = null; - -// The settings have changed. Is send on server activation as well. -connection.onDidChangeConfiguration((change) => { - let settings = change.settings; - configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL); - - jsonConfigurationSettings = settings.json && settings.json.schemas; - updateConfiguration(); - - foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0)); - resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0)); - - // dynamically enable & disable the formatter - if (dynamicFormatterRegistration) { - const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable; - if (enableFormatter) { - if (!formatterRegistration) { - formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }, { language: 'jsonc' }] }); - } - } else if (formatterRegistration) { - formatterRegistration.then(r => r.dispose()); - formatterRegistration = null; - } - } -}); - -// The jsonValidation extension configuration has changed -connection.onNotification(SchemaAssociationNotification.type, associations => { - schemaAssociations = associations; - updateConfiguration(); -}); - -// A schema has changed -connection.onNotification(SchemaContentChangeNotification.type, uri => { - languageService.resetSchema(uri); -}); - -// Retry schema validation on all open documents -connection.onRequest(ForceValidateRequest.type, uri => { - return new Promise(resolve => { - const document = documents.get(uri); - if (document) { - updateConfiguration(); - validateTextDocument(document, diagnostics => { - resolve(diagnostics); - }); - } else { - resolve([]); - } - }); -}); - -function updateConfiguration() { - const languageSettings = { - validate: true, - allowComments: true, - schemas: new Array() - }; - if (schemaAssociations) { - for (const pattern in schemaAssociations) { - const association = schemaAssociations[pattern]; - if (Array.isArray(association)) { - association.forEach(uri => { - languageSettings.schemas.push({ uri, fileMatch: [pattern] }); - }); - } - } - } - if (jsonConfigurationSettings) { - jsonConfigurationSettings.forEach((schema, index) => { - let uri = schema.url; - if (!uri && schema.schema) { - uri = schema.schema.id || `vscode://schemas/custom/${index}`; - } - if (uri) { - languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema }); - } - }); - } - languageService.configure(languageSettings); - - // Revalidate any open text documents - documents.all().forEach(triggerValidation); -} - -// The content of a text document has changed. This event is emitted -// when the text document first opened or when its content has changed. -documents.onDidChangeContent((change) => { - LimitExceededWarnings.cancel(change.document.uri); - triggerValidation(change.document); -}); - -// a document has closed: clear all diagnostics -documents.onDidClose(event => { - LimitExceededWarnings.cancel(event.document.uri); - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); -}); - -const pendingValidationRequests: { [uri: string]: NodeJS.Timer; } = {}; -const validationDelayMs = 500; - -function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - clearTimeout(request); - delete pendingValidationRequests[textDocument.uri]; - } -} - -function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); -} - -function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { - const respond = (diagnostics: Diagnostic[]) => { - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); - if (callback) { - callback(diagnostics); - } - }; - if (textDocument.getText().length === 0) { - respond([]); // ignore empty documents - return; - } - const jsonDocument = getJSONDocument(textDocument); - const version = textDocument.version; - - const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' }; - languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => { - setTimeout(() => { - const currDocument = documents.get(textDocument.uri); - if (currDocument && currDocument.version === version) { - respond(diagnostics); // Send the computed diagnostics to VSCode. - } - }, 100); - }, error => { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error)); - }); -} - -connection.onDidChangeWatchedFiles((change) => { - // Monitored files have changed in VSCode - let hasChanges = false; - change.changes.forEach(c => { - if (languageService.resetSchema(c.uri)) { - hasChanges = true; - } - }); - if (hasChanges) { - documents.all().forEach(triggerValidation); - } -}); - -const jsonDocuments = getLanguageModelCache(10, 60, document => languageService.parseJSONDocument(document)); -documents.onDidClose(e => { - jsonDocuments.onDocumentRemoved(e.document); -}); -connection.onShutdown(() => { - jsonDocuments.dispose(); -}); - -function getJSONDocument(document: TextDocument): JSONDocument { - return jsonDocuments.get(document); -} - -connection.onCompletion((textDocumentPosition, token) => { - return runSafeAsync(async () => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - return languageService.doComplete(document, textDocumentPosition.position, jsonDocument); - } - return null; - }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onCompletionResolve((completionItem, token) => { - return runSafeAsync(() => { - return languageService.doResolve(completionItem); - }, completionItem, `Error while resolving completion proposal`, token); -}); - -connection.onHover((textDocumentPositionParams, token) => { - return runSafeAsync(async () => { - const document = documents.get(textDocumentPositionParams.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument); - } - return null; - }, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`, token); -}); - -connection.onDocumentSymbol((documentSymbolParams, token) => { - return runSafe(() => { - const document = documents.get(documentSymbolParams.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols'); - if (hierarchicalDocumentSymbolSupport) { - return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded }); - } else { - return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded }); - } - } - return []; - }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); -}); - -connection.onDocumentRangeFormatting((formatParams, token) => { - return runSafe(() => { - const document = documents.get(formatParams.textDocument.uri); - if (document) { - const edits = languageService.format(document, formatParams.range, formatParams.options); - if (edits.length > formatterMaxNumberOfEdits) { - const newText = TextDocument.applyEdits(document, edits); - return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length - 1)), newText)]; - } - return edits; - } - return []; - }, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token); -}); - -connection.onDocumentColor((params, token) => { - return runSafeAsync(async () => { - const document = documents.get(params.textDocument.uri); - if (document) { - const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors'); - const jsonDocument = getJSONDocument(document); - return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded }); - } - return []; - }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); -}); - -connection.onColorPresentation((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - return languageService.getColorPresentations(document, jsonDocument, params.color, params.range); - } - return []; - }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); -}); - -connection.onFoldingRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const onRangeLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges'); - return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded }); - } - return null; - }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); -}); - - -connection.onSelectionRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - return languageService.getSelectionRanges(document, params.positions, jsonDocument); - } - return []; - }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); -}); - -// Listen on the connection -connection.listen(); diff --git a/extensions/json-language-features/server/src/node/jsonServerMain.ts b/extensions/json-language-features/server/src/node/jsonServerMain.ts new file mode 100644 index 00000000000..94192b4b1ce --- /dev/null +++ b/extensions/json-language-features/server/src/node/jsonServerMain.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, Connection } from 'vscode-languageserver/node'; +import { formatError } from '../utils/runner'; +import { startServer } from '../jsonServer'; +import { RequestService } from '../requests'; + +import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; +import { URI as Uri } from 'vscode-uri'; +import * as fs from 'fs'; + +// Create a connection for the server. +const connection: Connection = createConnection(); + +console.log = connection.console.log.bind(connection.console); +console.error = connection.console.error.bind(connection.console); + +process.on('unhandledRejection', (e: any) => { + connection.console.error(formatError(`Unhandled exception`, e)); +}); + +function getHTTPRequestService(): RequestService { + return { + getContent(uri: string, _encoding?: string) { + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uri, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } + }; +} + +function getFileRequestService(): RequestService { + return { + getContent(location: string, encoding?: string) { + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.readFile(uri.fsPath, encoding, (err, buf) => { + if (err) { + return e(err); + } + c(buf.toString()); + }); + }); + } + }; +} + + +startServer(connection, { file: getFileRequestService(), http: getHTTPRequestService(), configureHttpRequests }); diff --git a/extensions/json-language-features/server/src/requests.ts b/extensions/json-language-features/server/src/requests.ts new file mode 100644 index 00000000000..a87cf92483f --- /dev/null +++ b/extensions/json-language-features/server/src/requests.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vscode-uri'; + +export interface RequestService { + getContent(uri: string, encoding?: string): Promise; +} + +export function getScheme(uri: string) { + return uri.substr(0, uri.indexOf(':')); +} + +export function dirname(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; +} + +export function basename(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return uri.substr(lastIndexOfSlash + 1); +} + + +const Slash = '/'.charCodeAt(0); +const Dot = '.'.charCodeAt(0); + +export function extname(uri: string) { + for (let i = uri.length - 1; i >= 0; i--) { + const ch = uri.charCodeAt(i); + if (ch === Dot) { + if (i > 0 && uri.charCodeAt(i - 1) !== Slash) { + return uri.substr(i); + } else { + break; + } + } else if (ch === Slash) { + break; + } + } + return ''; +} + +export function isAbsolutePath(path: string) { + return path.charCodeAt(0) === Slash; +} + +export function resolvePath(uriString: string, path: string): string { + if (isAbsolutePath(path)) { + const uri = URI.parse(uriString); + const parts = path.split('/'); + return uri.with({ path: normalizePath(parts) }).toString(); + } + return joinPath(uriString, path); +} + +export function normalizePath(parts: string[]): string { + const newParts: string[] = []; + for (const part of parts) { + if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { + // ignore + } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { + newParts.pop(); + } else { + newParts.push(part); + } + } + if (parts.length > 1 && parts[parts.length - 1].length === 0) { + newParts.push(''); + } + let res = newParts.join('/'); + if (parts[0].length === 0) { + res = '/' + res; + } + return res; +} + +export function joinPath(uriString: string, ...paths: string[]): string { + const uri = URI.parse(uriString); + const parts = uri.path.split('/'); + for (let path of paths) { + parts.push(...path.split('/')); + } + return uri.with({ path: normalizePath(parts) }).toString(); +} diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 9c72cea8b07..422475cc900 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -53,7 +53,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -https-proxy-agent@^2.2.3: +https-proxy-agent@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -61,72 +61,82 @@ https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" -jsonc-parser@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" - integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== + +jsonc-parser@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -request-light@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746" - integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw== +request-light@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d" + integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA== dependencies: http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.3" + https-proxy-agent "^2.2.4" vscode-nls "^4.1.1" -vscode-json-languageservice@^3.4.12: - version "3.4.12" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.12.tgz#e7c96a1824896a624cc7bb14f46fbf9cb7e6c5a3" - integrity sha512-+tA0KPVM1pDfORZqsQen7bY5buBpQGDTVYEobm5MoGtXNeZY2Kn0iy5wIQqXveb28LRv/I5xKE87dmNJTEaijQ== +vscode-json-languageservice@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.10.0.tgz#19eed884fd0f234f8ed2fa0a96e772f293ccc5c4" + integrity sha512-8IvuRSQnjznu+obqy6Dy4S4H68Ke7a3Kb+A0FcdctyAMAWEnrORpCpMOMqEYiPLm/OTYLVWJ7ql3qToDTozu4w== dependencies: - jsonc-parser "^2.2.0" - vscode-languageserver-textdocument "^1.0.1-next.1" - vscode-languageserver-types "^3.15.0" - vscode-nls "^4.1.1" - vscode-uri "^2.1.1" + jsonc-parser "^2.3.1" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "3.16.0-next.2" + vscode-nls "^5.0.0" + vscode-uri "^2.1.2" -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageserver-protocol@^3.15.1: - version "3.15.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.1.tgz#7555e595f0058b9a166f14605ad039e97fab320a" - integrity sha512-wJAo06VM9ZBnRqslplDjfz6Tdive0O7z44yNxBFA3x0/YZkXBIL6I+9rwQ/9Y//0X0eCh12FQrj+KmEXf2L5eA== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.0" + vscode-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" -vscode-languageserver-textdocument@^1.0.1-next.1: - version "1.0.1-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1-next.1.tgz#c8f2f792c7c88d33ea8441ca04bfb8376896b671" - integrity sha512-Cmt0KsNxouns+d7/Kw/jWtWU9Z3h56z1qAA8utjDOEqrDcrTs2rDXv3EJRa99nuKM3wVf6DbWym1VqL9q71XPA== +vscode-languageserver-textdocument@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f" + integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== -vscode-languageserver-types@3.15.0, vscode-languageserver-types@^3.15.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0.tgz#c45a23308ec0967135c483b759dfaf97978d9e0a" - integrity sha512-AXteNagMhBWnZ6gNN0UB4HTiD/7TajgfHl6jaM6O7qz3zDJw0H3Jf83w05phihnBRCML+K6Ockh8f8bL0OObPw== +vscode-languageserver-types@3.16.0-next.2: + version "3.16.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" + integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-languageserver@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.1.tgz#4f499d245f1baf83bd607dd79c4c3fd19e8cefc0" - integrity sha512-Wk4I/Dn5KNARWockdCrYuuImJz6bpYG8n2G3Kk5AU6Xy9nWNHD6YjB9/Rd99p4goViZOyETM+hYE81LnEzQZUA== +vscode-languageserver@7.0.0-next.3: + version "7.0.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0-next.3.tgz#3833bd09259a4a085baeba90783f1e4d06d81095" + integrity sha512-qSt8eb546iFuoFIN+9MPl4Avru6Iz2/JP0UmS/3djf40ICa31Np/yJ7anX2j0Az5rCzb0fak8oeKwDioGeVOYg== dependencies: - vscode-languageserver-protocol "^3.15.1" + vscode-languageserver-protocol "3.16.0-next.4" vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== -vscode-uri@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90" - integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A== +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + +vscode-uri@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" + integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 608f3eaed5b..29972b7df03 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -76,7 +76,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -https-proxy-agent@^2.2.3: +https-proxy-agent@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -94,13 +94,13 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -request-light@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746" - integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw== +request-light@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d" + integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA== dependencies: http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.3" + https-proxy-agent "^2.2.4" vscode-nls "^4.1.1" semver@^5.3.0: @@ -120,37 +120,42 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageclient@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.1.tgz#acd138e0a19a40c5788365e882ae11c164d9a460" - integrity sha512-7yZaSHichTJEyOJykI2RLQEECf9MqNLoklzC/1OVi/M8ioIsWQ1+lkN1nTsUhd6+F7p9ar9dNmPiEhL0i5uUBA== +vscode-languageclient@7.0.0-next.5.1: + version "7.0.0-next.5.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0-next.5.1.tgz#ed93f14e4c2cdccedf15002c7bf8ef9cb638f36c" + integrity sha512-OONvbk3IFpubwF8/Y5uPQaq5J5CEskpeET3SfK4iGlv5OUK+44JawH/SEW5wXuEPpfdMLEMZLuGLU5v5d7N7PQ== dependencies: semver "^6.3.0" - vscode-languageserver-protocol "^3.15.1" + vscode-languageserver-protocol "3.16.0-next.4" -vscode-languageserver-protocol@^3.15.1: - version "3.15.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.1.tgz#7555e595f0058b9a166f14605ad039e97fab320a" - integrity sha512-wJAo06VM9ZBnRqslplDjfz6Tdive0O7z44yNxBFA3x0/YZkXBIL6I+9rwQ/9Y//0X0eCh12FQrj+KmEXf2L5eA== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.0" + vscode-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" -vscode-languageserver-types@3.15.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0.tgz#c45a23308ec0967135c483b759dfaf97978d9e0a" - integrity sha512-AXteNagMhBWnZ6gNN0UB4HTiD/7TajgfHl6jaM6O7qz3zDJw0H3Jf83w05phihnBRCML+K6Ockh8f8bL0OObPw== +vscode-languageserver-types@3.16.0-next.2: + version "3.16.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" + integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +vscode-nls@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== + zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/json/build/update-grammars.js b/extensions/json/build/update-grammars.js index d7d92e18258..bf72e5290f0 100644 --- a/extensions/json/build/update-grammars.js +++ b/extensions/json/build/update-grammars.js @@ -31,7 +31,7 @@ function adaptJSON(grammar, replacementScope) { } } -var tsGrammarRepo = 'Microsoft/vscode-JSON.tmLanguage'; +var tsGrammarRepo = 'microsoft/vscode-JSON.tmLanguage'; updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSON.tmLanguage.json'); updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSONC.tmLanguage.json', grammar => adaptJSON(grammar, '.json.comments')); diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json index fabb7a93aba..53db20003ba 100644 --- a/extensions/json/cgmanifest.json +++ b/extensions/json/cgmanifest.json @@ -4,8 +4,8 @@ "component": { "type": "git", "git": { - "name": "Microsoft/vscode-JSON.tmLanguage", - "repositoryUrl": "https://github.com/Microsoft/vscode-JSON.tmLanguage", + "name": "microsoft/vscode-JSON.tmLanguage", + "repositoryUrl": "https://github.com/microsoft/vscode-JSON.tmLanguage", "commitHash": "9bd83f1c252b375e957203f21793316203f61f70" } }, @@ -14,4 +14,4 @@ } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/json/package.json b/extensions/json/package.json index 8f34baf1484..73902a8ec49 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -22,13 +22,14 @@ "extensions": [ ".json", ".bowerrc", - ".jshintrc", ".jscsrc", - ".swcrc", ".webmanifest", ".js.map", ".css.map", - ".har" + ".ts.map", + ".har", + ".jslintrc", + ".jsonld" ], "filenames": [ "composer.lock", @@ -47,11 +48,14 @@ "JSON with Comments" ], "extensions": [ - ".hintrc", - ".babelrc", ".jsonc", ".eslintrc", - ".eslintrc.json" + ".eslintrc.json", + ".jsfmtrc", + ".jshintrc", + ".swcrc", + ".hintrc", + ".babelrc" ], "configuration": "./language-configuration.json" } diff --git a/extensions/json/syntaxes/JSON.tmLanguage.json b/extensions/json/syntaxes/JSON.tmLanguage.json index 910045be39e..9454f0ed814 100644 --- a/extensions/json/syntaxes/JSON.tmLanguage.json +++ b/extensions/json/syntaxes/JSON.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage", + "This file has been converted from https://github.com/microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70", + "version": "https://github.com/microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70", "name": "JSON (Javascript Next)", "scopeName": "source.json", "patterns": [ @@ -210,4 +210,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/json/syntaxes/JSONC.tmLanguage.json b/extensions/json/syntaxes/JSONC.tmLanguage.json index 50028ef0f35..bf65fce9893 100644 --- a/extensions/json/syntaxes/JSONC.tmLanguage.json +++ b/extensions/json/syntaxes/JSONC.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage", + "This file has been converted from https://github.com/microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70", + "version": "https://github.com/microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70", "name": "JSON with comments", "scopeName": "source.json.comments", "patterns": [ @@ -210,4 +210,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json index fc3dec21721..6f94bec76e6 100644 --- a/extensions/json/test/colorize-results/test_json.json +++ b/extensions/json/test/colorize-results/test_json.json @@ -389,7 +389,7 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json constant.character.escape.json", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -1165,4 +1165,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/log/cgmanifest.json b/extensions/log/cgmanifest.json index 56318ba0e84..0fe21e5c2c4 100644 --- a/extensions/log/cgmanifest.json +++ b/extensions/log/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "vscode-logfile-highlighter", "repositoryUrl": "https://github.com/emilast/vscode-logfile-highlighter", - "commitHash": "e90aa2554b439827f125fd60ff6d7773fc582fcc" + "commitHash": "5dcab1c304110b605041824cde3810c6ef305477" } }, "license": "MIT", - "version": "2.5.0" + "version": "2.8.0" } ], "version": 1 diff --git a/extensions/log/syntaxes/log.tmLanguage.json b/extensions/log/syntaxes/log.tmLanguage.json index b0f144a67fe..4882a761e6f 100644 --- a/extensions/log/syntaxes/log.tmLanguage.json +++ b/extensions/log/syntaxes/log.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/emilast/vscode-logfile-highlighter/commit/e90aa2554b439827f125fd60ff6d7773fc582fcc", + "version": "https://github.com/emilast/vscode-logfile-highlighter/commit/5dcab1c304110b605041824cde3810c6ef305477", "name": "Log file", "scopeName": "text.log", "patterns": [ @@ -17,7 +17,7 @@ "name": "comment log.verbose" }, { - "match": "\\bV/", + "match": "(?<=^[\\s\\d\\p]*)\\bV\\b", "name": "comment log.verbose" }, { @@ -29,11 +29,11 @@ "name": "markup.changed log.debug" }, { - "match": "\\bD/", + "match": "(?<=^[\\s\\d\\p]*)\\bD\\b", "name": "markup.changed log.debug" }, { - "match": "\\b(HINT|INFO|INFORMATION|Info|NOTICE)\\b|(?i)\\b(info|information)\\:", + "match": "\\b(HINT|INFO|INFORMATION|Info|NOTICE|II)\\b|(?i)\\b(info|information)\\:", "name": "markup.inserted log.info" }, { @@ -41,11 +41,11 @@ "name": "markup.inserted log.info" }, { - "match": "\\bI/", + "match": "(?<=^[\\s\\d\\p]*)\\bI\\b", "name": "markup.inserted log.info" }, { - "match": "\\b(WARNING|WARN|Warn)\\b|(?i)\\b(warning)\\:", + "match": "\\b(WARNING|WARN|Warn|WW)\\b|(?i)\\b(warning)\\:", "name": "markup.deleted log.warning" }, { @@ -53,11 +53,11 @@ "name": "markup.deleted log.warning" }, { - "match": "\\bW/", + "match": "(?<=^[\\s\\d\\p]*)\\bW\\b", "name": "markup.deleted log.warning" }, { - "match": "\\b(ALERT|CRITICAL|EMERGENCY|ERROR|FAILURE|FAIL|Fatal|Error)\\b|(?i)\\b(error)\\:", + "match": "\\b(ALERT|CRITICAL|EMERGENCY|ERROR|FAILURE|FAIL|Fatal|FATAL|Error|EE)\\b|(?i)\\b(error)\\:", "name": "string.regexp, strong log.error" }, { @@ -65,7 +65,7 @@ "name": "string.regexp, strong log.error" }, { - "match": "\\bE/", + "match": "(?<=^[\\s\\d\\p]*)\\bE\\b", "name": "string.regexp, strong log.error" }, { @@ -88,6 +88,10 @@ "match": "[0-9a-fA-F]{8}[-]?([0-9a-fA-F]{4}[-]?){3}[0-9a-fA-F]{12}", "name": "constant.language log.constant" }, + { + "match": "([0-9a-fA-F]+[:-])+[0-9a-fA-F]+", + "name": "constant.language log.constant" + }, { "match": "\\b([0-9]+|true|false|null)\\b", "name": "constant.language log.constant" diff --git a/extensions/make/cgmanifest.json b/extensions/make/cgmanifest.json index 544b0bc5bd3..9a311cfe811 100644 --- a/extensions/make/cgmanifest.json +++ b/extensions/make/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "fadeevab/make.tmbundle", "repositoryUrl": "https://github.com/fadeevab/make.tmbundle", - "commitHash": "fd57c0552dbe5e4d0c1e6765daea4b296d9bfc59" + "commitHash": "e36e02becd20730259b0115d9ca5c419f65023a9" } }, "licenseDetail": [ diff --git a/extensions/make/syntaxes/make.tmLanguage.json b/extensions/make/syntaxes/make.tmLanguage.json index 2503fce46fc..890148cb19f 100644 --- a/extensions/make/syntaxes/make.tmLanguage.json +++ b/extensions/make/syntaxes/make.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/fadeevab/make.tmbundle/commit/fd57c0552dbe5e4d0c1e6765daea4b296d9bfc59", + "version": "https://github.com/fadeevab/make.tmbundle/commit/e36e02becd20730259b0115d9ca5c419f65023a9", "name": "Makefile", "scopeName": "source.makefile", "patterns": [ @@ -18,13 +18,13 @@ "include": "#variable-assignment" }, { - "include": "#target" + "include": "#directives" }, { "include": "#recipe" }, { - "include": "#directives" + "include": "#target" } ], "repository": { diff --git a/extensions/make/test/colorize-fixtures/makefile b/extensions/make/test/colorize-fixtures/makefile index 45a7c80465a..7720e43ae4d 100644 --- a/extensions/make/test/colorize-fixtures/makefile +++ b/extensions/make/test/colorize-fixtures/makefile @@ -84,3 +84,5 @@ var-$(nested-var)=val # but not so much. var-$(shell printf 2) := val2 $(info Should be 'val2' here: $(var-2)) + +export a ?= b:c diff --git a/extensions/make/test/colorize-results/makefile.json b/extensions/make/test/colorize-results/makefile.json index 616de8fb8e0..eacaa05384f 100644 --- a/extensions/make/test/colorize-results/makefile.json +++ b/extensions/make/test/colorize-results/makefile.json @@ -136,7 +136,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -279,7 +279,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -323,7 +323,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile comment.line.number-sign.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "constant.character: #569CD6" @@ -378,7 +378,7 @@ "t": "source.makefile comment.line.number-sign.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "constant.character: #569CD6" @@ -554,7 +554,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -884,7 +884,7 @@ "t": "source.makefile meta.scope.recipe.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -3029,7 +3029,7 @@ "t": "source.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -3353,5 +3353,71 @@ "light_vs": "string: #A31515", "hc_black": "string: #CE9178" } + }, + { + "c": "export", + "t": "source.makefile keyword.control.export.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "a", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "?=", + "t": "source.makefile punctuation.separator.key-value.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " b:c", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } } ] \ No newline at end of file diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index 26a3599cf5b..bbc5e342db0 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -85,11 +85,11 @@ "snippets": [ { "language": "markdown", - "path": "./snippets/markdown.json" + "path": "./snippets/markdown.code-snippets" } ] }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js microsoft/vscode-markdown-tm-grammar syntaxes/markdown.tmLanguage ./syntaxes/markdown.tmLanguage.json" } -} \ No newline at end of file +} diff --git a/extensions/markdown-basics/snippets/markdown.json b/extensions/markdown-basics/snippets/markdown.code-snippets similarity index 50% rename from extensions/markdown-basics/snippets/markdown.json rename to extensions/markdown-basics/snippets/markdown.code-snippets index 6ee831ae4a5..b07f984138c 100644 --- a/extensions/markdown-basics/snippets/markdown.json +++ b/extensions/markdown-basics/snippets/markdown.code-snippets @@ -14,43 +14,54 @@ "body": "> ${1:${TM_SELECTED_TEXT}}", "description": "Insert quoted text" }, - "Insert code": { + "Insert inline code": { "prefix": "code", "body": "`${1:${TM_SELECTED_TEXT}}`$0", - "description": "Insert code" + "description": "Insert inline code" }, "Insert fenced code block": { "prefix": "fenced codeblock", - "body": [ - "```${1:language}", - "${TM_SELECTED_TEXT}$0", - "```" - ], + "body": ["```${1:language}", "${TM_SELECTED_TEXT}$0", "```"], "description": "Insert fenced code block" }, - "Insert heading": { - "prefix": "heading", + "Insert heading level 1": { + "prefix": "heading1", "body": "# ${1:${TM_SELECTED_TEXT}}", - "description": "Insert heading" + "description": "Insert heading level 1" + }, + "Insert heading level 2": { + "prefix": "heading2", + "body": "## ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 2" + }, + "Insert heading level 3": { + "prefix": "heading3", + "body": "### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 3" + }, + "Insert heading level 4": { + "prefix": "heading4", + "body": "#### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 4" + }, + "Insert heading level 5": { + "prefix": "heading5", + "body": "##### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 5" + }, + "Insert heading level 6": { + "prefix": "heading6", + "body": "###### ${1:${TM_SELECTED_TEXT}}", + "description": "Insert heading level 6" }, "Insert unordered list": { "prefix": "unordered list", - "body": [ - "- ${1:first}", - "- ${2:second}", - "- ${3:third}", - "$0" - ], + "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" - ], + "body": ["1. ${1:first}", "2. ${2:second}", "3. ${3:third}", "$0"], "description": "Insert ordered list" }, "Insert horizontal rule": { @@ -67,5 +78,10 @@ "prefix": "image", "body": "![${TM_SELECTED_TEXT:${1:alt}}](https://${2:link})$0", "description": "Insert image" + }, + "Insert strikethrough": { + "prefix": "strikethrough", + "body": "~~${1:${TM_SELECTED_TEXT}}~~", + "description": "Insert strikethrough" } } diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 1babbfd546d..e8feaa75fa6 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/8fbbc11a6bb917f287bbe21d0573454020599547", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4be9cb335581f3559166c319607dac9100103083", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -1715,6 +1715,72 @@ } ] }, + "fenced_code_block_erlang": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(erlang)((\\s+|:|\\{)[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "4": { + "name": "fenced_code.block.language.markdown" + }, + "5": { + "name": "fenced_code.block.language.attributes.markdown" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.erlang", + "patterns": [ + { + "include": "source.erlang" + } + ] + } + ] + }, + "fenced_code_block_elixir": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(elixir)((\\s+|:|\\{)[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "4": { + "name": "fenced_code.block.language.markdown" + }, + "5": { + "name": "fenced_code.block.language.attributes.markdown" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.elixir", + "patterns": [ + { + "include": "source.elixir" + } + ] + } + ] + }, "fenced_code_block": { "patterns": [ { @@ -1867,6 +1933,12 @@ { "include": "#fenced_code_block_log" }, + { + "include": "#fenced_code_block_erlang" + }, + { + "include": "#fenced_code_block_elixir" + }, { "include": "#fenced_code_block_unknown" } @@ -1891,12 +1963,12 @@ "name": "markup.fenced_code.block.markdown" }, "heading": { - "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s+(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", + "match": "(?:^|\\G)[ ]{0,3}(#{1,6}\\s+(.*?)(\\s+#{1,6})?\\s*)$", "captures": { "1": { "patterns": [ { - "match": "(#{6})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{6})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.6.markdown", "captures": { "1": { @@ -1911,7 +1983,7 @@ } }, { - "match": "(#{5})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{5})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.5.markdown", "captures": { "1": { @@ -1926,7 +1998,7 @@ } }, { - "match": "(#{4})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{4})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.4.markdown", "captures": { "1": { @@ -1941,7 +2013,7 @@ } }, { - "match": "(#{3})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{3})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.3.markdown", "captures": { "1": { @@ -1956,7 +2028,7 @@ } }, { - "match": "(#{2})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{2})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.2.markdown", "captures": { "1": { @@ -1971,7 +2043,7 @@ } }, { - "match": "(#{1})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{1})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.1.markdown", "captures": { "1": { @@ -2267,7 +2339,7 @@ "name": "meta.other.valid-ampersand.markdown" }, "bold": { - "begin": "(?x) (\\*\\*(?=\\w)|(?]*+> # HTML tags\n | (?`+)([^`]|(?!(?(?!`))`)*+\\k\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (? # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whitespace\n ? # URL\n [ \\t]*+ # Optional whitespace\n ( # Optional Title\n (?['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | (?!(?<=\\S)\\1). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=__\\b|\\*\\*)\\1 # Close\n)\n", + "begin": "(?x) (?<open>(\\*\\*(?=\\w)|(?<!\\w)\\*\\*|(?<!\\w)\\b__))(?=\\S) (?=\n (\n <[^>]*+> # HTML tags\n | (?<raw>`+)([^`]|(?!(?<!`)\\k<raw>(?!`))`)*+\\k<raw>\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (?<square> # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g<square>*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whitespace\n <?(.*?)>? # URL\n [ \\t]*+ # Optional whitespace\n ( # Optional Title\n (?<title>['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | (?!(?<=\\S)\\k<open>). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=__\\b|\\*\\*)\\k<open> # Close\n)\n", "captures": { "1": { "name": "punctuation.definition.bold.markdown" @@ -2412,7 +2484,7 @@ "name": "meta.image.reference.markdown" }, "italic": { - "begin": "(?x) (\\*(?=\\w)|(?<!\\w)\\*|(?<!\\w)\\b_)(?=\\S) # Open\n (?=\n (\n <[^>]*+> # HTML tags\n | (?<raw>`+)([^`]|(?!(?<!`)\\k<raw>(?!`))`)*+\\k<raw>\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (?<square> # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g<square>*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whtiespace\n <?(.*?)>? # URL\n [ \\t]*+ # Optional whtiespace\n ( # Optional Title\n (?<title>['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | \\1\\1 # Must be bold closer\n | (?!(?<=\\S)\\1). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=_\\b|\\*)\\1 # Close\n )\n", + "begin": "(?x) (?<open>(\\*(?=\\w)|(?<!\\w)\\*|(?<!\\w)\\b_))(?=\\S) # Open\n (?=\n (\n <[^>]*+> # HTML tags\n | (?<raw>`+)([^`]|(?!(?<!`)\\k<raw>(?!`))`)*+\\k<raw>\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (?<square> # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g<square>*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whtiespace\n <?(.*?)>? # URL\n [ \\t]*+ # Optional whtiespace\n ( # Optional Title\n (?<title>['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | \\k<open>\\k<open> # Must be bold closer\n | (?!(?<=\\S)\\k<open>). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=_\\b|\\*)\\k<open> # Close\n )\n", "captures": { "1": { "name": "punctuation.definition.italic.markdown" diff --git a/extensions/markdown-basics/test/colorize-results/test_md.json b/extensions/markdown-basics/test/colorize-results/test_md.json index 6963f66e797..1fee1287417 100644 --- a/extensions/markdown-basics/test/colorize-results/test_md.json +++ b/extensions/markdown-basics/test/colorize-results/test_md.json @@ -33,7 +33,18 @@ } }, { - "c": " #", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.1.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "#", "t": "text.html.markdown markup.heading.markdown heading.1.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -77,7 +88,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", "t": "text.html.markdown markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -2189,7 +2211,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", "t": "text.html.markdown markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -2343,7 +2376,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", "t": "text.html.markdown markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", diff --git a/extensions/markdown-language-features/.vscodeignore b/extensions/markdown-language-features/.vscodeignore index 30d948fbc66..9f1e0620775 100644 --- a/extensions/markdown-language-features/.vscodeignore +++ b/extensions/markdown-language-features/.vscodeignore @@ -1,9 +1,11 @@ test/** +test-workspace/** src/** tsconfig.json out/test/** out/** extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json yarn.lock preview-src/** diff --git a/extensions/markdown-language-features/extension-browser.webpack.config.js b/extensions/markdown-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..7fcc53a728b --- /dev/null +++ b/extensions/markdown-language-features/extension-browser.webpack.config.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts' + } +}); diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 1aa7530d341..0433f33c89a 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -1,2 +1 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=2)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let o=void 0;function r(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=r,t.getSettings=function(){if(o)return o;if(o=r("data-settings"))return o;throw new Error("Could not load settings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0),r="code-line";function i(e){return t=0,n=o.getSettings().lineCount-1,r=e,Math.min(n,Math.max(t,r));var t,n,r}const s=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName(r)){const n=+t.getAttribute("data-line");isNaN(n)||("CODE"===t.tagName&&t.parentElement&&"PRE"===t.parentElement.tagName?e.push({element:t.parentElement,line:n}):e.push({element:t,line:n}))}}return e}})();function a(e){const t=Math.floor(e),n=s();let o=n[0]||null;for(const e of n){if(e.line===t)return{previous:e,next:void 0};if(e.line>t)return{previous:o,next:e};o=e}return{previous:o}}function c(e){const t=s(),n=e-window.scrollY;let o=-1,r=t.length-1;for(;o+1<r;){const e=Math.floor((o+r)/2),i=u(t[e]);i.top+i.height>=n?r=e:o=e}const i=t[r],a=u(i);if(r>=1&&a.top>n){return{previous:t[o],next:i}}return r>1&&r<t.length&&a.top+a.height>n?{previous:i,next:t[r+1]}:{previous:i}}function u({element:e}){const t=e.getBoundingClientRect(),n=e.querySelector(`.${r}`);if(n){const e=n.getBoundingClientRect(),o=Math.max(1,e.top-t.top);return{top:t.top,height:o}}return t}t.getElementsForSourceLine=a,t.getLineElementsAtPageOffset=c,t.scrollToRevealSourceLine=function(e){if(!o.getSettings().scrollPreviewWithEditor)return;if(e<=0)return void window.scroll(window.scrollX,0);const{previous:t,next:n}=a(e);if(!t)return;let r=0;const i=u(t),s=i.top;if(n&&n.line!==t.line){r=s+(e-t.line)/(n.line-t.line)*(n.element.getBoundingClientRect().top-s)}else{const t=e-Math.floor(e);r=s+i.height*t}window.scroll(window.scrollX,Math.max(1,window.scrollY+r))},t.getEditorLineNumberForPageOffset=function(e){const{previous:t,next:n}=c(e);if(t){const o=u(t),r=e-window.scrollY-o.top;if(n){const e=r/(u(n).top-o.top);return i(t.line+e*(n.line-t.line))}{const e=r/o.height;return i(t.line+e)}}return null},t.getLineElementForFragment=function(e){return s().find(t=>t.element.id===e)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(3),r=n(4),i=n(5),s=n(1),a=n(0),c=n(6);let u=!0;const l=new o.ActiveLineMarker,f=a.getSettings(),d=acquireVsCodeApi();let g=a.getData("data-state");d.setState(g);const p=i.createPosterForVsCode(d);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=()=>{v()},r.onceDocumentLoaded(()=>{f.scrollPreviewWithEditor&&setTimeout(()=>{if(g.fragment){const e=s.getLineElementForFragment(g.fragment);e&&(u=!0,s.scrollToRevealSourceLine(e.line))}else{const e=+f.line;isNaN(e)||(u=!0,s.scrollToRevealSourceLine(e))}},0)});const m=(()=>{const e=c(e=>{u=!0,s.scrollToRevealSourceLine(e)},50);return(t,n)=>{isNaN(t)||(n.line=t,e(t))}})();let v=c(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n<t.length;n++){const o=t[n];o.classList.contains("loading")&&o.classList.remove("loading"),e.push({id:o.id,height:o.height,width:o.width})}p.postMessage("cacheImageSizes",e)}},50);window.addEventListener("resize",()=>{u=!0,v()},!0),window.addEventListener("message",e=>{if(e.data.source===f.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":m(e.data.line,f)}},!1),document.addEventListener("dblclick",e=>{if(!f.doubleClickToSwitchToEditor)return;for(let t=e.target;t;t=t.parentNode)if("A"===t.tagName)return;const t=e.pageY,n=s.getEditorLineNumberForPageOffset(t);"number"!=typeof n||isNaN(n)||p.postMessage("didClick",{line:Math.floor(n)})});const h=["http:","https:","mailto:","vscode:","vscode-insiders:"];document.addEventListener("click",e=>{if(!e)return;let t=e.target;for(;t;){if(t.tagName&&"A"===t.tagName&&t.href){if(t.getAttribute("href").startsWith("#"))return;if(h.some(e=>t.href.startsWith(e)))return;const n=t.getAttribute("data-href")||t.getAttribute("href");return/^[a-z\-]+:/i.test(n)?void 0:(p.postMessage("openLink",{href:n}),e.preventDefault(),void e.stopPropagation())}t=t.parentNode}},!0),window.addEventListener("scroll",c(()=>{if(u)u=!1;else{const e=s.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||(p.postMessage("revealLine",{line:e}),g.line=e,d.setState(g))}},50))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(1);t.ActiveLineMarker=class{onDidChangeTextEditorSelection(e){const{previous:t}=o.getElementsForSourceLine(e);this._update(t&&t.element)}_update(e){this._unmarkActiveElement(this._current),this._markActiveElement(e),this._current=e}_unmarkActiveElement(e){e&&(e.className=e.className.replace(/\bcode-active-line\b/g,""))}_markActiveElement(e){e&&(e.className+=" code-active-line")}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);t.createPosterForVsCode=e=>new class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}}},function(e,t,n){(function(t){var n="Expected a function",o=NaN,r="[object Symbol]",i=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,f="object"==typeof self&&self&&self.Object===Object&&self,d=l||f||Function("return this")(),g=Object.prototype.toString,p=Math.max,m=Math.min,v=function(){return d.Date.now()};function h(e,t,o){var r,i,s,a,c,u,l=0,f=!1,d=!1,g=!0;if("function"!=typeof e)throw new TypeError(n);function h(t){var n=r,o=i;return r=i=void 0,l=t,a=e.apply(o,n)}function y(e){var n=e-u;return void 0===u||n>=t||n<0||d&&e-l>=s}function E(){var e=v();if(y(e))return M(e);c=setTimeout(E,function(e){var n=t-(e-u);return d?m(n,s-(e-l)):n}(e))}function M(e){return c=void 0,g&&r?h(e):(r=i=void 0,a)}function S(){var e=v(),n=y(e);if(r=arguments,i=this,u=e,n){if(void 0===c)return function(e){return l=e,c=setTimeout(E,t),f?h(e):a}(u);if(d)return c=setTimeout(E,t),h(u)}return void 0===c&&(c=setTimeout(E,t)),a}return t=b(t)||0,w(o)&&(f=!!o.leading,s=(d="maxWait"in o)?p(b(o.maxWait)||0,t):s,g="trailing"in o?!!o.trailing:g),S.cancel=function(){void 0!==c&&clearTimeout(c),l=0,r=u=i=c=void 0},S.flush=function(){return void 0===c?a:M(v())},S}function w(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function b(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&g.call(e)==r}(e))return o;if(w(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=w(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(i,"");var n=a.test(e);return n||c.test(e)?u(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=function(e,t,o){var r=!0,i=!0;if("function"!=typeof e)throw new TypeError(n);return w(o)&&(r="leading"in o?!!o.leading:r,i="trailing"in o?!!o.trailing:i),h(e,t,{leading:r,maxWait:t,trailing:i})}}).call(this,n(7))},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2Nyb2xsLXN5bmMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvYWN0aXZlTGluZU1hcmtlci50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9ldmVudHMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvbWVzc2FnaW5nLnRzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2Rhc2gudGhyb3R0bGUvaW5kZXguanMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyJdLCJuYW1lcyI6WyJpbnN0YWxsZWRNb2R1bGVzIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiZXhwb3J0cyIsIm1vZHVsZSIsImkiLCJsIiwibW9kdWxlcyIsImNhbGwiLCJtIiwiYyIsImQiLCJuYW1lIiwiZ2V0dGVyIiwibyIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZW51bWVyYWJsZSIsImdldCIsInIiLCJTeW1ib2wiLCJ0b1N0cmluZ1RhZyIsInZhbHVlIiwidCIsIm1vZGUiLCJfX2VzTW9kdWxlIiwibnMiLCJjcmVhdGUiLCJrZXkiLCJiaW5kIiwibiIsIm9iamVjdCIsInByb3BlcnR5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJwIiwicyIsImNhY2hlZFNldHRpbmdzIiwidW5kZWZpbmVkIiwiZ2V0RGF0YSIsImVsZW1lbnQiLCJkb2N1bWVudCIsImdldEVsZW1lbnRCeUlkIiwiZGF0YSIsImdldEF0dHJpYnV0ZSIsIkpTT04iLCJwYXJzZSIsIkVycm9yIiwiZ2V0U2V0dGluZ3MiLCJzZXR0aW5nc18xIiwiY29kZUxpbmVDbGFzcyIsImNsYW1wTGluZSIsImxpbmUiLCJtaW4iLCJtYXgiLCJsaW5lQ291bnQiLCJNYXRoIiwiZ2V0Q29kZUxpbmVFbGVtZW50cyIsImVsZW1lbnRzIiwiYm9keSIsImdldEVsZW1lbnRzQnlDbGFzc05hbWUiLCJpc05hTiIsInRhZ05hbWUiLCJwYXJlbnRFbGVtZW50IiwicHVzaCIsImdldEVsZW1lbnRzRm9yU291cmNlTGluZSIsInRhcmdldExpbmUiLCJsaW5lTnVtYmVyIiwiZmxvb3IiLCJsaW5lcyIsInByZXZpb3VzIiwiZW50cnkiLCJuZXh0IiwiZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0Iiwib2Zmc2V0IiwicG9zaXRpb24iLCJ3aW5kb3ciLCJzY3JvbGxZIiwibG8iLCJoaSIsImxlbmd0aCIsIm1pZCIsImJvdW5kcyIsImdldEVsZW1lbnRCb3VuZHMiLCJ0b3AiLCJoZWlnaHQiLCJoaUVsZW1lbnQiLCJoaUJvdW5kcyIsIm15Qm91bmRzIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwiY29kZUxpbmVDaGlsZCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGlsZEJvdW5kcyIsInNjcm9sbFRvUmV2ZWFsU291cmNlTGluZSIsInNjcm9sbFByZXZpZXdXaXRoRWRpdG9yIiwic2Nyb2xsIiwic2Nyb2xsWCIsInNjcm9sbFRvIiwicmVjdCIsInByZXZpb3VzVG9wIiwicHJvZ3Jlc3NJbkVsZW1lbnQiLCJnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCIsInByZXZpb3VzQm91bmRzIiwib2Zmc2V0RnJvbVByZXZpb3VzIiwicHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMiLCJwcm9ncmVzc1dpdGhpbkVsZW1lbnQiLCJnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50IiwiZnJhZ21lbnQiLCJmaW5kIiwiaWQiLCJhY3RpdmVMaW5lTWFya2VyXzEiLCJldmVudHNfMSIsIm1lc3NhZ2luZ18xIiwic2Nyb2xsX3N5bmNfMSIsInRocm90dGxlIiwic2Nyb2xsRGlzYWJsZWQiLCJtYXJrZXIiLCJBY3RpdmVMaW5lTWFya2VyIiwic2V0dGluZ3MiLCJ2c2NvZGUiLCJhY3F1aXJlVnNDb2RlQXBpIiwic3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsIm9uY2VEb2N1bWVudExvYWRlZCIsInNldFRpbWVvdXQiLCJpbml0aWFsTGluZSIsIm9uVXBkYXRlVmlldyIsImRvU2Nyb2xsIiwiaW1hZ2VJbmZvIiwiaW1hZ2VzIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJpbWciLCJjbGFzc0xpc3QiLCJjb250YWlucyIsInJlbW92ZSIsIndpZHRoIiwicG9zdE1lc3NhZ2UiLCJhZGRFdmVudExpc3RlbmVyIiwiZXZlbnQiLCJzb3VyY2UiLCJ0eXBlIiwib25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uIiwiZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yIiwibm9kZSIsInRhcmdldCIsInBhcmVudE5vZGUiLCJwYWdlWSIsInBhc3NUaHJvdWdoTGlua1NjaGVtZXMiLCJocmVmIiwic3RhcnRzV2l0aCIsInNvbWUiLCJzY2hlbWUiLCJocmVmVGV4dCIsInRlc3QiLCJwcmV2ZW50RGVmYXVsdCIsInN0b3BQcm9wYWdhdGlvbiIsInRoaXMiLCJfdXBkYXRlIiwiYmVmb3JlIiwiX3VubWFya0FjdGl2ZUVsZW1lbnQiLCJfY3VycmVudCIsIl9tYXJrQWN0aXZlRWxlbWVudCIsImNsYXNzTmFtZSIsInJlcGxhY2UiLCJmIiwicmVhZHlTdGF0ZSIsIkZVTkNfRVJST1JfVEVYVCIsIk5BTiIsInN5bWJvbFRhZyIsInJlVHJpbSIsInJlSXNCYWRIZXgiLCJyZUlzQmluYXJ5IiwicmVJc09jdGFsIiwiZnJlZVBhcnNlSW50IiwicGFyc2VJbnQiLCJmcmVlR2xvYmFsIiwiZ2xvYmFsIiwiZnJlZVNlbGYiLCJzZWxmIiwicm9vdCIsIkZ1bmN0aW9uIiwib2JqZWN0VG9TdHJpbmciLCJ0b1N0cmluZyIsIm5hdGl2ZU1heCIsIm5hdGl2ZU1pbiIsIm5vdyIsIkRhdGUiLCJkZWJvdW5jZSIsImZ1bmMiLCJ3YWl0Iiwib3B0aW9ucyIsImxhc3RBcmdzIiwibGFzdFRoaXMiLCJtYXhXYWl0IiwicmVzdWx0IiwidGltZXJJZCIsImxhc3RDYWxsVGltZSIsImxhc3RJbnZva2VUaW1lIiwibGVhZGluZyIsIm1heGluZyIsInRyYWlsaW5nIiwiVHlwZUVycm9yIiwiaW52b2tlRnVuYyIsInRpbWUiLCJhcmdzIiwidGhpc0FyZyIsImFwcGx5Iiwic2hvdWxkSW52b2tlIiwidGltZVNpbmNlTGFzdENhbGwiLCJ0aW1lckV4cGlyZWQiLCJ0cmFpbGluZ0VkZ2UiLCJyZW1haW5pbmdXYWl0IiwiZGVib3VuY2VkIiwiaXNJbnZva2luZyIsImFyZ3VtZW50cyIsImxlYWRpbmdFZGdlIiwidG9OdW1iZXIiLCJpc09iamVjdCIsImNhbmNlbCIsImNsZWFyVGltZW91dCIsImZsdXNoIiwiaXNPYmplY3RMaWtlIiwiaXNTeW1ib2wiLCJvdGhlciIsInZhbHVlT2YiLCJpc0JpbmFyeSIsInNsaWNlIiwiZyIsImUiXSwibWFwcGluZ3MiOiJhQUNFLElBQUlBLEVBQW1CLEdBR3ZCLFNBQVNDLEVBQW9CQyxHQUc1QixHQUFHRixFQUFpQkUsR0FDbkIsT0FBT0YsRUFBaUJFLEdBQVVDLFFBR25DLElBQUlDLEVBQVNKLEVBQWlCRSxHQUFZLENBQ3pDRyxFQUFHSCxFQUNISSxHQUFHLEVBQ0hILFFBQVMsSUFVVixPQU5BSSxFQUFRTCxHQUFVTSxLQUFLSixFQUFPRCxRQUFTQyxFQUFRQSxFQUFPRCxRQUFTRixHQUcvREcsRUFBT0UsR0FBSSxFQUdKRixFQUFPRCxRQUtmRixFQUFvQlEsRUFBSUYsRUFHeEJOLEVBQW9CUyxFQUFJVixFQUd4QkMsRUFBb0JVLEVBQUksU0FBU1IsRUFBU1MsRUFBTUMsR0FDM0NaLEVBQW9CYSxFQUFFWCxFQUFTUyxJQUNsQ0csT0FBT0MsZUFBZWIsRUFBU1MsRUFBTSxDQUFFSyxZQUFZLEVBQU1DLElBQUtMLEtBS2hFWixFQUFvQmtCLEVBQUksU0FBU2hCLEdBQ1gsb0JBQVhpQixRQUEwQkEsT0FBT0MsYUFDMUNOLE9BQU9DLGVBQWViLEVBQVNpQixPQUFPQyxZQUFhLENBQUVDLE1BQU8sV0FFN0RQLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxLQVF2RHJCLEVBQW9Cc0IsRUFBSSxTQUFTRCxFQUFPRSxHQUV2QyxHQURVLEVBQVBBLElBQVVGLEVBQVFyQixFQUFvQnFCLElBQy9CLEVBQVBFLEVBQVUsT0FBT0YsRUFDcEIsR0FBVyxFQUFQRSxHQUE4QixpQkFBVkYsR0FBc0JBLEdBQVNBLEVBQU1HLFdBQVksT0FBT0gsRUFDaEYsSUFBSUksRUFBS1gsT0FBT1ksT0FBTyxNQUd2QixHQUZBMUIsRUFBb0JrQixFQUFFTyxHQUN0QlgsT0FBT0MsZUFBZVUsRUFBSSxVQUFXLENBQUVULFlBQVksRUFBTUssTUFBT0EsSUFDdEQsRUFBUEUsR0FBNEIsaUJBQVRGLEVBQW1CLElBQUksSUFBSU0sS0FBT04sRUFBT3JCLEVBQW9CVSxFQUFFZSxFQUFJRSxFQUFLLFNBQVNBLEdBQU8sT0FBT04sRUFBTU0sSUFBUUMsS0FBSyxLQUFNRCxJQUM5SSxPQUFPRixHQUlSekIsRUFBb0I2QixFQUFJLFNBQVMxQixHQUNoQyxJQUFJUyxFQUFTVCxHQUFVQSxFQUFPcUIsV0FDN0IsV0FBd0IsT0FBT3JCLEVBQWdCLFNBQy9DLFdBQThCLE9BQU9BLEdBRXRDLE9BREFILEVBQW9CVSxFQUFFRSxFQUFRLElBQUtBLEdBQzVCQSxHQUlSWixFQUFvQmEsRUFBSSxTQUFTaUIsRUFBUUMsR0FBWSxPQUFPakIsT0FBT2tCLFVBQVVDLGVBQWUxQixLQUFLdUIsRUFBUUMsSUFHekcvQixFQUFvQmtDLEVBQUksR0FJakJsQyxFQUFvQkEsRUFBb0JtQyxFQUFJLEcsK0JDN0VyRHJCLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQUN0RCxJQUFJZSxPQUFpQkMsRUFDckIsU0FBU0MsRUFBUVgsR0FDYixNQUFNWSxFQUFVQyxTQUFTQyxlQUFlLGdDQUN4QyxHQUFJRixFQUFTLENBQ1QsTUFBTUcsRUFBT0gsRUFBUUksYUFBYWhCLEdBQ2xDLEdBQUllLEVBQ0EsT0FBT0UsS0FBS0MsTUFBTUgsR0FHMUIsTUFBTSxJQUFJSSxNQUFNLDJCQUEyQm5CLEtBRS9DekIsRUFBUW9DLFFBQVVBLEVBV2xCcEMsRUFBUTZDLFlBVlIsV0FDSSxHQUFJWCxFQUNBLE9BQU9BLEVBR1gsR0FEQUEsRUFBaUJFLEVBQVEsaUJBRXJCLE9BQU9GLEVBRVgsTUFBTSxJQUFJVSxNQUFNLDZCLDZCQ3JCcEJoQyxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFDdEQsTUFBTTJCLEVBQWEsRUFBUSxHQUNyQkMsRUFBZ0IsWUFJdEIsU0FBU0MsRUFBVUMsR0FDZixPQUpXQyxFQUlFLEVBSkdDLEVBSUFMLEVBQVdELGNBQWNPLFVBQVksRUFKaENqQyxFQUltQzhCLEVBSGpESSxLQUFLSCxJQUFJQyxFQUFLRSxLQUFLRixJQUFJRCxFQUFLL0IsSUFEdkMsSUFBZStCLEVBQUtDLEVBQUtoQyxFQU16QixNQUFNbUMsRUFBc0IsTUFDeEIsSUFBSUMsRUFDSixNQUFPLEtBQ0gsSUFBS0EsRUFBVSxDQUNYQSxFQUFXLENBQUMsQ0FBRWxCLFFBQVNDLFNBQVNrQixLQUFNUCxLQUFNLElBQzVDLElBQUssTUFBTVosS0FBV0MsU0FBU21CLHVCQUF1QlYsR0FBZ0IsQ0FDbEUsTUFBTUUsR0FBUVosRUFBUUksYUFBYSxhQUMvQmlCLE1BQU1ULEtBR2MsU0FBcEJaLEVBQVFzQixTQUFzQnRCLEVBQVF1QixlQUFtRCxRQUFsQ3ZCLEVBQVF1QixjQUFjRCxRQUc3RUosRUFBU00sS0FBSyxDQUFFeEIsUUFBU0EsRUFBUXVCLGNBQWVYLFNBR2hETSxFQUFTTSxLQUFLLENBQUV4QixRQUFTQSxFQUFTWSxXQUk5QyxPQUFPTSxJQXBCYSxHQTZCNUIsU0FBU08sRUFBeUJDLEdBQzlCLE1BQU1DLEVBQWFYLEtBQUtZLE1BQU1GLEdBQ3hCRyxFQUFRWixJQUNkLElBQUlhLEVBQVdELEVBQU0sSUFBTSxLQUMzQixJQUFLLE1BQU1FLEtBQVNGLEVBQU8sQ0FDdkIsR0FBSUUsRUFBTW5CLE9BQVNlLEVBQ2YsTUFBTyxDQUFFRyxTQUFVQyxFQUFPQyxVQUFNbEMsR0FFL0IsR0FBSWlDLEVBQU1uQixLQUFPZSxFQUNsQixNQUFPLENBQUVHLFdBQVVFLEtBQU1ELEdBRTdCRCxFQUFXQyxFQUVmLE1BQU8sQ0FBRUQsWUFNYixTQUFTRyxFQUE0QkMsR0FDakMsTUFBTUwsRUFBUVosSUFDUmtCLEVBQVdELEVBQVNFLE9BQU9DLFFBQ2pDLElBQUlDLEdBQU0sRUFDTkMsRUFBS1YsRUFBTVcsT0FBUyxFQUN4QixLQUFPRixFQUFLLEVBQUlDLEdBQUksQ0FDaEIsTUFBTUUsRUFBTXpCLEtBQUtZLE9BQU9VLEVBQUtDLEdBQU0sR0FDN0JHLEVBQVNDLEVBQWlCZCxFQUFNWSxJQUNsQ0MsRUFBT0UsSUFBTUYsRUFBT0csUUFBVVYsRUFDOUJJLEVBQUtFLEVBR0xILEVBQUtHLEVBR2IsTUFBTUssRUFBWWpCLEVBQU1VLEdBQ2xCUSxFQUFXSixFQUFpQkcsR0FDbEMsR0FBSVAsR0FBTSxHQUFLUSxFQUFTSCxJQUFNVCxFQUFVLENBRXBDLE1BQU8sQ0FBRUwsU0FEU0QsRUFBTVMsR0FDTU4sS0FBTWMsR0FFeEMsT0FBSVAsRUFBSyxHQUFLQSxFQUFLVixFQUFNVyxRQUFVTyxFQUFTSCxJQUFNRyxFQUFTRixPQUFTVixFQUN6RCxDQUFFTCxTQUFVZ0IsRUFBV2QsS0FBTUgsRUFBTVUsRUFBSyxJQUU1QyxDQUFFVCxTQUFVZ0IsR0FHdkIsU0FBU0gsR0FBaUIsUUFBRTNDLElBQ3hCLE1BQU1nRCxFQUFXaEQsRUFBUWlELHdCQUduQkMsRUFBZ0JsRCxFQUFRbUQsY0FBYyxJQUFJekMsS0FDaEQsR0FBSXdDLEVBQWUsQ0FDZixNQUFNRSxFQUFjRixFQUFjRCx3QkFDNUJKLEVBQVM3QixLQUFLRixJQUFJLEVBQUlzQyxFQUFZUixJQUFNSSxFQUFTSixLQUN2RCxNQUFPLENBQ0hBLElBQUtJLEVBQVNKLElBQ2RDLE9BQVFBLEdBR2hCLE9BQU9HLEVBNUNYckYsRUFBUThELHlCQUEyQkEsRUE4Qm5DOUQsRUFBUXNFLDRCQUE4QkEsRUE4Q3RDdEUsRUFBUTBGLHlCQTNCUixTQUFrQ3pDLEdBQzlCLElBQUtILEVBQVdELGNBQWM4Qyx3QkFDMUIsT0FFSixHQUFJMUMsR0FBUSxFQUVSLFlBREF3QixPQUFPbUIsT0FBT25CLE9BQU9vQixRQUFTLEdBR2xDLE1BQU0sU0FBRTFCLEVBQVEsS0FBRUUsR0FBU1AsRUFBeUJiLEdBQ3BELElBQUtrQixFQUNELE9BRUosSUFBSTJCLEVBQVcsRUFDZixNQUFNQyxFQUFPZixFQUFpQmIsR0FDeEI2QixFQUFjRCxFQUFLZCxJQUN6QixHQUFJWixHQUFRQSxFQUFLcEIsT0FBU2tCLEVBQVNsQixLQUFNLENBSXJDNkMsRUFBV0UsR0FGYy9DLEVBQU9rQixFQUFTbEIsT0FBU29CLEVBQUtwQixLQUFPa0IsRUFBU2xCLE9BQ2pEb0IsRUFBS2hDLFFBQVFpRCx3QkFBd0JMLElBQU1lLE9BR2hFLENBQ0QsTUFBTUMsRUFBb0JoRCxFQUFPSSxLQUFLWSxNQUFNaEIsR0FDNUM2QyxFQUFXRSxFQUFlRCxFQUFLYixPQUFTZSxFQUU1Q3hCLE9BQU9tQixPQUFPbkIsT0FBT29CLFFBQVN4QyxLQUFLRixJQUFJLEVBQUdzQixPQUFPQyxRQUFVb0IsS0FxQi9EOUYsRUFBUWtHLGlDQWxCUixTQUEwQzNCLEdBQ3RDLE1BQU0sU0FBRUosRUFBUSxLQUFFRSxHQUFTQyxFQUE0QkMsR0FDdkQsR0FBSUosRUFBVSxDQUNWLE1BQU1nQyxFQUFpQm5CLEVBQWlCYixHQUNsQ2lDLEVBQXNCN0IsRUFBU0UsT0FBT0MsUUFBVXlCLEVBQWVsQixJQUNyRSxHQUFJWixFQUFNLENBQ04sTUFBTWdDLEVBQTBCRCxHQUFzQnBCLEVBQWlCWCxHQUFNWSxJQUFNa0IsRUFBZWxCLEtBRWxHLE9BQU9qQyxFQURNbUIsRUFBU2xCLEtBQU9vRCxHQUEyQmhDLEVBQUtwQixLQUFPa0IsRUFBU2xCLE9BRzVFLENBQ0QsTUFBTXFELEVBQXdCRixFQUFzQkQsRUFBcUIsT0FFekUsT0FBT25ELEVBRE1tQixFQUFTbEIsS0FBT3FELElBSXJDLE9BQU8sTUFXWHRHLEVBQVF1RywwQkFMUixTQUFtQ0MsR0FDL0IsT0FBT2xELElBQXNCbUQsS0FBTXBFLEdBQ3hCQSxFQUFRQSxRQUFRcUUsS0FBT0YsSyw2QkMxSnRDNUYsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBQ3RELE1BQU13RixFQUFxQixFQUFRLEdBQzdCQyxFQUFXLEVBQVEsR0FDbkJDLEVBQWMsRUFBUSxHQUN0QkMsRUFBZ0IsRUFBUSxHQUN4QmhFLEVBQWEsRUFBUSxHQUNyQmlFLEVBQVcsRUFBUSxHQUN6QixJQUFJQyxHQUFpQixFQUNyQixNQUFNQyxFQUFTLElBQUlOLEVBQW1CTyxpQkFDaENDLEVBQVdyRSxFQUFXRCxjQUN0QnVFLEVBQVNDLG1CQUVmLElBQUlDLEVBQVF4RSxFQUFXVixRQUFRLGNBQy9CZ0YsRUFBT0csU0FBU0QsR0FDaEIsTUFBTUUsRUFBWVgsRUFBWVksc0JBQXNCTCxHQUNwRDNDLE9BQU9pRCxXQUFXQyxVQUFVSCxHQUM1Qi9DLE9BQU9tRCxvQkFBb0JELFVBQVVILEdBQ3JDL0MsT0FBT29ELE9BQVMsS0FDWkMsS0FFSmxCLEVBQVNtQixtQkFBbUIsS0FDcEJaLEVBQVN4Qix5QkFDVHFDLFdBQVcsS0FFUCxHQUFJVixFQUFNZCxTQUFVLENBQ2hCLE1BQU1uRSxFQUFVeUUsRUFBY1AsMEJBQTBCZSxFQUFNZCxVQUMxRG5FLElBQ0EyRSxHQUFpQixFQUNqQkYsRUFBY3BCLHlCQUF5QnJELEVBQVFZLFdBR2xELENBQ0QsTUFBTWdGLEdBQWVkLEVBQVNsRSxLQUN6QlMsTUFBTXVFLEtBQ1BqQixHQUFpQixFQUNqQkYsRUFBY3BCLHlCQUF5QnVDLE1BR2hELEtBR1gsTUFBTUMsRUFBZSxNQUNqQixNQUFNQyxFQUFXcEIsRUFBVTlELElBQ3ZCK0QsR0FBaUIsRUFDakJGLEVBQWNwQix5QkFBeUJ6QyxJQUN4QyxJQUNILE1BQU8sQ0FBQ0EsRUFBTWtFLEtBQ0x6RCxNQUFNVCxLQUNQa0UsRUFBU2xFLEtBQU9BLEVBQ2hCa0YsRUFBU2xGLE1BUkEsR0FZckIsSUFBSTZFLEVBQW1CZixFQUFTLEtBQzVCLE1BQU1xQixFQUFZLEdBQ2xCLElBQUlDLEVBQVMvRixTQUFTZ0cscUJBQXFCLE9BQzNDLEdBQUlELEVBQVEsQ0FDUixJQUFJbkksRUFDSixJQUFLQSxFQUFJLEVBQUdBLEVBQUltSSxFQUFPeEQsT0FBUTNFLElBQUssQ0FDaEMsTUFBTXFJLEVBQU1GLEVBQU9uSSxHQUNmcUksRUFBSUMsVUFBVUMsU0FBUyxZQUN2QkYsRUFBSUMsVUFBVUUsT0FBTyxXQUV6Qk4sRUFBVXZFLEtBQUssQ0FDWDZDLEdBQUk2QixFQUFJN0IsR0FDUnhCLE9BQVFxRCxFQUFJckQsT0FDWnlELE1BQU9KLEVBQUlJLFFBR25CbkIsRUFBVW9CLFlBQVksa0JBQW1CUixLQUU5QyxJQUNIM0QsT0FBT29FLGlCQUFpQixTQUFVLEtBQzlCN0IsR0FBaUIsRUFDakJjLE1BQ0QsR0FDSHJELE9BQU9vRSxpQkFBaUIsVUFBV0MsSUFDL0IsR0FBSUEsRUFBTXRHLEtBQUt1RyxTQUFXNUIsRUFBUzRCLE9BR25DLE9BQVFELEVBQU10RyxLQUFLd0csTUFDZixJQUFLLGlDQUNEL0IsRUFBT2dDLCtCQUErQkgsRUFBTXRHLEtBQUtTLE1BQ2pELE1BQ0osSUFBSyxhQUNEaUYsRUFBYVksRUFBTXRHLEtBQUtTLEtBQU1rRSxNQUd2QyxHQUNIN0UsU0FBU3VHLGlCQUFpQixXQUFZQyxJQUNsQyxJQUFLM0IsRUFBUytCLDRCQUNWLE9BR0osSUFBSyxJQUFJQyxFQUFPTCxFQUFNTSxPQUFRRCxFQUFNQSxFQUFPQSxFQUFLRSxXQUM1QyxHQUFxQixNQUFqQkYsRUFBS3hGLFFBQ0wsT0FHUixNQUFNWSxFQUFTdUUsRUFBTVEsTUFDZnJHLEVBQU82RCxFQUFjWixpQ0FBaUMzQixHQUN4QyxpQkFBVHRCLEdBQXNCUyxNQUFNVCxJQUNuQ3VFLEVBQVVvQixZQUFZLFdBQVksQ0FBRTNGLEtBQU1JLEtBQUtZLE1BQU1oQixPQUc3RCxNQUFNc0csRUFBeUIsQ0FBQyxRQUFTLFNBQVUsVUFBVyxVQUFXLG9CQUN6RWpILFNBQVN1RyxpQkFBaUIsUUFBU0MsSUFDL0IsSUFBS0EsRUFDRCxPQUVKLElBQUlLLEVBQU9MLEVBQU1NLE9BQ2pCLEtBQU9ELEdBQU0sQ0FDVCxHQUFJQSxFQUFLeEYsU0FBNEIsTUFBakJ3RixFQUFLeEYsU0FBbUJ3RixFQUFLSyxLQUFNLENBQ25ELEdBQUlMLEVBQUsxRyxhQUFhLFFBQVFnSCxXQUFXLEtBQ3JDLE9BR0osR0FBSUYsRUFBdUJHLEtBQUtDLEdBQVVSLEVBQUtLLEtBQUtDLFdBQVdFLElBQzNELE9BRUosTUFBTUMsRUFBV1QsRUFBSzFHLGFBQWEsY0FBZ0IwRyxFQUFLMUcsYUFBYSxRQUVyRSxNQUFLLGNBQWNvSCxLQUFLRCxRQU14QixHQUxJcEMsRUFBVW9CLFlBQVksV0FBWSxDQUFFWSxLQUFNSSxJQUMxQ2QsRUFBTWdCLHNCQUNOaEIsRUFBTWlCLG1CQUtkWixFQUFPQSxFQUFLRSxjQUVqQixHQUNINUUsT0FBT29FLGlCQUFpQixTQUFVOUIsRUFBUyxLQUN2QyxHQUFJQyxFQUNBQSxHQUFpQixNQUVoQixDQUNELE1BQU0vRCxFQUFPNkQsRUFBY1osaUNBQWlDekIsT0FBT0MsU0FDL0MsaUJBQVR6QixHQUFzQlMsTUFBTVQsS0FDbkN1RSxFQUFVb0IsWUFBWSxhQUFjLENBQUUzRixTQUN0Q3FFLEVBQU1yRSxLQUFPQSxFQUNibUUsRUFBT0csU0FBU0QsTUFHekIsTSw2QkNySkgxRyxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFLdEQsTUFBTTJGLEVBQWdCLEVBQVEsR0F3QjlCOUcsRUFBUWtILGlCQXZCUixNQUNJLCtCQUErQmpFLEdBQzNCLE1BQU0sU0FBRWtCLEdBQWEyQyxFQUFjaEQseUJBQXlCYixHQUM1RCtHLEtBQUtDLFFBQVE5RixHQUFZQSxFQUFTOUIsU0FFdEMsUUFBUTZILEdBQ0pGLEtBQUtHLHFCQUFxQkgsS0FBS0ksVUFDL0JKLEtBQUtLLG1CQUFtQkgsR0FDeEJGLEtBQUtJLFNBQVdGLEVBRXBCLHFCQUFxQjdILEdBQ1pBLElBR0xBLEVBQVFpSSxVQUFZakksRUFBUWlJLFVBQVVDLFFBQVEsd0JBQXlCLEtBRTNFLG1CQUFtQmxJLEdBQ1ZBLElBR0xBLEVBQVFpSSxXQUFhLHdCLDZCQ3RCN0IxSixPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFTdERuQixFQUFRK0gsbUJBUlIsU0FBNEJ5QyxHQUNJLFlBQXhCbEksU0FBU21JLFlBQW9ELGtCQUF4Qm5JLFNBQVNtSSxXQUM5Q25JLFNBQVN1RyxpQkFBaUIsbUJBQW9CMkIsR0FHOUNBLE0sNkJDTlI1SixPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFDdEQsTUFBTTJCLEVBQWEsRUFBUSxHQUMzQjlDLEVBQVF5SCxzQkFBeUJMLEdBQ3RCLElBQUksTUFDUCxZQUFZNEIsRUFBTXhGLEdBQ2Q0RCxFQUFPd0IsWUFBWSxDQUNmSSxPQUNBRCxPQUFRakcsRUFBV0QsY0FBY2tHLE9BQ2pDdkYsWSxpQkNiaEIsWUFVQSxJQUFJa0gsRUFBa0Isc0JBR2xCQyxFQUFNLElBR05DLEVBQVksa0JBR1pDLEVBQVMsYUFHVEMsRUFBYSxxQkFHYkMsRUFBYSxhQUdiQyxFQUFZLGNBR1pDLEVBQWVDLFNBR2ZDLEVBQThCLGlCQUFWQyxHQUFzQkEsR0FBVUEsRUFBT3hLLFNBQVdBLFFBQVV3SyxFQUdoRkMsRUFBMEIsaUJBQVJDLE1BQW9CQSxNQUFRQSxLQUFLMUssU0FBV0EsUUFBVTBLLEtBR3hFQyxFQUFPSixHQUFjRSxHQUFZRyxTQUFTLGNBQVRBLEdBVWpDQyxFQVBjN0ssT0FBT2tCLFVBT1E0SixTQUc3QkMsRUFBWXRJLEtBQUtGLElBQ2pCeUksRUFBWXZJLEtBQUtILElBa0JqQjJJLEVBQU0sV0FDUixPQUFPTixFQUFLTyxLQUFLRCxPQXlEbkIsU0FBU0UsRUFBU0MsRUFBTUMsRUFBTUMsR0FDNUIsSUFBSUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFBaUIsRUFDakJDLEdBQVUsRUFDVkMsR0FBUyxFQUNUQyxHQUFXLEVBRWYsR0FBbUIsbUJBQVJaLEVBQ1QsTUFBTSxJQUFJYSxVQUFVbkMsR0FVdEIsU0FBU29DLEVBQVdDLEdBQ2xCLElBQUlDLEVBQU9iLEVBQ1BjLEVBQVViLEVBS2QsT0FIQUQsRUFBV0MsT0FBV2pLLEVBQ3RCc0ssRUFBaUJNLEVBQ2pCVCxFQUFTTixFQUFLa0IsTUFBTUQsRUFBU0QsR0FxQi9CLFNBQVNHLEVBQWFKLEdBQ3BCLElBQUlLLEVBQW9CTCxFQUFPUCxFQU0vQixZQUF5QnJLLElBQWpCcUssR0FBK0JZLEdBQXFCbkIsR0FDekRtQixFQUFvQixHQUFPVCxHQU5KSSxFQUFPTixHQU04QkosRUFHakUsU0FBU2dCLElBQ1AsSUFBSU4sRUFBT2xCLElBQ1gsR0FBSXNCLEVBQWFKLEdBQ2YsT0FBT08sRUFBYVAsR0FHdEJSLEVBQVV2RSxXQUFXcUYsRUF6QnZCLFNBQXVCTixHQUNyQixJQUVJVCxFQUFTTCxHQUZXYyxFQUFPUCxHQUkvQixPQUFPRyxFQUFTZixFQUFVVSxFQUFRRCxHQUhSVSxFQUFPTixJQUdrQ0gsRUFvQmhDaUIsQ0FBY1IsSUFHbkQsU0FBU08sRUFBYVAsR0FLcEIsT0FKQVIsT0FBVXBLLEVBSU55SyxHQUFZVCxFQUNQVyxFQUFXQyxJQUVwQlosRUFBV0MsT0FBV2pLLEVBQ2ZtSyxHQWVULFNBQVNrQixJQUNQLElBQUlULEVBQU9sQixJQUNQNEIsRUFBYU4sRUFBYUosR0FNOUIsR0FKQVosRUFBV3VCLFVBQ1h0QixFQUFXcEMsS0FDWHdDLEVBQWVPLEVBRVhVLEVBQVksQ0FDZCxRQUFnQnRMLElBQVpvSyxFQUNGLE9BdkVOLFNBQXFCUSxHQU1uQixPQUpBTixFQUFpQk0sRUFFakJSLEVBQVV2RSxXQUFXcUYsRUFBY3BCLEdBRTVCUyxFQUFVSSxFQUFXQyxHQUFRVCxFQWlFekJxQixDQUFZbkIsR0FFckIsR0FBSUcsRUFHRixPQURBSixFQUFVdkUsV0FBV3FGLEVBQWNwQixHQUM1QmEsRUFBV04sR0FNdEIsWUFIZ0JySyxJQUFab0ssSUFDRkEsRUFBVXZFLFdBQVdxRixFQUFjcEIsSUFFOUJLLEVBSVQsT0F4R0FMLEVBQU8yQixFQUFTM0IsSUFBUyxFQUNyQjRCLEVBQVMzQixLQUNYUSxJQUFZUixFQUFRUSxRQUVwQkwsR0FEQU0sRUFBUyxZQUFhVCxHQUNIUCxFQUFVaUMsRUFBUzFCLEVBQVFHLFVBQVksRUFBR0osR0FBUUksRUFDckVPLEVBQVcsYUFBY1YsSUFBWUEsRUFBUVUsU0FBV0EsR0FpRzFEWSxFQUFVTSxPQW5DVixnQkFDa0IzTCxJQUFab0ssR0FDRndCLGFBQWF4QixHQUVmRSxFQUFpQixFQUNqQk4sRUFBV0ssRUFBZUosRUFBV0csT0FBVXBLLEdBK0JqRHFMLEVBQVVRLE1BNUJWLFdBQ0UsWUFBbUI3TCxJQUFab0ssRUFBd0JELEVBQVNnQixFQUFhekIsTUE0QmhEMkIsRUEwRlQsU0FBU0ssRUFBUzFNLEdBQ2hCLElBQUk2SCxTQUFjN0gsRUFDbEIsUUFBU0EsSUFBa0IsVUFBUjZILEdBQTRCLFlBQVJBLEdBNEV6QyxTQUFTNEUsRUFBU3pNLEdBQ2hCLEdBQW9CLGlCQUFUQSxFQUNULE9BQU9BLEVBRVQsR0FoQ0YsU0FBa0JBLEdBQ2hCLE1BQXVCLGlCQUFUQSxHQXRCaEIsU0FBc0JBLEdBQ3BCLFFBQVNBLEdBQXlCLGlCQUFUQSxFQXNCdEI4TSxDQUFhOU0sSUFBVXNLLEVBQWVwTCxLQUFLYyxJQUFVeUosRUE4QnBEc0QsQ0FBUy9NLEdBQ1gsT0FBT3dKLEVBRVQsR0FBSWtELEVBQVMxTSxHQUFRLENBQ25CLElBQUlnTixFQUFnQyxtQkFBakJoTixFQUFNaU4sUUFBd0JqTixFQUFNaU4sVUFBWWpOLEVBQ25FQSxFQUFRME0sRUFBU00sR0FBVUEsRUFBUSxHQUFNQSxFQUUzQyxHQUFvQixpQkFBVGhOLEVBQ1QsT0FBaUIsSUFBVkEsRUFBY0EsR0FBU0EsRUFFaENBLEVBQVFBLEVBQU1vSixRQUFRTSxFQUFRLElBQzlCLElBQUl3RCxFQUFXdEQsRUFBV2xCLEtBQUsxSSxHQUMvQixPQUFRa04sR0FBWXJELEVBQVVuQixLQUFLMUksR0FDL0I4SixFQUFhOUosRUFBTW1OLE1BQU0sR0FBSUQsRUFBVyxFQUFJLEdBQzNDdkQsRUFBV2pCLEtBQUsxSSxHQUFTd0osR0FBT3hKLEVBR3ZDbEIsRUFBT0QsUUE5SVAsU0FBa0JnTSxFQUFNQyxFQUFNQyxHQUM1QixJQUFJUSxHQUFVLEVBQ1ZFLEdBQVcsRUFFZixHQUFtQixtQkFBUlosRUFDVCxNQUFNLElBQUlhLFVBQVVuQyxHQU10QixPQUpJbUQsRUFBUzNCLEtBQ1hRLEVBQVUsWUFBYVIsSUFBWUEsRUFBUVEsUUFBVUEsRUFDckRFLEVBQVcsYUFBY1YsSUFBWUEsRUFBUVUsU0FBV0EsR0FFbkRiLEVBQVNDLEVBQU1DLEVBQU0sQ0FDMUIsUUFBV1MsRUFDWCxRQUFXVCxFQUNYLFNBQVlXLE8sK0JDdFRoQixJQUFJMkIsRUFHSkEsRUFBSSxXQUNILE9BQU92RSxLQURKLEdBSUosSUFFQ3VFLEVBQUlBLEdBQUssSUFBSS9DLFNBQVMsY0FBYixHQUNSLE1BQU9nRCxHQUVjLGlCQUFYL0osU0FBcUI4SixFQUFJOUosUUFPckN4RSxFQUFPRCxRQUFVdU8iLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMik7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xubGV0IGNhY2hlZFNldHRpbmdzID0gdW5kZWZpbmVkO1xuZnVuY3Rpb24gZ2V0RGF0YShrZXkpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICBjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5leHBvcnRzLmdldERhdGEgPSBnZXREYXRhO1xuZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKSB7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgY2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuZXhwb3J0cy5nZXRTZXR0aW5ncyA9IGdldFNldHRpbmdzO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmNvbnN0IGNvZGVMaW5lQ2xhc3MgPSAnY29kZS1saW5lJztcbmZ1bmN0aW9uIGNsYW1wKG1pbiwgbWF4LCB2YWx1ZSkge1xuICAgIHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lKSB7XG4gICAgcmV0dXJuIGNsYW1wKDAsIHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuICAgIGxldCBlbGVtZW50cztcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBpZiAoIWVsZW1lbnRzKSB7XG4gICAgICAgICAgICBlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShjb2RlTGluZUNsYXNzKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpO1xuICAgICAgICAgICAgICAgIGlmIChpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGVsZW1lbnQudGFnTmFtZSA9PT0gJ0NPREUnICYmIGVsZW1lbnQucGFyZW50RWxlbWVudCAmJiBlbGVtZW50LnBhcmVudEVsZW1lbnQudGFnTmFtZSA9PT0gJ1BSRScpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gRmVuY2hlZCBjb2RlIGJsb2NrcyBhcmUgYSBzcGVjaWFsIGNhc2Ugc2luY2UgdGhlIGBjb2RlLWxpbmVgIGNhbiBvbmx5IGJlIG1hcmtlZCBvblxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgYDxjb2RlPmAgZWxlbWVudCBhbmQgbm90IHRoZSBwYXJlbnQgYDxwcmU+YCBlbGVtZW50LlxuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudC5wYXJlbnRFbGVtZW50LCBsaW5lIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQsIGxpbmUgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBlbGVtZW50cztcbiAgICB9O1xufSkoKTtcbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5mdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZSkge1xuICAgIGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuICAgIGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuICAgIGxldCBwcmV2aW91cyA9IGxpbmVzWzBdIHx8IG51bGw7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuICAgICAgICBpZiAoZW50cnkubGluZSA9PT0gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGVudHJ5LCBuZXh0OiB1bmRlZmluZWQgfTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG4gICAgICAgIH1cbiAgICAgICAgcHJldmlvdXMgPSBlbnRyeTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cbmV4cG9ydHMuZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgY29uc3QgcG9zaXRpb24gPSBvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWTtcbiAgICBsZXQgbG8gPSAtMTtcbiAgICBsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuICAgIHdoaWxlIChsbyArIDEgPCBoaSkge1xuICAgICAgICBjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuICAgICAgICBjb25zdCBib3VuZHMgPSBnZXRFbGVtZW50Qm91bmRzKGxpbmVzW21pZF0pO1xuICAgICAgICBpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcbiAgICAgICAgICAgIGhpID0gbWlkO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbG8gPSBtaWQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuICAgIGNvbnN0IGhpQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhoaUVsZW1lbnQpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIGlmIChoaSA+IDEgJiYgaGkgPCBsaW5lcy5sZW5ndGggJiYgaGlCb3VuZHMudG9wICsgaGlCb3VuZHMuaGVpZ2h0ID4gcG9zaXRpb24pIHtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGhpRWxlbWVudCwgbmV4dDogbGluZXNbaGkgKyAxXSB9O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5leHBvcnRzLmdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldDtcbmZ1bmN0aW9uIGdldEVsZW1lbnRCb3VuZHMoeyBlbGVtZW50IH0pIHtcbiAgICBjb25zdCBteUJvdW5kcyA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgLy8gU29tZSBjb2RlIGxpbmUgZWxlbWVudHMgbWF5IGNvbnRhaW4gb3RoZXIgY29kZSBsaW5lIGVsZW1lbnRzLlxuICAgIC8vIEluIHRob3NlIGNhc2VzLCBvbmx5IHRha2UgdGhlIGhlaWdodCB1cCB0byB0aGF0IGNoaWxkLlxuICAgIGNvbnN0IGNvZGVMaW5lQ2hpbGQgPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoYC4ke2NvZGVMaW5lQ2xhc3N9YCk7XG4gICAgaWYgKGNvZGVMaW5lQ2hpbGQpIHtcbiAgICAgICAgY29uc3QgY2hpbGRCb3VuZHMgPSBjb2RlTGluZUNoaWxkLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBoZWlnaHQgPSBNYXRoLm1heCgxLCAoY2hpbGRCb3VuZHMudG9wIC0gbXlCb3VuZHMudG9wKSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0b3A6IG15Qm91bmRzLnRvcCxcbiAgICAgICAgICAgIGhlaWdodDogaGVpZ2h0XG4gICAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiBteUJvdW5kcztcbn1cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmUpIHtcbiAgICBpZiAoIXNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChsaW5lIDw9IDApIHtcbiAgICAgICAgd2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgIGlmICghcHJldmlvdXMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgc2Nyb2xsVG8gPSAwO1xuICAgIGNvbnN0IHJlY3QgPSBnZXRFbGVtZW50Qm91bmRzKHByZXZpb3VzKTtcbiAgICBjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuICAgIGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuICAgICAgICAvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuICAgICAgICBjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICBjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IHByb2dyZXNzSW5FbGVtZW50ID0gbGluZSAtIE1hdGguZmxvb3IobGluZSk7XG4gICAgICAgIHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG4gICAgfVxuICAgIHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cbmV4cG9ydHMuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lID0gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lO1xuZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHByZXZpb3VzKSB7XG4gICAgICAgIGNvbnN0IHByZXZpb3VzQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhwcmV2aW91cyk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChnZXRFbGVtZW50Qm91bmRzKG5leHQpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIGNsYW1wTGluZShsaW5lKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuICAgICAgICAgICAgY29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufVxuZXhwb3J0cy5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0O1xuLyoqXG4gKiBUcnkgdG8gZmluZCB0aGUgaHRtbCBlbGVtZW50IGJ5IHVzaW5nIGEgZnJhZ21lbnQgaWRcbiAqL1xuZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudChmcmFnbWVudCkge1xuICAgIHJldHVybiBnZXRDb2RlTGluZUVsZW1lbnRzKCkuZmluZCgoZWxlbWVudCkgPT4ge1xuICAgICAgICByZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcbiAgICB9KTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAoc3RhdGUuZnJhZ21lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHN0YXRlLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGluaXRpYWxMaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDApO1xuICAgIH1cbn0pO1xuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcbiAgICBjb25zdCBkb1Njcm9sbCA9IHRocm90dGxlKChsaW5lKSA9PiB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgc2Nyb2xsX3N5bmNfMS5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG4gICAgfSwgNTApO1xuICAgIHJldHVybiAobGluZSwgc2V0dGluZ3MpID0+IHtcbiAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgc2V0dGluZ3MubGluZSA9IGxpbmU7XG4gICAgICAgICAgICBkb1Njcm9sbChsaW5lKTtcbiAgICAgICAgfVxuICAgIH07XG59KSgpO1xubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG4gICAgY29uc3QgaW1hZ2VJbmZvID0gW107XG4gICAgbGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcbiAgICBpZiAoaW1hZ2VzKSB7XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW1hZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBpbWFnZXNbaV07XG4gICAgICAgICAgICBpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG4gICAgICAgICAgICAgICAgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2xvYWRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGltYWdlSW5mby5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogaW1nLmlkLFxuICAgICAgICAgICAgICAgIGhlaWdodDogaW1nLmhlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogaW1nLndpZHRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG4gICAgfVxufSwgNTApO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcbiAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59LCBmYWxzZSk7XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3NcbiAgICBmb3IgKGxldCBub2RlID0gZXZlbnQudGFyZ2V0OyBub2RlOyBub2RlID0gbm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcbiAgICB9XG59KTtcbmNvbnN0IHBhc3NUaHJvdWdoTGlua1NjaGVtZXMgPSBbJ2h0dHA6JywgJ2h0dHBzOicsICdtYWlsdG86JywgJ3ZzY29kZTonLCAndnNjb2RlLWluc2lkZXJzOiddO1xuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBldmVudCA9PiB7XG4gICAgaWYgKCFldmVudCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBub2RlID0gZXZlbnQudGFyZ2V0O1xuICAgIHdoaWxlIChub2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgJiYgbm9kZS50YWdOYW1lID09PSAnQScgJiYgbm9kZS5ocmVmKSB7XG4gICAgICAgICAgICBpZiAobm9kZS5nZXRBdHRyaWJ1dGUoJ2hyZWYnKS5zdGFydHNXaXRoKCcjJykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBQYXNzIHRocm91Z2gga25vd24gc2NoZW1lc1xuICAgICAgICAgICAgaWYgKHBhc3NUaHJvdWdoTGlua1NjaGVtZXMuc29tZShzY2hlbWUgPT4gbm9kZS5ocmVmLnN0YXJ0c1dpdGgoc2NoZW1lKSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBocmVmVGV4dCA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLWhyZWYnKSB8fCBub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpO1xuICAgICAgICAgICAgLy8gSWYgb3JpZ2luYWwgbGluayBkb2Vzbid0IGxvb2sgbGlrZSBhIHVybCwgZGVsZWdhdGUgYmFjayB0byBWUyBDb2RlIHRvIHJlc29sdmVcbiAgICAgICAgICAgIGlmICghL15bYS16XFwtXSs6L2kudGVzdChocmVmVGV4dCkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ29wZW5MaW5rJywgeyBocmVmOiBocmVmVGV4dCB9KTtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBub2RlID0gbm9kZS5wYXJlbnROb2RlO1xuICAgIH1cbn0sIHRydWUpO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcbiAgICBpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcbiAgICAgICAgc2Nyb2xsRGlzYWJsZWQgPSBmYWxzZTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KHdpbmRvdy5zY3JvbGxZKTtcbiAgICAgICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgncmV2ZWFsTGluZScsIHsgbGluZSB9KTtcbiAgICAgICAgICAgIHN0YXRlLmxpbmUgPSBsaW5lO1xuICAgICAgICAgICAgdnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbiAgICAgICAgfVxuICAgIH1cbn0sIDUwKSk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3Qgc2Nyb2xsX3N5bmNfMSA9IHJlcXVpcmUoXCIuL3Njcm9sbC1zeW5jXCIpO1xuY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG4gICAgb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmUpIHtcbiAgICAgICAgY29uc3QgeyBwcmV2aW91cyB9ID0gc2Nyb2xsX3N5bmNfMS5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcbiAgICB9XG4gICAgX3VwZGF0ZShiZWZvcmUpIHtcbiAgICAgICAgdGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcbiAgICAgICAgdGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcbiAgICAgICAgdGhpcy5fY3VycmVudCA9IGJlZm9yZTtcbiAgICB9XG4gICAgX3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuICAgIH1cbiAgICBfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuICAgIH1cbn1cbmV4cG9ydHMuQWN0aXZlTGluZU1hcmtlciA9IEFjdGl2ZUxpbmVNYXJrZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGYpIHtcbiAgICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBmKCk7XG4gICAgfVxufVxuZXhwb3J0cy5vbmNlRG9jdW1lbnRMb2FkZWQgPSBvbmNlRG9jdW1lbnRMb2FkZWQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3Qgc2V0dGluZ3NfMSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzXCIpO1xuZXhwb3J0cy5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUgPSAodnNjb2RlKSA9PiB7XG4gICAgcmV0dXJuIG5ldyBjbGFzcyB7XG4gICAgICAgIHBvc3RNZXNzYWdlKHR5cGUsIGJvZHkpIHtcbiAgICAgICAgICAgIHZzY29kZS5wb3N0TWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG4gICAgICAgICAgICAgICAgYm9keVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9O1xufTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xuXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxuZyA9IChmdW5jdGlvbigpIHtcblx0cmV0dXJuIHRoaXM7XG59KSgpO1xuXG50cnkge1xuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcblx0ZyA9IGcgfHwgbmV3IEZ1bmN0aW9uKFwicmV0dXJuIHRoaXNcIikoKTtcbn0gY2F0Y2ggKGUpIHtcblx0Ly8gVGhpcyB3b3JrcyBpZiB0aGUgd2luZG93IHJlZmVyZW5jZSBpcyBhdmFpbGFibGVcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XG59XG5cbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXG4vLyBlYXNpZXIgdG8gaGFuZGxlIHRoaXMgY2FzZS4gaWYoIWdsb2JhbCkgeyAuLi59XG5cbm1vZHVsZS5leHBvcnRzID0gZztcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var i=t[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(o,i,function(t){return e[t]}.bind(null,i));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=3)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let o=void 0;function i(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=i,t.getSettings=function(){if(o)return o;if(o=i("data-settings"))return o;throw new Error("Could not load settings")}},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0),i="code-line";function r(e){return t=0,n=o.getSettings().lineCount-1,i=e,Math.min(n,Math.max(t,i));var t,n,i}const c=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName(i)){const n=+t.getAttribute("data-line");isNaN(n)||("CODE"===t.tagName&&t.parentElement&&"PRE"===t.parentElement.tagName?e.push({element:t.parentElement,line:n}):e.push({element:t,line:n}))}}return e}})();function s(e){const t=Math.floor(e),n=c();let o=n[0]||null;for(const e of n){if(e.line===t)return{previous:e,next:void 0};if(e.line>t)return{previous:o,next:e};o=e}return{previous:o}}function a(e){const t=c(),n=e-window.scrollY;let o=-1,i=t.length-1;for(;o+1<i;){const e=Math.floor((o+i)/2),r=u(t[e]);r.top+r.height>=n?i=e:o=e}const r=t[i],s=u(r);if(i>=1&&s.top>n){return{previous:t[o],next:r}}return i>1&&i<t.length&&s.top+s.height>n?{previous:r,next:t[i+1]}:{previous:r}}function u({element:e}){const t=e.getBoundingClientRect(),n=e.querySelector(`.${i}`);if(n){const e=n.getBoundingClientRect(),o=Math.max(1,e.top-t.top);return{top:t.top,height:o}}return t}t.getElementsForSourceLine=s,t.getLineElementsAtPageOffset=a,t.scrollToRevealSourceLine=function(e){if(!o.getSettings().scrollPreviewWithEditor)return;if(e<=0)return void window.scroll(window.scrollX,0);const{previous:t,next:n}=s(e);if(!t)return;let i=0;const r=u(t),c=r.top;if(n&&n.line!==t.line){i=c+(e-t.line)/(n.line-t.line)*(n.element.getBoundingClientRect().top-c)}else{const t=e-Math.floor(e);i=c+r.height*t}window.scroll(window.scrollX,Math.max(1,window.scrollY+i))},t.getEditorLineNumberForPageOffset=function(e){const{previous:t,next:n}=a(e);if(t){const o=u(t),i=e-window.scrollY-o.top;if(n){const e=i/(u(n).top-o.top);return r(t.line+e*(n.line-t.line))}{const e=i/o.height;return r(t.line+e)}}return null},t.getLineElementForFragment=function(e){return c().find(t=>t.element.id===e)}},function(e,t,n){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const o=n(7),i=n(8),r=n(9),c=n(2),s=n(0),a=n(10);let u=!0;const l=new o.ActiveLineMarker,f=s.getSettings(),d=acquireVsCodeApi(),m=d.getState(),p={..."object"==typeof m?m:{},...s.getData("data-state")};d.setState(p);const g=r.createPosterForVsCode(d);window.cspAlerter.setPoster(g),window.styleLoadingMonitor.setPoster(g),window.onload=()=>{v()},i.onceDocumentLoaded(()=>{const t=p.scrollProgress;"number"!=typeof t||f.fragment?f.scrollPreviewWithEditor&&e(()=>{if(f.fragment){p.fragment=void 0,d.setState(p);const e=c.getLineElementForFragment(f.fragment);e&&(u=!0,c.scrollToRevealSourceLine(e.line))}else isNaN(f.line)||(u=!0,c.scrollToRevealSourceLine(f.line))}):e(()=>{u=!0,window.scrollTo(0,t*document.body.clientHeight)})});const h=(()=>{const e=a(e=>{u=!0,c.scrollToRevealSourceLine(e)},50);return t=>{isNaN(t)||(p.line=t,e(t))}})();let v=a(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n<t.length;n++){const o=t[n];o.classList.contains("loading")&&o.classList.remove("loading"),e.push({id:o.id,height:o.height,width:o.width})}g.postMessage("cacheImageSizes",e)}},50);window.addEventListener("resize",()=>{u=!0,w(),v()},!0),window.addEventListener("message",e=>{if(e.data.source===f.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":h(e.data.line)}},!1),document.addEventListener("dblclick",e=>{if(!f.doubleClickToSwitchToEditor)return;for(let t=e.target;t;t=t.parentNode)if("A"===t.tagName)return;const t=e.pageY,n=c.getEditorLineNumberForPageOffset(t);"number"!=typeof n||isNaN(n)||g.postMessage("didClick",{line:Math.floor(n)})});const y=["http:","https:","mailto:","vscode:","vscode-insiders:"];function w(){p.scrollProgress=window.scrollY/document.body.clientHeight,d.setState(p)}document.addEventListener("click",e=>{if(!e)return;let t=e.target;for(;t;){if(t.tagName&&"A"===t.tagName&&t.href){if(t.getAttribute("href").startsWith("#"))return;let n=t.getAttribute("data-href");if(!n){if(y.some(e=>t.href.startsWith(e)))return;n=t.getAttribute("href")}return/^[a-z\-]+:/i.test(n)?void 0:(g.postMessage("openLink",{href:n}),e.preventDefault(),void e.stopPropagation())}t=t.parentNode}},!0),window.addEventListener("scroll",a(()=>{if(w(),u)u=!1;else{const e=c.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||g.postMessage("revealLine",{line:e})}},50))}).call(this,n(4).setImmediate)},function(e,t,n){(function(e){var o=Function.prototype.apply;function i(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new i(o.call(setTimeout,window,arguments),clearTimeout)},t.setInterval=function(){return new i(o.call(setInterval,window,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},i.prototype.unref=i.prototype.ref=function(){},i.prototype.close=function(){this._clearFn.call(window,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},n(5),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(1))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var o,i,r,c,s,a=1,u={},l=!1,f=e.document,d=Object.getPrototypeOf&&Object.getPrototypeOf(e);d=d&&d.setTimeout?d:e,"[object process]"==={}.toString.call(e.process)?o=function(e){t.nextTick((function(){p(e)}))}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((r=new MessageChannel).port1.onmessage=function(e){p(e.data)},o=function(e){r.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(i=f.documentElement,o=function(e){var t=f.createElement("script");t.onreadystatechange=function(){p(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):o=function(e){setTimeout(p,0,e)}:(c="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(c)&&p(+t.data.slice(c.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),o=function(t){e.postMessage(c+t,"*")}),d.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n<t.length;n++)t[n]=arguments[n+1];var i={callback:e,args:t};return u[a]=i,o(a),a++},d.clearImmediate=m}function m(e){delete u[e]}function p(e){if(l)setTimeout(p,0,e);else{var t=u[e];if(t){l=!0;try{!function(e){var t=e.callback,o=e.args;switch(o.length){case 0:t();break;case 1:t(o[0]);break;case 2:t(o[0],o[1]);break;case 3:t(o[0],o[1],o[2]);break;default:t.apply(n,o)}}(t)}finally{m(e),l=!1}}}}}("undefined"==typeof self?void 0===e?this:e:self)}).call(this,n(1),n(6))},function(e,t){var n,o,i=e.exports={};function r(){throw new Error("setTimeout has not been defined")}function c(){throw new Error("clearTimeout has not been defined")}function s(e){if(n===setTimeout)return setTimeout(e,0);if((n===r||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:r}catch(e){n=r}try{o="function"==typeof clearTimeout?clearTimeout:c}catch(e){o=c}}();var a,u=[],l=!1,f=-1;function d(){l&&a&&(l=!1,a.length?u=a.concat(u):f=-1,u.length&&m())}function m(){if(!l){var e=s(d);l=!0;for(var t=u.length;t;){for(a=u,u=[];++f<t;)a&&a[f].run();f=-1,t=u.length}a=null,l=!1,function(e){if(o===clearTimeout)return clearTimeout(e);if((o===c||!o)&&clearTimeout)return o=clearTimeout,clearTimeout(e);try{o(e)}catch(t){try{return o.call(null,e)}catch(t){return o.call(this,e)}}}(e)}}function p(e,t){this.fun=e,this.array=t}function g(){}i.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];u.push(new p(e,t)),1!==u.length||l||s(m)},p.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=g,i.addListener=g,i.once=g,i.off=g,i.removeListener=g,i.removeAllListeners=g,i.emit=g,i.prependListener=g,i.prependOnceListener=g,i.listeners=function(e){return[]},i.binding=function(e){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(e){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(2);t.ActiveLineMarker=class{onDidChangeTextEditorSelection(e){const{previous:t}=o.getElementsForSourceLine(e);this._update(t&&t.element)}_update(e){this._unmarkActiveElement(this._current),this._markActiveElement(e),this._current=e}_unmarkActiveElement(e){e&&(e.className=e.className.replace(/\bcode-active-line\b/g,""))}_markActiveElement(e){e&&(e.className+=" code-active-line")}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);t.createPosterForVsCode=e=>new class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}}},function(e,t,n){(function(t){var n="Expected a function",o=NaN,i="[object Symbol]",r=/^\s+|\s+$/g,c=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,a=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,f="object"==typeof self&&self&&self.Object===Object&&self,d=l||f||Function("return this")(),m=Object.prototype.toString,p=Math.max,g=Math.min,h=function(){return d.Date.now()};function v(e,t,o){var i,r,c,s,a,u,l=0,f=!1,d=!1,m=!0;if("function"!=typeof e)throw new TypeError(n);function v(t){var n=i,o=r;return i=r=void 0,l=t,s=e.apply(o,n)}function b(e){var n=e-u;return void 0===u||n>=t||n<0||d&&e-l>=c}function T(){var e=h();if(b(e))return E(e);a=setTimeout(T,function(e){var n=t-(e-u);return d?g(n,c-(e-l)):n}(e))}function E(e){return a=void 0,m&&i?v(e):(i=r=void 0,s)}function _(){var e=h(),n=b(e);if(i=arguments,r=this,u=e,n){if(void 0===a)return function(e){return l=e,a=setTimeout(T,t),f?v(e):s}(u);if(d)return a=setTimeout(T,t),v(u)}return void 0===a&&(a=setTimeout(T,t)),s}return t=w(t)||0,y(o)&&(f=!!o.leading,c=(d="maxWait"in o)?p(w(o.maxWait)||0,t):c,m="trailing"in o?!!o.trailing:m),_.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=u=r=a=void 0},_.flush=function(){return void 0===a?s:E(h())},_}function y(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function w(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&m.call(e)==i}(e))return o;if(y(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=y(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(r,"");var n=s.test(e);return n||a.test(e)?u(e.slice(2),n?2:8):c.test(e)?o:+e}e.exports=function(e,t,o){var i=!0,r=!0;if("function"!=typeof e)throw new TypeError(n);return y(o)&&(i="leading"in o?!!o.leading:i,r="trailing"in o?!!o.trailing:r),v(e,t,{leading:i,maxWait:t,trailing:r})}}).call(this,n(1))}]); \ No newline at end of file diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 5c06f2b082f..edaa6f925d1 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -4,13 +4,28 @@ *--------------------------------------------------------------------------------------------*/ html, body { - font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif); - font-size: var(--vscode-markdown-font-size, 14px); + font-family: var(--markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif); + font-size: var(--markdown-font-size, 14px); padding: 0 26px; - line-height: var(--vscode-markdown-line-height, 22px); + line-height: var(--markdown-line-height, 22px); word-wrap: break-word; } +body { + padding-top: 1em; +} + +/* Reset margin top for elements */ +h1, h2, h3, h4, h5, h6, +p, ol, ul, pre { + margin-top: 0; +} + +h2, h3, h4, h5, h6 { + font-weight: normal; + margin-bottom: 0.2em; +} + #code-csp-warning { position: fixed; top: 0; @@ -112,6 +127,15 @@ textarea:focus { outline-offset: -1px; } +p { + margin-bottom: 0.7em; +} + +ul, +ol { + margin-bottom: 0.7em; +} + hr { border: 0; height: 2px; @@ -123,9 +147,6 @@ h1 { line-height: 1.2; border-bottom-width: 1px; border-bottom-style: solid; -} - -h1, h2, h3 { font-weight: normal; } @@ -133,15 +154,13 @@ table { border-collapse: collapse; } -table > thead > tr > th { +th { text-align: left; border-bottom: 1px solid; } -table > thead > tr > th, -table > thead > tr > td, -table > tbody > tr > th, -table > tbody > tr > td { +th, +td { padding: 5px 10px; } @@ -157,7 +176,7 @@ blockquote { } code { - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-family: var(--vscode-editor-font-family, "SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace); font-size: 1em; line-height: 1.357em; } @@ -196,22 +215,22 @@ pre code { border-color: rgb(0, 0, 0); } -.vscode-light table > thead > tr > th { +.vscode-light th { border-color: rgba(0, 0, 0, 0.69); } -.vscode-dark table > thead > tr > th { +.vscode-dark th { border-color: rgba(255, 255, 255, 0.69); } .vscode-light h1, .vscode-light hr, -.vscode-light table > tbody > tr + tr > td { +.vscode-light td { border-color: rgba(0, 0, 0, 0.18); } .vscode-dark h1, .vscode-dark hr, -.vscode-dark table > tbody > tr + tr > td { +.vscode-dark td { border-color: rgba(255, 255, 255, 0.18); } diff --git a/extensions/markdown-language-features/media/pre.js b/extensions/markdown-language-features/media/pre.js index bddc3b86ac5..8268f1e2d5b 100644 --- a/extensions/markdown-language-features/media/pre.js +++ b/extensions/markdown-language-features/media/pre.js @@ -1,2 +1 @@ -!function(e){var t={};function n(s){if(t[s])return t[s].exports;var o=t[s]={i:s,l:!1,exports:{}};return e[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(s,o,function(t){return e[t]}.bind(null,o));return s},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=8)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let s=void 0;function o(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=o,t.getSettings=function(){if(s)return s;if(s=o("data-settings"))return s;throw new Error("Could not load settings")}},,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(9),o=n(11);window.cspAlerter=new s.CspAlerter,window.styleLoadingMonitor=new o.StyleLoadingMonitor},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(0),o=n(10);t.CspAlerter=class{constructor(){this.didShow=!1,this.didHaveCspWarning=!1,document.addEventListener("securitypolicyviolation",()=>{this.onCspWarning()}),window.addEventListener("message",e=>{e&&e.data&&"vscode-did-block-svg"===e.data.name&&this.onCspWarning()})}setPoster(e){this.messaging=e,this.didHaveCspWarning&&this.showCspWarning()}onCspWarning(){this.didHaveCspWarning=!0,this.showCspWarning()}showCspWarning(){const e=o.getStrings(),t=s.getSettings();if(this.didShow||t.disableSecurityWarnings||!this.messaging)return;this.didShow=!0;const n=document.createElement("a");n.innerText=e.cspAlertMessageText,n.setAttribute("id","code-csp-warning"),n.setAttribute("title",e.cspAlertMessageTitle),n.setAttribute("role","button"),n.setAttribute("aria-label",e.cspAlertMessageLabel),n.onclick=()=>{this.messaging.postMessage("showPreviewSecuritySelector",{source:t.source})},document.body.appendChild(n)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getStrings=function(){const e=document.getElementById("vscode-markdown-preview-data");if(e){const t=e.getAttribute("data-strings");if(t)return JSON.parse(t)}throw new Error("Could not load strings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.StyleLoadingMonitor=class{constructor(){this.unloadedStyles=[],this.finishedLoading=!1;const e=e=>{const t=e.target.dataset.source;this.unloadedStyles.push(t)};window.addEventListener("DOMContentLoaded",()=>{for(const t of document.getElementsByClassName("code-user-style"))t.dataset.source&&(t.onerror=e)}),window.addEventListener("load",()=>{this.unloadedStyles.length&&(this.finishedLoading=!0,this.poster&&this.poster.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles}))})}setPoster(e){this.poster=e,this.finishedLoading&&e.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles})}}}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2NzcC50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zdHJpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJlX18iLCJtb2R1bGVJZCIsImV4cG9ydHMiLCJtb2R1bGUiLCJpIiwibCIsIm1vZHVsZXMiLCJjYWxsIiwibSIsImMiLCJkIiwibmFtZSIsImdldHRlciIsIm8iLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImVudW1lcmFibGUiLCJnZXQiLCJyIiwiU3ltYm9sIiwidG9TdHJpbmdUYWciLCJ2YWx1ZSIsInQiLCJtb2RlIiwiX19lc01vZHVsZSIsIm5zIiwiY3JlYXRlIiwia2V5IiwiYmluZCIsIm4iLCJvYmplY3QiLCJwcm9wZXJ0eSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwicCIsInMiLCJjYWNoZWRTZXR0aW5ncyIsInVuZGVmaW5lZCIsImdldERhdGEiLCJlbGVtZW50IiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsImRhdGEiLCJnZXRBdHRyaWJ1dGUiLCJKU09OIiwicGFyc2UiLCJFcnJvciIsImdldFNldHRpbmdzIiwiY3NwXzEiLCJsb2FkaW5nXzEiLCJ3aW5kb3ciLCJjc3BBbGVydGVyIiwiQ3NwQWxlcnRlciIsInN0eWxlTG9hZGluZ01vbml0b3IiLCJTdHlsZUxvYWRpbmdNb25pdG9yIiwic2V0dGluZ3NfMSIsInN0cmluZ3NfMSIsInRoaXMiLCJkaWRTaG93IiwiZGlkSGF2ZUNzcFdhcm5pbmciLCJhZGRFdmVudExpc3RlbmVyIiwib25Dc3BXYXJuaW5nIiwiZXZlbnQiLCJwb3N0ZXIiLCJtZXNzYWdpbmciLCJzaG93Q3NwV2FybmluZyIsInN0cmluZ3MiLCJnZXRTdHJpbmdzIiwic2V0dGluZ3MiLCJkaXNhYmxlU2VjdXJpdHlXYXJuaW5ncyIsIm5vdGlmaWNhdGlvbiIsImNyZWF0ZUVsZW1lbnQiLCJpbm5lclRleHQiLCJjc3BBbGVydE1lc3NhZ2VUZXh0Iiwic2V0QXR0cmlidXRlIiwiY3NwQWxlcnRNZXNzYWdlVGl0bGUiLCJjc3BBbGVydE1lc3NhZ2VMYWJlbCIsIm9uY2xpY2siLCJwb3N0TWVzc2FnZSIsInNvdXJjZSIsImJvZHkiLCJhcHBlbmRDaGlsZCIsInN0b3JlIiwidW5sb2FkZWRTdHlsZXMiLCJmaW5pc2hlZExvYWRpbmciLCJvblN0eWxlTG9hZEVycm9yIiwidGFyZ2V0IiwiZGF0YXNldCIsInB1c2giLCJsaW5rIiwiZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSIsIm9uZXJyb3IiLCJsZW5ndGgiXSwibWFwcGluZ3MiOiJhQUNFLElBQUlBLEVBQW1CLEdBR3ZCLFNBQVNDLEVBQW9CQyxHQUc1QixHQUFHRixFQUFpQkUsR0FDbkIsT0FBT0YsRUFBaUJFLEdBQVVDLFFBR25DLElBQUlDLEVBQVNKLEVBQWlCRSxHQUFZLENBQ3pDRyxFQUFHSCxFQUNISSxHQUFHLEVBQ0hILFFBQVMsSUFVVixPQU5BSSxFQUFRTCxHQUFVTSxLQUFLSixFQUFPRCxRQUFTQyxFQUFRQSxFQUFPRCxRQUFTRixHQUcvREcsRUFBT0UsR0FBSSxFQUdKRixFQUFPRCxRQUtmRixFQUFvQlEsRUFBSUYsRUFHeEJOLEVBQW9CUyxFQUFJVixFQUd4QkMsRUFBb0JVLEVBQUksU0FBU1IsRUFBU1MsRUFBTUMsR0FDM0NaLEVBQW9CYSxFQUFFWCxFQUFTUyxJQUNsQ0csT0FBT0MsZUFBZWIsRUFBU1MsRUFBTSxDQUFFSyxZQUFZLEVBQU1DLElBQUtMLEtBS2hFWixFQUFvQmtCLEVBQUksU0FBU2hCLEdBQ1gsb0JBQVhpQixRQUEwQkEsT0FBT0MsYUFDMUNOLE9BQU9DLGVBQWViLEVBQVNpQixPQUFPQyxZQUFhLENBQUVDLE1BQU8sV0FFN0RQLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxLQVF2RHJCLEVBQW9Cc0IsRUFBSSxTQUFTRCxFQUFPRSxHQUV2QyxHQURVLEVBQVBBLElBQVVGLEVBQVFyQixFQUFvQnFCLElBQy9CLEVBQVBFLEVBQVUsT0FBT0YsRUFDcEIsR0FBVyxFQUFQRSxHQUE4QixpQkFBVkYsR0FBc0JBLEdBQVNBLEVBQU1HLFdBQVksT0FBT0gsRUFDaEYsSUFBSUksRUFBS1gsT0FBT1ksT0FBTyxNQUd2QixHQUZBMUIsRUFBb0JrQixFQUFFTyxHQUN0QlgsT0FBT0MsZUFBZVUsRUFBSSxVQUFXLENBQUVULFlBQVksRUFBTUssTUFBT0EsSUFDdEQsRUFBUEUsR0FBNEIsaUJBQVRGLEVBQW1CLElBQUksSUFBSU0sS0FBT04sRUFBT3JCLEVBQW9CVSxFQUFFZSxFQUFJRSxFQUFLLFNBQVNBLEdBQU8sT0FBT04sRUFBTU0sSUFBUUMsS0FBSyxLQUFNRCxJQUM5SSxPQUFPRixHQUlSekIsRUFBb0I2QixFQUFJLFNBQVMxQixHQUNoQyxJQUFJUyxFQUFTVCxHQUFVQSxFQUFPcUIsV0FDN0IsV0FBd0IsT0FBT3JCLEVBQWdCLFNBQy9DLFdBQThCLE9BQU9BLEdBRXRDLE9BREFILEVBQW9CVSxFQUFFRSxFQUFRLElBQUtBLEdBQzVCQSxHQUlSWixFQUFvQmEsRUFBSSxTQUFTaUIsRUFBUUMsR0FBWSxPQUFPakIsT0FBT2tCLFVBQVVDLGVBQWUxQixLQUFLdUIsRUFBUUMsSUFHekcvQixFQUFvQmtDLEVBQUksR0FJakJsQyxFQUFvQkEsRUFBb0JtQyxFQUFJLEcsK0JDN0VyRHJCLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQUN0RCxJQUFJZSxPQUFpQkMsRUFDckIsU0FBU0MsRUFBUVgsR0FDYixNQUFNWSxFQUFVQyxTQUFTQyxlQUFlLGdDQUN4QyxHQUFJRixFQUFTLENBQ1QsTUFBTUcsRUFBT0gsRUFBUUksYUFBYWhCLEdBQ2xDLEdBQUllLEVBQ0EsT0FBT0UsS0FBS0MsTUFBTUgsR0FHMUIsTUFBTSxJQUFJSSxNQUFNLDJCQUEyQm5CLEtBRS9DekIsRUFBUW9DLFFBQVVBLEVBV2xCcEMsRUFBUTZDLFlBVlIsV0FDSSxHQUFJWCxFQUNBLE9BQU9BLEVBR1gsR0FEQUEsRUFBaUJFLEVBQVEsaUJBRXJCLE9BQU9GLEVBRVgsTUFBTSxJQUFJVSxNQUFNLDZCLG9DQ3JCcEJoQyxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFDdEQsTUFBTTJCLEVBQVEsRUFBUSxHQUNoQkMsRUFBWSxFQUFRLElBQzFCQyxPQUFPQyxXQUFhLElBQUlILEVBQU1JLFdBQzlCRixPQUFPRyxvQkFBc0IsSUFBSUosRUFBVUsscUIsNkJDSjNDeEMsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBQ3RELE1BQU1rQyxFQUFhLEVBQVEsR0FDckJDLEVBQVksRUFBUSxJQThDMUJ0RCxFQUFRa0QsV0ExQ1IsTUFDSSxjQUNJSyxLQUFLQyxTQUFVLEVBQ2ZELEtBQUtFLG1CQUFvQixFQUN6Qm5CLFNBQVNvQixpQkFBaUIsMEJBQTJCLEtBQ2pESCxLQUFLSSxpQkFFVFgsT0FBT1UsaUJBQWlCLFVBQVlFLElBQzVCQSxHQUFTQSxFQUFNcEIsTUFBNEIseUJBQXBCb0IsRUFBTXBCLEtBQUsvQixNQUNsQzhDLEtBQUtJLGlCQUlqQixVQUFVRSxHQUNOTixLQUFLTyxVQUFZRCxFQUNiTixLQUFLRSxtQkFDTEYsS0FBS1EsaUJBR2IsZUFDSVIsS0FBS0UsbUJBQW9CLEVBQ3pCRixLQUFLUSxpQkFFVCxpQkFDSSxNQUFNQyxFQUFVVixFQUFVVyxhQUNwQkMsRUFBV2IsRUFBV1IsY0FDNUIsR0FBSVUsS0FBS0MsU0FBV1UsRUFBU0MsMEJBQTRCWixLQUFLTyxVQUMxRCxPQUVKUCxLQUFLQyxTQUFVLEVBQ2YsTUFBTVksRUFBZTlCLFNBQVMrQixjQUFjLEtBQzVDRCxFQUFhRSxVQUFZTixFQUFRTyxvQkFDakNILEVBQWFJLGFBQWEsS0FBTSxvQkFDaENKLEVBQWFJLGFBQWEsUUFBU1IsRUFBUVMsc0JBQzNDTCxFQUFhSSxhQUFhLE9BQVEsVUFDbENKLEVBQWFJLGFBQWEsYUFBY1IsRUFBUVUsc0JBQ2hETixFQUFhTyxRQUFVLEtBQ25CcEIsS0FBS08sVUFBVWMsWUFBWSw4QkFBK0IsQ0FBRUMsT0FBUVgsRUFBU1csVUFFakZ2QyxTQUFTd0MsS0FBS0MsWUFBWVgsTSw2QkM3Q2xDeEQsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBV3REbkIsRUFBUWlFLFdBVlIsV0FDSSxNQUFNZSxFQUFRMUMsU0FBU0MsZUFBZSxnQ0FDdEMsR0FBSXlDLEVBQU8sQ0FDUCxNQUFNeEMsRUFBT3dDLEVBQU12QyxhQUFhLGdCQUNoQyxHQUFJRCxFQUNBLE9BQU9FLEtBQUtDLE1BQU1ILEdBRzFCLE1BQU0sSUFBSUksTUFBTSw0Qiw2QkNicEJoQyxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFpQ3REbkIsRUFBUW9ELG9CQWhDUixNQUNJLGNBQ0lHLEtBQUswQixlQUFpQixHQUN0QjFCLEtBQUsyQixpQkFBa0IsRUFDdkIsTUFBTUMsRUFBb0J2QixJQUN0QixNQUFNaUIsRUFBU2pCLEVBQU13QixPQUFPQyxRQUFRUixPQUNwQ3RCLEtBQUswQixlQUFlSyxLQUFLVCxJQUU3QjdCLE9BQU9VLGlCQUFpQixtQkFBb0IsS0FDeEMsSUFBSyxNQUFNNkIsS0FBUWpELFNBQVNrRCx1QkFBdUIsbUJBQzNDRCxFQUFLRixRQUFRUixTQUNiVSxFQUFLRSxRQUFVTixLQUkzQm5DLE9BQU9VLGlCQUFpQixPQUFRLEtBQ3ZCSCxLQUFLMEIsZUFBZVMsU0FHekJuQyxLQUFLMkIsaUJBQWtCLEVBQ25CM0IsS0FBS00sUUFDTE4sS0FBS00sT0FBT2UsWUFBWSx3QkFBeUIsQ0FBRUssZUFBZ0IxQixLQUFLMEIsb0JBSXBGLFVBQVVwQixHQUNOTixLQUFLTSxPQUFTQSxFQUNWTixLQUFLMkIsaUJBQ0xyQixFQUFPZSxZQUFZLHdCQUF5QixDQUFFSyxlQUFnQjFCLEtBQUswQiIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gOCk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xubGV0IGNhY2hlZFNldHRpbmdzID0gdW5kZWZpbmVkO1xuZnVuY3Rpb24gZ2V0RGF0YShrZXkpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICBjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5leHBvcnRzLmdldERhdGEgPSBnZXREYXRhO1xuZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKSB7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgY2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuZXhwb3J0cy5nZXRTZXR0aW5ncyA9IGdldFNldHRpbmdzO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IGNzcF8xID0gcmVxdWlyZShcIi4vY3NwXCIpO1xuY29uc3QgbG9hZGluZ18xID0gcmVxdWlyZShcIi4vbG9hZGluZ1wiKTtcbndpbmRvdy5jc3BBbGVydGVyID0gbmV3IGNzcF8xLkNzcEFsZXJ0ZXIoKTtcbndpbmRvdy5zdHlsZUxvYWRpbmdNb25pdG9yID0gbmV3IGxvYWRpbmdfMS5TdHlsZUxvYWRpbmdNb25pdG9yKCk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3Qgc2V0dGluZ3NfMSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzXCIpO1xuY29uc3Qgc3RyaW5nc18xID0gcmVxdWlyZShcIi4vc3RyaW5nc1wiKTtcbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuY2xhc3MgQ3NwQWxlcnRlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZGlkU2hvdyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gZmFsc2U7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3NlY3VyaXR5cG9saWN5dmlvbGF0aW9uJywgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5vbkNzcFdhcm5pbmcoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoZXZlbnQgJiYgZXZlbnQuZGF0YSAmJiBldmVudC5kYXRhLm5hbWUgPT09ICd2c2NvZGUtZGlkLWJsb2NrLXN2ZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uQ3NwV2FybmluZygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0UG9zdGVyKHBvc3Rlcikge1xuICAgICAgICB0aGlzLm1lc3NhZ2luZyA9IHBvc3RlcjtcbiAgICAgICAgaWYgKHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcpIHtcbiAgICAgICAgICAgIHRoaXMuc2hvd0NzcFdhcm5pbmcoKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBvbkNzcFdhcm5pbmcoKSB7XG4gICAgICAgIHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG4gICAgfVxuICAgIHNob3dDc3BXYXJuaW5nKCkge1xuICAgICAgICBjb25zdCBzdHJpbmdzID0gc3RyaW5nc18xLmdldFN0cmluZ3MoKTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBzZXR0aW5nc18xLmdldFNldHRpbmdzKCk7XG4gICAgICAgIGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5kaWRTaG93ID0gdHJ1ZTtcbiAgICAgICAgY29uc3Qgbm90aWZpY2F0aW9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgICAgICBub3RpZmljYXRpb24uaW5uZXJUZXh0ID0gc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUZXh0O1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3RpdGxlJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUaXRsZSk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZUxhYmVsKTtcbiAgICAgICAgbm90aWZpY2F0aW9uLm9uY2xpY2sgPSAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnc2hvd1ByZXZpZXdTZWN1cml0eVNlbGVjdG9yJywgeyBzb3VyY2U6IHNldHRpbmdzLnNvdXJjZSB9KTtcbiAgICAgICAgfTtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuICAgIH1cbn1cbmV4cG9ydHMuQ3NwQWxlcnRlciA9IENzcEFsZXJ0ZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gZ2V0U3RyaW5ncygpIHtcbiAgICBjb25zdCBzdG9yZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKHN0b3JlKSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBzdG9yZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RyaW5ncycpO1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzdHJpbmdzJyk7XG59XG5leHBvcnRzLmdldFN0cmluZ3MgPSBnZXRTdHJpbmdzO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jbGFzcyBTdHlsZUxvYWRpbmdNb25pdG9yIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy51bmxvYWRlZFN0eWxlcyA9IFtdO1xuICAgICAgICB0aGlzLmZpbmlzaGVkTG9hZGluZyA9IGZhbHNlO1xuICAgICAgICBjb25zdCBvblN0eWxlTG9hZEVycm9yID0gKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBzb3VyY2UgPSBldmVudC50YXJnZXQuZGF0YXNldC5zb3VyY2U7XG4gICAgICAgICAgICB0aGlzLnVubG9hZGVkU3R5bGVzLnB1c2goc291cmNlKTtcbiAgICAgICAgfTtcbiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGxpbmsgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS11c2VyLXN0eWxlJykpIHtcbiAgICAgICAgICAgICAgICBpZiAobGluay5kYXRhc2V0LnNvdXJjZSkge1xuICAgICAgICAgICAgICAgICAgICBsaW5rLm9uZXJyb3IgPSBvblN0eWxlTG9hZEVycm9yO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgKCkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnVubG9hZGVkU3R5bGVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuZmluaXNoZWRMb2FkaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIGlmICh0aGlzLnBvc3Rlcikge1xuICAgICAgICAgICAgICAgIHRoaXMucG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0UG9zdGVyKHBvc3Rlcikge1xuICAgICAgICB0aGlzLnBvc3RlciA9IHBvc3RlcjtcbiAgICAgICAgaWYgKHRoaXMuZmluaXNoZWRMb2FkaW5nKSB7XG4gICAgICAgICAgICBwb3N0ZXIucG9zdE1lc3NhZ2UoJ3ByZXZpZXdTdHlsZUxvYWRFcnJvcicsIHsgdW5sb2FkZWRTdHlsZXM6IHRoaXMudW5sb2FkZWRTdHlsZXMgfSk7XG4gICAgICAgIH1cbiAgICB9XG59XG5leHBvcnRzLlN0eWxlTG9hZGluZ01vbml0b3IgPSBTdHlsZUxvYWRpbmdNb25pdG9yO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +!function(e){var t={};function n(s){if(t[s])return t[s].exports;var o=t[s]={i:s,l:!1,exports:{}};return e[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(s,o,function(t){return e[t]}.bind(null,o));return s},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let s=void 0;function o(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=o,t.getSettings=function(){if(s)return s;if(s=o("data-settings"))return s;throw new Error("Could not load settings")}},,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(12),o=n(14);window.cspAlerter=new s.CspAlerter,window.styleLoadingMonitor=new o.StyleLoadingMonitor},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(0),o=n(13);t.CspAlerter=class{constructor(){this.didShow=!1,this.didHaveCspWarning=!1,document.addEventListener("securitypolicyviolation",()=>{this.onCspWarning()}),window.addEventListener("message",e=>{e&&e.data&&"vscode-did-block-svg"===e.data.name&&this.onCspWarning()})}setPoster(e){this.messaging=e,this.didHaveCspWarning&&this.showCspWarning()}onCspWarning(){this.didHaveCspWarning=!0,this.showCspWarning()}showCspWarning(){const e=o.getStrings(),t=s.getSettings();if(this.didShow||t.disableSecurityWarnings||!this.messaging)return;this.didShow=!0;const n=document.createElement("a");n.innerText=e.cspAlertMessageText,n.setAttribute("id","code-csp-warning"),n.setAttribute("title",e.cspAlertMessageTitle),n.setAttribute("role","button"),n.setAttribute("aria-label",e.cspAlertMessageLabel),n.onclick=()=>{this.messaging.postMessage("showPreviewSecuritySelector",{source:t.source})},document.body.appendChild(n)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getStrings=function(){const e=document.getElementById("vscode-markdown-preview-data");if(e){const t=e.getAttribute("data-strings");if(t)return JSON.parse(t)}throw new Error("Could not load strings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.StyleLoadingMonitor=class{constructor(){this.unloadedStyles=[],this.finishedLoading=!1;const e=e=>{const t=e.target.dataset.source;this.unloadedStyles.push(t)};window.addEventListener("DOMContentLoaded",()=>{for(const t of document.getElementsByClassName("code-user-style"))t.dataset.source&&(t.onerror=e)}),window.addEventListener("load",()=>{this.unloadedStyles.length&&(this.finishedLoading=!0,this.poster&&this.poster.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles}))})}setPoster(e){this.poster=e,this.finishedLoading&&e.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles})}}}]); \ No newline at end of file diff --git a/extensions/markdown-language-features/media/preview-right-dark.svg b/extensions/markdown-language-features/media/preview-right-dark.svg deleted file mode 100644 index 1d59877196b..00000000000 --- a/extensions/markdown-language-features/media/preview-right-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M11.219 8.35484C11.6063 8.12309 12.049 8.00047 12.5003 8C12.8904 7.99939 13.2753 8.0901 13.6241 8.26486C13.9729 8.43963 14.276 8.6936 14.5091 9.00646C14.7421 9.31933 14.8987 9.6824 14.9664 10.0666C15.034 10.4509 15.0108 10.8456 14.8985 11.2192C14.7863 11.5929 14.5882 11.9351 14.32 12.2184C14.0518 12.5018 13.7211 12.7185 13.3542 12.8511C12.9873 12.9837 12.5944 13.0287 12.207 12.9823C11.8197 12.9359 11.4485 12.7995 11.1233 12.584L8.7683 14.9399L8.06055 14.2322L10.4163 11.877C10.1677 11.5003 10.0258 11.0634 10.0054 10.6126C9.98511 10.1618 10.0872 9.71384 10.3009 9.31634C10.5145 8.91885 10.8318 8.58659 11.219 8.35484ZM11.667 11.7472C11.9136 11.912 12.2036 12 12.5003 12C12.8981 12 13.2797 11.842 13.561 11.5607C13.8423 11.2794 14.0003 10.8978 14.0003 10.5C14.0003 10.2033 13.9123 9.91332 13.7475 9.66665C13.5827 9.41997 13.3484 9.22772 13.0743 9.11418C12.8002 9.00065 12.4986 8.97095 12.2077 9.02883C11.9167 9.0867 11.6494 9.22956 11.4396 9.43934C11.2299 9.64912 11.087 9.9164 11.0291 10.2074C10.9712 10.4983 11.001 10.7999 11.1145 11.074C11.228 11.3481 11.4203 11.5824 11.667 11.7472Z" fill="#C5C5C5"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M13 1L14 2V7.33573C13.6829 7.18584 13.3457 7.08481 13 7.03533V2L8 2L8 12.8787L6.87837 14H2L1 13V2L2 1H13ZM9.70794 14H9.70785L10 13.7077V13.7079L9.70794 14ZM13 10.5174C13.0002 10.5116 13.0003 10.5058 13.0003 10.5C13.0003 10.4942 13.0002 10.4884 13 10.4826V10.5174ZM2 2L7 2L7 13H2L2 2Z" fill="#C5C5C5"/> -</svg> diff --git a/extensions/markdown-language-features/media/preview-right-light.svg b/extensions/markdown-language-features/media/preview-right-light.svg deleted file mode 100644 index 3f1152fc3cd..00000000000 --- a/extensions/markdown-language-features/media/preview-right-light.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M11.219 8.35484C11.6063 8.12309 12.049 8.00047 12.5003 8C12.8904 7.99939 13.2753 8.0901 13.6241 8.26486C13.9729 8.43963 14.276 8.6936 14.5091 9.00646C14.7421 9.31933 14.8987 9.6824 14.9664 10.0666C15.034 10.4509 15.0108 10.8456 14.8985 11.2192C14.7863 11.5929 14.5882 11.9351 14.32 12.2184C14.0518 12.5018 13.7211 12.7185 13.3542 12.8511C12.9873 12.9837 12.5944 13.0287 12.207 12.9823C11.8197 12.9359 11.4485 12.7995 11.1233 12.584L8.7683 14.9399L8.06055 14.2322L10.4163 11.877C10.1677 11.5003 10.0258 11.0634 10.0054 10.6126C9.98511 10.1618 10.0872 9.71384 10.3009 9.31634C10.5145 8.91885 10.8318 8.58659 11.219 8.35484ZM11.667 11.7472C11.9136 11.912 12.2036 12 12.5003 12C12.8981 12 13.2797 11.842 13.561 11.5607C13.8423 11.2794 14.0003 10.8978 14.0003 10.5C14.0003 10.2033 13.9123 9.91332 13.7475 9.66665C13.5827 9.41997 13.3484 9.22772 13.0743 9.11418C12.8002 9.00065 12.4986 8.97095 12.2077 9.02883C11.9167 9.0867 11.6494 9.22956 11.4396 9.43934C11.2299 9.64912 11.087 9.9164 11.0291 10.2074C10.9712 10.4983 11.001 10.7999 11.1145 11.074C11.228 11.3481 11.4203 11.5824 11.667 11.7472Z" fill="#424242"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M13 1L14 2V7.33573C13.6829 7.18584 13.3457 7.08481 13 7.03533V2L8 2L8 12.8787L6.87837 14H2L1 13V2L2 1H13ZM9.70794 14H9.70785L10 13.7077V13.7079L9.70794 14ZM13 10.5174C13.0002 10.5116 13.0003 10.5058 13.0003 10.5C13.0003 10.4942 13.0002 10.4884 13 10.4826V10.5174ZM2 2L7 2L7 13H2L2 2Z" fill="#424242"/> -</svg> diff --git a/extensions/markdown-language-features/media/view-source-dark.svg b/extensions/markdown-language-features/media/view-source-dark.svg deleted file mode 100644 index ed302ae1398..00000000000 --- a/extensions/markdown-language-features/media/view-source-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M8.06065 3.85356L5.91421 6L5.2071 5.29289L6.49999 4H3.5C3.10218 4 2.72064 4.15804 2.43934 4.43934C2.15804 4.72065 2 5.10218 2 5.5C2 5.89783 2.15804 6.27936 2.43934 6.56066C2.72064 6.84197 3.10218 7 3.5 7H4V8H3.5C2.83696 8 2.20107 7.73661 1.73223 7.26777C1.26339 6.79893 1 6.16305 1 5.5C1 4.83696 1.26339 4.20108 1.73223 3.73224C2.20107 3.2634 2.83696 3 3.5 3H6.49999L6.49999 3H6.49996L6 2.50004V2.50001L5.2071 1.70711L5.91421 1L8.06065 3.14645L8.06065 3.85356ZM5 6.50003L5.91421 7.41424L6 7.32845V14H14V7H10V3H9.06065V2.73227L8.32838 2H11.2L11.5 2.1L14.9 5.6L15 6V14.5L14.5 15H5.5L5 14.5V9.00003V6.50003ZM11 3V6H13.9032L11 3Z" fill="#C5C5C5"/> -</svg> diff --git a/extensions/markdown-language-features/media/view-source-light.svg b/extensions/markdown-language-features/media/view-source-light.svg deleted file mode 100644 index 392a840c5ef..00000000000 --- a/extensions/markdown-language-features/media/view-source-light.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M8.06065 3.85356L5.91421 6L5.2071 5.29289L6.49999 4H3.5C3.10218 4 2.72064 4.15804 2.43934 4.43934C2.15804 4.72065 2 5.10218 2 5.5C2 5.89783 2.15804 6.27936 2.43934 6.56066C2.72064 6.84197 3.10218 7 3.5 7H4V8H3.5C2.83696 8 2.20107 7.73661 1.73223 7.26777C1.26339 6.79893 1 6.16305 1 5.5C1 4.83696 1.26339 4.20108 1.73223 3.73224C2.20107 3.2634 2.83696 3 3.5 3H6.49999L6.49999 3H6.49996L6 2.50004V2.50001L5.2071 1.70711L5.91421 1L8.06065 3.14645L8.06065 3.85356ZM5 6.50003L5.91421 7.41424L6 7.32845V14H14V7H10V3H9.06065V2.73227L8.32838 2H11.2L11.5 2.1L14.9 5.6L15 6V14.5L14.5 15H5.5L5 14.5V9.00003V6.50003ZM11 3V6H13.9032L11 3Z" fill="#424242"/> -</svg> diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 6fe2e99814f..34d6902fa4f 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -12,6 +12,7 @@ "vscode": "^1.20.0" }, "main": "./out/extension", + "browser": "./dist/browser/extension", "categories": [ "Programming Languages" ], @@ -26,7 +27,7 @@ "onCommand:markdown.showPreviewSecuritySelector", "onCommand:markdown.api.render", "onWebviewPanel:markdown.preview", - "onWebviewEditor:vscode.markdown.preview.editor" + "onCustomEditor:vscode.markdown.preview.editor" ], "contributes": { "commands": [ @@ -43,28 +44,19 @@ "command": "markdown.showPreviewToSide", "title": "%markdown.previewSide.title%", "category": "Markdown", - "icon": { - "light": "./media/preview-right-light.svg", - "dark": "./media/preview-right-dark.svg" - } + "icon": "$(open-preview)" }, { "command": "markdown.showLockedPreviewToSide", "title": "%markdown.showLockedPreviewToSide.title%", "category": "Markdown", - "icon": { - "light": "./media/preview-right-light.svg", - "dark": "./media/preview-right-dark.svg" - } + "icon": "$(open-preview)" }, { "command": "markdown.showSource", "title": "%markdown.showSource.title%", "category": "Markdown", - "icon": { - "light": "./media/view-source-light.svg", - "dark": "./media/view-source-dark.svg" - } + "icon": "$(go-to-file)" }, { "command": "markdown.showPreviewSecuritySelector", @@ -86,7 +78,7 @@ "editor/title": [ { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "alt": "markdown.showPreview", "group": "navigation" }, @@ -122,23 +114,23 @@ { "command": "markdown.showPreview", "when": "resourceLangId == markdown", - "group": "navigation" + "group": "1_open" } ], "commandPalette": [ { "command": "markdown.showPreview", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { "command": "markdown.showLockedPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { @@ -148,7 +140,7 @@ }, { "command": "markdown.showPreviewSecuritySelector", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewSecuritySelector", @@ -160,7 +152,7 @@ }, { "command": "markdown.preview.refresh", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.preview.refresh", @@ -173,13 +165,13 @@ "command": "markdown.showPreview", "key": "shift+ctrl+v", "mac": "shift+cmd+v", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewToSide", "key": "ctrl+k v", "mac": "cmd+k v", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" } ], "configuration": { @@ -210,7 +202,7 @@ }, "markdown.preview.fontFamily": { "type": "string", - "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif", + "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif", "description": "%markdown.preview.fontFamily.desc%", "scope": "resource" }, @@ -309,10 +301,10 @@ "markdown.previewScripts": [ "./media/index.js" ], - "webviewEditors": [ + "customEditors": [ { "viewType": "vscode.markdown.preview.editor", - "displayName": "(Experimental) VS Code Markdown Preview", + "displayName": "Markdown Preview (Experimental)", "priority": "option", "selector": [ { @@ -327,12 +319,14 @@ "watch": "npm run build-preview && gulp watch-extension:markdown-language-features", "vscode:prepublish": "npm run build-ext && npm run build-preview", "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json", - "build-preview": "webpack --mode production" + "build-preview": "webpack --mode production", + "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", + "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { "highlight.js": "9.15.10", "markdown-it": "^10.0.0", - "markdown-it-front-matter": "^0.1.2", + "markdown-it-front-matter": "^0.2.1", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index fbbab999519..6cf645a4103 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -1,7 +1,7 @@ { "displayName": "Markdown Language Features", "description": "Provides rich language support for Markdown.", - "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for every newline.", + "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for newlines inside paragraphs.", "markdown.preview.linkify": "Enable or disable conversion of URL-like text to links in the markdown preview.", "markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.", "markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 092148e19a4..064c30ac969 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -18,8 +18,14 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); -// Set VS Code state -let state = getData<{ line: number; fragment: string; }>('data-state'); +const originalState = vscode.getState(); + +const state = { + ...(typeof originalState === 'object' ? originalState : {}), + ...getData<any>('data-state') +}; + +// Make sure to sync VS Code state here vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -32,23 +38,35 @@ window.onload = () => { }; onceDocumentLoaded(() => { + const scrollProgress = state.scrollProgress; + + if (typeof scrollProgress === 'number' && !settings.fragment) { + setImmediate(() => { + scrollDisabled = true; + window.scrollTo(0, scrollProgress * document.body.clientHeight); + }); + return; + } + if (settings.scrollPreviewWithEditor) { - setTimeout(() => { + setImmediate(() => { // Try to scroll to fragment if available - if (state.fragment) { - const element = getLineElementForFragment(state.fragment); + if (settings.fragment) { + state.fragment = undefined; + vscode.setState(state); + + const element = getLineElementForFragment(settings.fragment); if (element) { scrollDisabled = true; scrollToRevealSourceLine(element.line); } } else { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { + if (!isNaN(settings.line!)) { scrollDisabled = true; - scrollToRevealSourceLine(initialLine); + scrollToRevealSourceLine(settings.line!); } } - }, 0); + }); } }); @@ -58,9 +76,10 @@ const onUpdateView = (() => { scrollToRevealSourceLine(line); }, 50); - return (line: number, settings: any) => { + return (line: number) => { if (!isNaN(line)) { - settings.line = line; + state.line = line; + doScroll(line); } }; @@ -91,6 +110,7 @@ let updateImageSizes = throttle(() => { window.addEventListener('resize', () => { scrollDisabled = true; + updateScrollProgress(); updateImageSizes(); }, true); @@ -105,7 +125,7 @@ window.addEventListener('message', event => { break; case 'updateView': - onUpdateView(event.data.line, settings); + onUpdateView(event.data.line); break; } }, false); @@ -143,13 +163,15 @@ document.addEventListener('click', event => { return; } - // Pass through known schemes - if (passThroughLinkSchemes.some(scheme => node.href.startsWith(scheme))) { - return; + let hrefText = node.getAttribute('data-href'); + if (!hrefText) { + // Pass through known schemes + if (passThroughLinkSchemes.some(scheme => node.href.startsWith(scheme))) { + return; + } + hrefText = node.getAttribute('href'); } - const hrefText = node.getAttribute('data-href') || node.getAttribute('href'); - // If original link doesn't look like a url, delegate back to VS Code to resolve if (!/^[a-z\-]+:/i.test(hrefText)) { messaging.postMessage('openLink', { href: hrefText }); @@ -165,15 +187,20 @@ document.addEventListener('click', event => { }, true); window.addEventListener('scroll', throttle(() => { + updateScrollProgress(); + if (scrollDisabled) { scrollDisabled = false; } else { const line = getEditorLineNumberForPageOffset(window.scrollY); if (typeof line === 'number' && !isNaN(line)) { messaging.postMessage('revealLine', { line }); - state.line = line; - vscode.setState(state); } } }, 50)); +function updateScrollProgress() { + state.scrollProgress = window.scrollY / document.body.clientHeight; + vscode.setState(state); +} + diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index c77081b2659..470246f94c1 100644 --- a/extensions/markdown-language-features/preview-src/settings.ts +++ b/extensions/markdown-language-features/preview-src/settings.ts @@ -5,7 +5,8 @@ export interface PreviewSettings { readonly source: string; - readonly line: number; + readonly line?: number; + readonly fragment?: string readonly lineCount: number; readonly scrollPreviewWithEditor?: boolean; readonly scrollEditorWithPreview: boolean; diff --git a/extensions/markdown-language-features/preview-src/tsconfig.json b/extensions/markdown-language-features/preview-src/tsconfig.json index 85159a000d9..e19cd4a675d 100644 --- a/extensions/markdown-language-features/preview-src/tsconfig.json +++ b/extensions/markdown-language-features/preview-src/tsconfig.json @@ -2,6 +2,11 @@ "extends": "../../shared.tsconfig.json", "compilerOptions": { "outDir": "./dist/", - "jsx": "react" + "jsx": "react", + "lib": [ + "es2018", + "DOM", + "DOM.Iterable" + ] } } diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index ef173b60854..c17dc17cb76 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -13,9 +13,9 @@ import { isMarkdownFile } from '../util/file'; export interface OpenDocumentLinkArgs { - readonly path: string; + readonly path: {}; readonly fragment: string; - readonly fromResource: any; + readonly fromResource: {}; } enum OpenMarkdownLinks { @@ -29,13 +29,22 @@ export class OpenDocumentLinkCommand implements Command { public static createCommandUri( fromResource: vscode.Uri, - path: string, + path: vscode.Uri, fragment: string, ): vscode.Uri { + const toJson = (uri: vscode.Uri) => { + return { + scheme: uri.scheme, + authority: uri.authority, + path: uri.path, + fragment: uri.fragment, + query: uri.query, + }; + }; return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify(<OpenDocumentLinkArgs>{ - path: encodeURIComponent(path), + path: toJson(path), fragment, - fromResource: encodeURIComponent(fromResource.toString(true)), + fromResource: toJson(fromResource), }))}`); } @@ -43,35 +52,43 @@ export class OpenDocumentLinkCommand implements Command { private readonly engine: MarkdownEngine ) { } - public execute(args: OpenDocumentLinkArgs) { - const fromResource = vscode.Uri.parse(decodeURIComponent(args.fromResource)); - const targetPath = decodeURIComponent(args.path); - const column = this.getViewColumn(fromResource); - return this.tryOpen(targetPath, args, column).catch(() => { - if (targetPath && extname(targetPath) === '') { - return this.tryOpen(targetPath + '.md', args, column); - } - const targetResource = vscode.Uri.file(targetPath); - return Promise.resolve(undefined) - .then(() => vscode.commands.executeCommand('vscode.open', targetResource, column)) - .then(() => undefined); - }); + public async execute(args: OpenDocumentLinkArgs) { + return OpenDocumentLinkCommand.execute(this.engine, args); } - private async tryOpen(path: string, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { - const resource = vscode.Uri.file(path); - if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { - if (!path || vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { - return this.tryRevealLine(vscode.window.activeTextEditor, args.fragment); + public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs) { + const fromResource = vscode.Uri.parse('').with(args.fromResource); + const targetResource = vscode.Uri.parse('').with(args.path); + const column = this.getViewColumn(fromResource); + try { + return await this.tryOpen(engine, targetResource, args, column); + } catch { + if (extname(targetResource.path) === '') { + return this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); } + await vscode.commands.executeCommand('vscode.open', targetResource, column); + return undefined; + } + } + + private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { + if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { + if (vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { + return this.tryRevealLine(engine, vscode.window.activeTextEditor, args.fragment); + } + } + + const stat = await vscode.workspace.fs.stat(resource); + if (stat.type === vscode.FileType.Directory) { + return vscode.commands.executeCommand('revealInExplorer', resource); } return vscode.workspace.openTextDocument(resource) .then(document => vscode.window.showTextDocument(document, column)) - .then(editor => this.tryRevealLine(editor, args.fragment)); + .then(editor => this.tryRevealLine(engine, editor, args.fragment)); } - private getViewColumn(resource: vscode.Uri): vscode.ViewColumn { + private static getViewColumn(resource: vscode.Uri): vscode.ViewColumn { const config = vscode.workspace.getConfiguration('markdown', resource); const openLinks = config.get<OpenMarkdownLinks>('links.openLocation', OpenMarkdownLinks.currentGroup); switch (openLinks) { @@ -83,18 +100,22 @@ export class OpenDocumentLinkCommand implements Command { } } - private async tryRevealLine(editor: vscode.TextEditor, fragment?: string) { + private static async tryRevealLine(engine: MarkdownEngine, editor: vscode.TextEditor, fragment?: string) { if (editor && fragment) { - const toc = new TableOfContentsProvider(this.engine, editor.document); + const toc = new TableOfContentsProvider(engine, editor.document); const entry = await toc.lookup(fragment); if (entry) { - return editor.revealRange(new vscode.Range(entry.line, 0, entry.line, 0), vscode.TextEditorRevealType.AtTop); + const lineStart = new vscode.Range(entry.line, 0, entry.line, 0); + editor.selection = new vscode.Selection(lineStart.start, lineStart.end); + return editor.revealRange(lineStart, vscode.TextEditorRevealType.AtTop); } const lineNumberFragment = fragment.match(/^L(\d+)$/i); if (lineNumberFragment) { const line = +lineNumberFragment[1] - 1; if (!isNaN(line)) { - return editor.revealRange(new vscode.Range(line, 0, line, 0), vscode.TextEditorRevealType.AtTop); + const lineStart = new vscode.Range(line, 0, line, 0); + editor.selection = new vscode.Selection(lineStart.start, lineStart.end); + return editor.revealRange(lineStart, vscode.TextEditorRevealType.AtTop); } } } diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 0bcc8473745..0bce318ae7e 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -9,15 +9,16 @@ import * as commands from './commands/index'; import LinkProvider from './features/documentLinkProvider'; import MDDocumentSymbolProvider from './features/documentSymbolProvider'; import MarkdownFoldingProvider from './features/foldingProvider'; +import MarkdownSmartSelect from './features/smartSelect'; import { MarkdownContentProvider } from './features/previewContentProvider'; import { MarkdownPreviewManager } from './features/previewManager'; import MarkdownWorkspaceSymbolProvider from './features/workspaceSymbolProvider'; import { Logger } from './logger'; import { MarkdownEngine } from './markdownEngine'; import { getMarkdownExtensionContributions } from './markdownExtensions'; -import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector, ContentSecurityPolicyArbiter } from './security'; -import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; +import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security'; import { githubSlugifier } from './slugify'; +import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; export function activate(context: vscode.ExtensionContext) { @@ -33,7 +34,7 @@ export function activate(context: vscode.ExtensionContext) { const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, contributions, logger); const symbolProvider = new MDDocumentSymbolProvider(engine); - const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions); + const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions, engine); context.subscriptions.push(previewManager); context.subscriptions.push(registerMarkdownLanguageFeatures(symbolProvider, engine)); @@ -60,6 +61,7 @@ function registerMarkdownLanguageFeatures( vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()), vscode.languages.registerFoldingRangeProvider(selector, new MarkdownFoldingProvider(engine)), + vscode.languages.registerSelectionRangeProvider(selector, new MarkdownSmartSelect(engine)), vscode.languages.registerWorkspaceSymbolProvider(new MarkdownWorkspaceSymbolProvider(symbolProvider)) ); } diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 441055e5e71..6d85e6135b0 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -14,8 +14,7 @@ const localize = nls.loadMessageBundle(); function parseLink( document: vscode.TextDocument, link: string, - base: string -): { uri: vscode.Uri, tooltip?: string } { +): { uri: vscode.Uri, tooltip?: string } | undefined { const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link); if (externalSchemeUri) { // Normalize VS Code links to target currently running version @@ -29,24 +28,43 @@ function parseLink( // Use a fake scheme to avoid parse warnings const tempUri = vscode.Uri.parse(`vscode-resource:${link}`); - let resourcePath = tempUri.path; - if (!tempUri.path && document.uri.scheme === 'file') { - resourcePath = document.uri.path; + let resourceUri: vscode.Uri | undefined; + if (!tempUri.path) { + resourceUri = document.uri; } else if (tempUri.path[0] === '/') { - const root = vscode.workspace.getWorkspaceFolder(document.uri); + const root = getWorkspaceFolder(document); if (root) { - resourcePath = path.join(root.uri.fsPath, tempUri.path); + resourceUri = vscode.Uri.joinPath(root, tempUri.path); } } else { - resourcePath = base ? path.join(base, tempUri.path) : tempUri.path; + if (document.uri.scheme === Schemes.untitled) { + const root = getWorkspaceFolder(document); + if (root) { + resourceUri = vscode.Uri.joinPath(root, tempUri.path); + } + } else { + const base = document.uri.with({ path: path.dirname(document.uri.fsPath) }); + resourceUri = vscode.Uri.joinPath(base, tempUri.path); + } } + if (!resourceUri) { + return undefined; + } + + resourceUri = resourceUri.with({ fragment: tempUri.fragment }); + return { - uri: OpenDocumentLinkCommand.createCommandUri(document.uri, resourcePath, tempUri.fragment), + uri: OpenDocumentLinkCommand.createCommandUri(document.uri, resourceUri, tempUri.fragment), tooltip: localize('documentLink.tooltip', 'Follow link') }; } +function getWorkspaceFolder(document: vscode.TextDocument) { + return vscode.workspace.getWorkspaceFolder(document.uri)?.uri + || vscode.workspace.workspaceFolders?.[0]?.uri; +} + function matchAll( pattern: RegExp, text: string @@ -62,7 +80,6 @@ function matchAll( function extractDocumentLink( document: vscode.TextDocument, - base: string, pre: number, link: string, matchIndex: number | undefined @@ -71,11 +88,14 @@ function extractDocumentLink( const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); try { - const { uri, tooltip } = parseLink(document, link, base); + const linkData = parseLink(document, link); + if (!linkData) { + return undefined; + } const documentLink = new vscode.DocumentLink( new vscode.Range(linkStart, linkEnd), - uri); - documentLink.tooltip = tooltip; + linkData.uri); + documentLink.tooltip = linkData.tooltip; return documentLink; } catch (e) { return undefined; @@ -85,33 +105,31 @@ function extractDocumentLink( export default class LinkProvider implements vscode.DocumentLinkProvider { private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]/g; - private readonly definitionPattern = /^([\t ]*\[((?:\\\]|[^\]])+)\]:\s*)(\S+)/gm; + private readonly definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)(\S+)/gm; public provideDocumentLinks( document: vscode.TextDocument, _token: vscode.CancellationToken ): vscode.DocumentLink[] { - const base = document.uri.scheme === 'file' ? path.dirname(document.uri.fsPath) : ''; const text = document.getText(); return [ - ...this.providerInlineLinks(text, document, base), - ...this.provideReferenceLinks(text, document, base) + ...this.providerInlineLinks(text, document), + ...this.provideReferenceLinks(text, document) ]; } private providerInlineLinks( text: string, document: vscode.TextDocument, - base: string ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const matchImage = match[4] && extractDocumentLink(document, base, match[3].length + 1, match[4], match.index); + const matchImage = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); if (matchImage) { results.push(matchImage); } - const matchLink = extractDocumentLink(document, base, match[1].length, match[5], match.index); + const matchLink = extractDocumentLink(document, match[1].length, match[5], match.index); if (matchLink) { results.push(matchLink); } @@ -122,7 +140,6 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { private provideReferenceLinks( text: string, document: vscode.TextDocument, - base: string ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; @@ -159,8 +176,10 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { for (const definition of definitions.values()) { try { - const { uri } = parseLink(document, definition.link, base); - results.push(new vscode.DocumentLink(definition.linkRange, uri)); + const linkData = parseLink(document, definition.link); + if (linkData) { + results.push(new vscode.DocumentLink(definition.linkRange, linkData.uri)); + } } catch (e) { // noop } diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 4850b3759de..553c1a3257b 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -11,12 +11,6 @@ import { flatten } from '../util/arrays'; const rangeLimit = 5000; -const isStartRegion = (t: string) => /^\s*<!--\s*#?region\b.*-->/.test(t); -const isEndRegion = (t: string) => /^\s*<!--\s*#?endregion\b.*-->/.test(t); - -const isRegionMarker = (token: Token) => - token.type === 'html_block' && (isStartRegion(token.content) || isEndRegion(token.content)); - export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvider { constructor( @@ -69,21 +63,6 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi } private async getBlockFoldingRanges(document: vscode.TextDocument): Promise<vscode.FoldingRange[]> { - - const isFoldableToken = (token: Token): boolean => { - switch (token.type) { - case 'fence': - case 'list_item_open': - return token.map[1] > token.map[0]; - - case 'html_block': - return token.map[1] > token.map[0] + 1; - - default: - return false; - } - }; - const tokens = await this.engine.parse(document); const multiLineListItems = tokens.filter(isFoldableToken); return multiLineListItems.map(listItem => { @@ -92,7 +71,36 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) { end = end - 1; } - return new vscode.FoldingRange(start, end); + return new vscode.FoldingRange(start, end, this.getFoldingRangeKind(listItem)); }); } + + private getFoldingRangeKind(listItem: Token): vscode.FoldingRangeKind | undefined { + return listItem.type === 'html_block' && listItem.content.startsWith('<!--') + ? vscode.FoldingRangeKind.Comment + : undefined; + } } + +const isStartRegion = (t: string) => /^\s*<!--\s*#?region\b.*-->/.test(t); +const isEndRegion = (t: string) => /^\s*<!--\s*#?endregion\b.*-->/.test(t); + +const isRegionMarker = (token: Token) => + token.type === 'html_block' && (isStartRegion(token.content) || isEndRegion(token.content)); + +const isFoldableToken = (token: Token): boolean => { + switch (token.type) { + case 'fence': + case 'list_item_open': + return token.map[1] > token.map[0]; + + case 'html_block': + if (isRegionMarker(token)) { + return false; + } + return token.map[1] > token.map[0] + 1; + + default: + return false; + } +}; diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index ad8edc1c2ad..f2fb800347c 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; import * as path from 'path'; - -import { Logger } from '../logger'; -import { MarkdownContentProvider } from './previewContentProvider'; -import { Disposable } from '../util/dispose'; - +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import { OpenDocumentLinkCommand, resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; +import { Logger } from '../logger'; +import { MarkdownContributionProvider } from '../markdownExtensions'; +import { Disposable } from '../util/dispose'; +import { isMarkdownFile } from '../util/file'; +import { normalizeResource, WebviewResourceProvider } from '../util/resources'; import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; -import { isMarkdownFile } from '../util/file'; -import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; -import { WebviewResourceProvider, normalizeResource } from '../util/resources'; +import { MarkdownContentProvider } from './previewContentProvider'; +import { MarkdownEngine } from '../markdownEngine'; + const localize = nls.loadMessageBundle(); interface WebviewMessage { @@ -61,10 +61,14 @@ interface PreviewStyleLoadErrorMessage extends WebviewMessage { } export class PreviewDocumentVersion { - public constructor( - public readonly resource: vscode.Uri, - public readonly version: number, - ) { } + + private readonly resource: vscode.Uri; + private readonly version: number; + + public constructor(document: vscode.TextDocument) { + this.resource = document.uri; + this.version = document.version; + } public equals(other: PreviewDocumentVersion): boolean { return this.resource.fsPath === other.resource.fsPath @@ -72,102 +76,87 @@ export class PreviewDocumentVersion { } } -interface DynamicPreviewInput { - readonly resource: vscode.Uri; - readonly resourceColumn: vscode.ViewColumn; - readonly locked: boolean; - readonly line?: number; +interface MarkdownPreviewDelegate { + getTitle?(resource: vscode.Uri): string; + getAdditionalState(): {}, + openPreviewLinkToMarkdownFile(markdownLink: vscode.Uri, fragment: string): void; } -export class DynamicMarkdownPreview extends Disposable { +class StartingScrollLine { + public readonly type = 'line'; - public static readonly viewType = 'markdown.preview'; + constructor( + public readonly line: number, + ) { } +} + +class StartingScrollFragment { + public readonly type = 'fragment'; + + constructor( + public readonly fragment: string, + ) { } +} + +type StartingScrollLocation = StartingScrollLine | StartingScrollFragment; + +class MarkdownPreview extends Disposable implements WebviewResourceProvider { private readonly delay = 300; - private _resource: vscode.Uri; - private readonly _resourceColumn: vscode.ViewColumn; + private readonly _resource: vscode.Uri; + private readonly _webviewPanel: vscode.WebviewPanel; - private _locked: boolean; - - private readonly editor: vscode.WebviewPanel; private throttleTimer: any; - private line: number | undefined = undefined; + + private line: number | undefined; + private scrollToFragment: string | undefined; + private firstUpdate = true; private currentVersion?: PreviewDocumentVersion; private isScrolling = false; private _disposed: boolean = false; - private imageInfo: { id: string, width: number, height: number; }[] = []; - private scrollToFragment: string | undefined; + private imageInfo: { readonly id: string, readonly width: number, readonly height: number; }[] = []; - public static revive( - input: DynamicPreviewInput, + constructor( webview: vscode.WebviewPanel, - contentProvider: MarkdownContentProvider, - previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, - topmostLineMonitor: TopmostLineMonitor, - contributionProvider: MarkdownContributionProvider, - ): DynamicMarkdownPreview { - webview.webview.options = DynamicMarkdownPreview.getWebviewOptions(input.resource, contributionProvider.contributions); - webview.title = DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked); - - return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); - } - - public static create( - input: DynamicPreviewInput, - previewColumn: vscode.ViewColumn, - contentProvider: MarkdownContentProvider, - previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, - topmostLineMonitor: TopmostLineMonitor, - contributionProvider: MarkdownContributionProvider - ): DynamicMarkdownPreview { - const webview = vscode.window.createWebviewPanel( - DynamicMarkdownPreview.viewType, - DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked), - previewColumn, { - enableFindWidget: true, - ...DynamicMarkdownPreview.getWebviewOptions(input.resource, contributionProvider.contributions) - }); - - return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); - } - - private constructor( - webview: vscode.WebviewPanel, - input: DynamicPreviewInput, + resource: vscode.Uri, + startingScroll: StartingScrollLocation | undefined, + private readonly delegate: MarkdownPreviewDelegate, + private readonly engine: MarkdownEngine, private readonly _contentProvider: MarkdownContentProvider, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, - topmostLineMonitor: TopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, ) { super(); - this._resource = input.resource; - this._resourceColumn = input.resourceColumn; - this._locked = input.locked; - this.editor = webview; - if (!isNaN(input.line!)) { - this.line = input.line; + + this._webviewPanel = webview; + this._resource = resource; + + switch (startingScroll?.type) { + case 'line': + if (!isNaN(startingScroll.line!)) { + this.line = startingScroll.line; + } + break; + + case 'fragment': + this.scrollToFragment = startingScroll.fragment; + break; } - this._register(this.editor.onDidDispose(() => { - this.dispose(); - })); - - this._register(this.editor.onDidChangeViewState(e => { - this._onDidChangeViewStateEmitter.fire(e); - })); - this._register(_contributionProvider.onContributionsChanged(() => { setImmediate(() => this.refresh()); })); - this._register(this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { + this._register(vscode.workspace.onDidChangeTextDocument(event => { + if (this.isPreviewOf(event.document.uri)) { + this.refresh(); + } + })); + + this._register(this._webviewPanel.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { return; } @@ -194,178 +183,70 @@ export class DynamicMarkdownPreview extends Disposable { break; case 'previewStyleLoadError': - vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", e.body.unloadedStyles.join(', '))); + vscode.window.showWarningMessage( + localize('onPreviewStyleLoadError', + "Could not load 'markdown.styles': {0}", + e.body.unloadedStyles.join(', '))); break; } })); - this._register(vscode.workspace.onDidChangeTextDocument(event => { - if (this.isPreviewOf(event.document.uri)) { - this.refresh(); - } - })); - - this._register(topmostLineMonitor.onDidChanged(event => { - if (this.isPreviewOf(event.resource)) { - this.updateForView(event.resource, event.line); - } - })); - - this._register(vscode.window.onDidChangeTextEditorSelection(event => { - if (this.isPreviewOf(event.textEditor.document.uri)) { - this.postMessage({ - type: 'onDidChangeTextEditorSelection', - line: event.selections[0].active.line, - source: this.resource.toString() - }); - } - })); - - this._register(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor && isMarkdownFile(editor.document) && !this._locked) { - this.update(editor.document.uri, false); - } - })); - - this.doUpdate(); + this.updatePreview(); } - private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter<void>()); - public readonly onDispose = this._onDisposeEmitter.event; - - private readonly _onDidChangeViewStateEmitter = this._register(new vscode.EventEmitter<vscode.WebviewPanelOnDidChangeViewStateEvent>()); - public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; + dispose() { + super.dispose(); + this._disposed = true; + clearTimeout(this.throttleTimer); + } public get resource(): vscode.Uri { return this._resource; } - public get resourceColumn(): vscode.ViewColumn { - return this._resourceColumn; - } - public get state() { return { - resource: this.resource.toString(), - locked: this._locked, + resource: this._resource.toString(), line: this.line, - resourceColumn: this.resourceColumn, imageInfo: this.imageInfo, - fragment: this.scrollToFragment + fragment: this.scrollToFragment, + ...this.delegate.getAdditionalState(), }; } - public dispose() { - if (this._disposed) { - return; - } - - this._disposed = true; - this._onDisposeEmitter.fire(); - this._onDisposeEmitter.dispose(); - - this.editor.dispose(); - super.dispose(); - } - - public update(resource: vscode.Uri, isRefresh = true) { - // Reposition scroll preview, position scroll to the top if active text editor - // doesn't corresponds with preview - const editor = vscode.window.activeTextEditor; - if (editor) { - if (!isRefresh || this._previewConfigurations.loadAndCacheConfiguration(this._resource).scrollEditorWithPreview) { - if (editor.document.uri.fsPath === resource.fsPath) { - this.line = getVisibleLine(editor); - } else { - this.line = 0; - } - } - } - - // If we have changed resources, cancel any pending updates - const isResourceChange = resource.fsPath !== this._resource.fsPath; - if (isResourceChange) { - clearTimeout(this.throttleTimer); - this.throttleTimer = undefined; - } - - this._resource = resource; - + public refresh() { // Schedule update if none is pending if (!this.throttleTimer) { - if (isResourceChange || this.firstUpdate) { - this.doUpdate(isRefresh); + if (this.firstUpdate) { + this.updatePreview(true); } else { - this.throttleTimer = setTimeout(() => this.doUpdate(isRefresh), this.delay); + this.throttleTimer = setTimeout(() => this.updatePreview(true), this.delay); } } this.firstUpdate = false; } - public refresh() { - this.update(this._resource, true); - } - - public updateConfiguration() { - if (this._previewConfigurations.hasConfigurationChanged(this._resource)) { - this.refresh(); - } - } - - public get position(): vscode.ViewColumn | undefined { - return this.editor.viewColumn; - } - - public matchesResource( - otherResource: vscode.Uri, - otherPosition: vscode.ViewColumn | undefined, - otherLocked: boolean - ): boolean { - if (this.position !== otherPosition) { - return false; - } - - if (this._locked) { - return otherLocked && this.isPreviewOf(otherResource); - } else { - return !otherLocked; - } - } - - public matches(otherPreview: DynamicMarkdownPreview): boolean { - return this.matchesResource(otherPreview._resource, otherPreview.position, otherPreview._locked); - } - - public reveal(viewColumn: vscode.ViewColumn) { - this.editor.reveal(viewColumn); - } - - public toggleLock() { - this._locked = !this._locked; - this.editor.title = DynamicMarkdownPreview.getPreviewTitle(this._resource, this._locked); - } - private get iconPath() { - const root = path.join(this._contributionProvider.extensionPath, 'media'); + const root = vscode.Uri.joinPath(this._contributionProvider.extensionUri, 'media'); return { - light: vscode.Uri.file(path.join(root, 'preview-light.svg')), - dark: vscode.Uri.file(path.join(root, 'preview-dark.svg')) + light: vscode.Uri.joinPath(root, 'preview-light.svg'), + dark: vscode.Uri.joinPath(root, 'preview-dark.svg'), }; } - private isPreviewOf(resource: vscode.Uri): boolean { + public isPreviewOf(resource: vscode.Uri): boolean { return this._resource.fsPath === resource.fsPath; } - private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string { - return locked - ? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath)) - : localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)); + public postMessage(msg: any) { + if (!this._disposed) { + this._webviewPanel.webview.postMessage(msg); + } } - private updateForView(resource: vscode.Uri, topLine: number | undefined) { - if (!this.isPreviewOf(resource)) { + public scrollTo(topLine: number) { + if (this._disposed) { return; } @@ -374,36 +255,26 @@ export class DynamicMarkdownPreview extends Disposable { return; } - if (typeof topLine === 'number') { - this._logger.log('updateForView', { markdownFile: resource }); - this.line = topLine; - this.postMessage({ - type: 'updateView', - line: topLine, - source: resource.toString() - }); - } + this._logger.log('updateForView', { markdownFile: this._resource }); + this.line = topLine; + this.postMessage({ + type: 'updateView', + line: topLine, + source: this._resource.toString() + }); } - private postMessage(msg: any) { - if (!this._disposed) { - this.editor.webview.postMessage(msg); - } - } + private async updatePreview(forceUpdate?: boolean): Promise<void> { + clearTimeout(this.throttleTimer); + this.throttleTimer = undefined; - private async doUpdate(forceUpdate?: boolean): Promise<void> { if (this._disposed) { return; } - const markdownResource = this._resource; - - clearTimeout(this.throttleTimer); - this.throttleTimer = undefined; - let document: vscode.TextDocument; try { - document = await vscode.workspace.openTextDocument(markdownResource); + document = await vscode.workspace.openTextDocument(this._resource); } catch { await this.showFileNotFoundError(); return; @@ -413,58 +284,24 @@ export class DynamicMarkdownPreview extends Disposable { return; } - const pendingVersion = new PreviewDocumentVersion(markdownResource, document.version); + const pendingVersion = new PreviewDocumentVersion(document); if (!forceUpdate && this.currentVersion?.equals(pendingVersion)) { if (this.line) { - this.updateForView(markdownResource, this.line); + this.scrollTo(this.line); } return; } this.currentVersion = pendingVersion; - if (this._resource === markdownResource) { - const self = this; - const resourceProvider: WebviewResourceProvider = { - asWebviewUri: (resource) => { - return this.editor.webview.asWebviewUri(normalizeResource(markdownResource, resource)); - }, - get cspSource() { return self.editor.webview.cspSource; } - }; - const content = await this._contentProvider.provideTextDocumentContent(document, resourceProvider, this._previewConfigurations, this.line, this.state); - // Another call to `doUpdate` may have happened. - // Make sure we are still updating for the correct document - if (this.currentVersion && this.currentVersion.equals(pendingVersion)) { - this.setContent(content); - } + const content = await this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state); + + // Another call to `doUpdate` may have happened. + // Make sure we are still updating for the correct document + if (this.currentVersion?.equals(pendingVersion)) { + this.setContent(content); } } - private static getWebviewOptions( - resource: vscode.Uri, - contributions: MarkdownContributions - ): vscode.WebviewOptions { - return { - enableScripts: true, - localResourceRoots: DynamicMarkdownPreview.getLocalResourceRoots(resource, contributions) - }; - } - - private static getLocalResourceRoots( - base: vscode.Uri, - contributions: MarkdownContributions - ): ReadonlyArray<vscode.Uri> { - const baseRoots = Array.from(contributions.previewResourceRoots); - - const folder = vscode.workspace.getWorkspaceFolder(base); - if (folder) { - baseRoots.push(folder.uri); - } else if (!base.scheme || base.scheme === 'file') { - baseRoots.push(vscode.Uri.file(path.dirname(base.fsPath))); - } - - return baseRoots.map(root => normalizeResource(base, root)); - } - private onDidScrollPreview(line: number) { this.line = line; @@ -510,23 +347,55 @@ export class DynamicMarkdownPreview extends Disposable { } private async showFileNotFoundError() { - this.setContent(this._contentProvider.provideFileNotFoundContent(this._resource)); + this._webviewPanel.webview.html = this._contentProvider.provideFileNotFoundContent(this._resource); } private setContent(html: string): void { - this.editor.title = DynamicMarkdownPreview.getPreviewTitle(this._resource, this._locked); - this.editor.iconPath = this.iconPath; - this.editor.webview.options = DynamicMarkdownPreview.getWebviewOptions(this._resource, this._contributionProvider.contributions); - this.editor.webview.html = html; + if (this._disposed) { + return; + } + + if (this.delegate.getTitle) { + this._webviewPanel.title = this.delegate.getTitle(this._resource); + } + this._webviewPanel.iconPath = this.iconPath; + this._webviewPanel.webview.options = this.getWebviewOptions(); + + this._webviewPanel.webview.html = html; } + private getWebviewOptions(): vscode.WebviewOptions { + return { + enableScripts: true, + localResourceRoots: this.getLocalResourceRoots() + }; + } + + private getLocalResourceRoots(): ReadonlyArray<vscode.Uri> { + const baseRoots = Array.from(this._contributionProvider.contributions.previewResourceRoots); + + const folder = vscode.workspace.getWorkspaceFolder(this._resource); + if (folder) { + const workspaceRoots = vscode.workspace.workspaceFolders?.map(folder => folder.uri); + if (workspaceRoots) { + baseRoots.push(...workspaceRoots); + } + } else if (!this._resource.scheme || this._resource.scheme === 'file') { + baseRoots.push(vscode.Uri.file(path.dirname(this._resource.fsPath))); + } + + return baseRoots.map(root => normalizeResource(this._resource, root)); + } + + private async onDidClickPreviewLink(href: string) { let [hrefPath, fragment] = decodeURIComponent(href).split('#'); // We perviously already resolve absolute paths. // Now make sure we handle relative file paths if (hrefPath[0] !== '/') { - hrefPath = path.join(path.dirname(this.resource.path), hrefPath); + // Fix #93691, use this.resource.fsPath instead of this.resource.path + hrefPath = path.join(path.dirname(this.resource.fsPath), hrefPath); } const config = vscode.workspace.getConfiguration('markdown', this.resource); @@ -534,14 +403,343 @@ export class DynamicMarkdownPreview extends Disposable { if (openLinks === 'inPreview') { const markdownLink = await resolveLinkToMarkdownFile(hrefPath); if (markdownLink) { - if (fragment) { - this.scrollToFragment = fragment; - } - this.update(markdownLink); + this.delegate.openPreviewLinkToMarkdownFile(markdownLink, fragment); return; } } - vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource }); + OpenDocumentLinkCommand.execute(this.engine, { path: hrefPath, fragment, fromResource: this.resource.toJSON() }); + } + + //#region WebviewResourceProvider + + asWebviewUri(resource: vscode.Uri) { + return this._webviewPanel.webview.asWebviewUri(normalizeResource(this._resource, resource)); + } + + get cspSource() { + return this._webviewPanel.webview.cspSource; + } + + //#endregion +} + +export interface ManagedMarkdownPreview { + + readonly resource: vscode.Uri; + readonly resourceColumn: vscode.ViewColumn; + + readonly onDispose: vscode.Event<void>; + readonly onDidChangeViewState: vscode.Event<vscode.WebviewPanelOnDidChangeViewStateEvent>; + + dispose(): void; + + refresh(): void; + updateConfiguration(): void; + + matchesResource( + otherResource: vscode.Uri, + otherPosition: vscode.ViewColumn | undefined, + otherLocked: boolean + ): boolean; +} + +export class StaticMarkdownPreview extends Disposable implements ManagedMarkdownPreview { + + public static revive( + resource: vscode.Uri, + webview: vscode.WebviewPanel, + contentProvider: MarkdownContentProvider, + previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, + ): StaticMarkdownPreview { + return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider, engine); + } + + private readonly preview: MarkdownPreview; + + private constructor( + private readonly _webviewPanel: vscode.WebviewPanel, + resource: vscode.Uri, + contentProvider: MarkdownContentProvider, + private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, + ) { + super(); + + this.preview = this._register(new MarkdownPreview(this._webviewPanel, resource, undefined, { + getAdditionalState: () => { return {}; }, + openPreviewLinkToMarkdownFile: () => { /* todo */ } + }, engine, contentProvider, _previewConfigurations, logger, contributionProvider)); + + this._register(this._webviewPanel.onDidDispose(() => { + this.dispose(); + })); + + this._register(this._webviewPanel.onDidChangeViewState(e => { + this._onDidChangeViewState.fire(e); + })); + } + + private readonly _onDispose = this._register(new vscode.EventEmitter<void>()); + public readonly onDispose = this._onDispose.event; + + private readonly _onDidChangeViewState = this._register(new vscode.EventEmitter<vscode.WebviewPanelOnDidChangeViewStateEvent>()); + public readonly onDidChangeViewState = this._onDidChangeViewState.event; + + dispose() { + this._onDispose.fire(); + super.dispose(); + } + + public matchesResource( + _otherResource: vscode.Uri, + _otherPosition: vscode.ViewColumn | undefined, + _otherLocked: boolean + ): boolean { + return false; + } + + public refresh() { + this.preview.refresh(); + } + + public updateConfiguration() { + if (this._previewConfigurations.hasConfigurationChanged(this.preview.resource)) { + this.refresh(); + } + } + + public get resource() { + return this.preview.resource; + } + + public get resourceColumn() { + return this._webviewPanel.viewColumn || vscode.ViewColumn.One; } } + +interface DynamicPreviewInput { + readonly resource: vscode.Uri; + readonly resourceColumn: vscode.ViewColumn; + readonly locked: boolean; + readonly line?: number; +} + +/** + * A + */ +export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdownPreview { + + public static readonly viewType = 'markdown.preview'; + + private readonly _resourceColumn: vscode.ViewColumn; + private _locked: boolean; + + private readonly _webviewPanel: vscode.WebviewPanel; + private _preview: MarkdownPreview; + + public static revive( + input: DynamicPreviewInput, + webview: vscode.WebviewPanel, + contentProvider: MarkdownContentProvider, + previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + topmostLineMonitor: TopmostLineMonitor, + contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, + ): DynamicMarkdownPreview { + return new DynamicMarkdownPreview(webview, input, + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); + } + + public static create( + input: DynamicPreviewInput, + previewColumn: vscode.ViewColumn, + contentProvider: MarkdownContentProvider, + previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + topmostLineMonitor: TopmostLineMonitor, + contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, + ): DynamicMarkdownPreview { + const webview = vscode.window.createWebviewPanel( + DynamicMarkdownPreview.viewType, + DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked), + previewColumn, { enableFindWidget: true, }); + + return new DynamicMarkdownPreview(webview, input, + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); + } + + private constructor( + webview: vscode.WebviewPanel, + input: DynamicPreviewInput, + private readonly _contentProvider: MarkdownContentProvider, + private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, + private readonly _logger: Logger, + private readonly _topmostLineMonitor: TopmostLineMonitor, + private readonly _contributionProvider: MarkdownContributionProvider, + private readonly _engine: MarkdownEngine, + ) { + super(); + + this._webviewPanel = webview; + + this._resourceColumn = input.resourceColumn; + this._locked = input.locked; + + this._preview = this.createPreview(input.resource, typeof input.line === 'number' ? new StartingScrollLine(input.line) : undefined); + + this._register(webview.onDidDispose(() => { this.dispose(); })); + + this._register(this._webviewPanel.onDidChangeViewState(e => { + this._onDidChangeViewStateEmitter.fire(e); + })); + + this._register(this._topmostLineMonitor.onDidChanged(event => { + if (this._preview.isPreviewOf(event.resource)) { + this._preview.scrollTo(event.line); + } + })); + + this._register(vscode.window.onDidChangeTextEditorSelection(event => { + if (this._preview.isPreviewOf(event.textEditor.document.uri)) { + this._preview.postMessage({ + type: 'onDidChangeTextEditorSelection', + line: event.selections[0].active.line, + source: this._preview.resource.toString() + }); + } + })); + + this._register(vscode.window.onDidChangeActiveTextEditor(editor => { + // Only allow previewing normal text editors which have a viewColumn: See #101514 + if (typeof editor?.viewColumn === 'undefined') { + return; + } + + if (isMarkdownFile(editor.document) && !this._locked && !this._preview.isPreviewOf(editor.document.uri)) { + const line = getVisibleLine(editor); + this.update(editor.document.uri, line ? new StartingScrollLine(line) : undefined); + } + })); + } + + private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter<void>()); + public readonly onDispose = this._onDisposeEmitter.event; + + private readonly _onDidChangeViewStateEmitter = this._register(new vscode.EventEmitter<vscode.WebviewPanelOnDidChangeViewStateEvent>()); + public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; + + dispose() { + this._preview.dispose(); + this._webviewPanel.dispose(); + + this._onDisposeEmitter.fire(); + this._onDisposeEmitter.dispose(); + super.dispose(); + } + + public get resource() { + return this._preview.resource; + } + + public get resourceColumn() { + return this._resourceColumn; + } + + public reveal(viewColumn: vscode.ViewColumn) { + this._webviewPanel.reveal(viewColumn); + } + + public refresh() { + this._preview.refresh(); + } + + public updateConfiguration() { + if (this._previewConfigurations.hasConfigurationChanged(this._preview.resource)) { + this.refresh(); + } + } + + public update(newResource: vscode.Uri, scrollLocation?: StartingScrollLocation) { + if (this._preview.isPreviewOf(newResource)) { + switch (scrollLocation?.type) { + case 'line': + this._preview.scrollTo(scrollLocation.line); + return; + + case 'fragment': + // Workaround. For fragments, just reload the entire preview + break; + + default: + return; + } + } + + this._preview.dispose(); + this._preview = this.createPreview(newResource, scrollLocation); + } + + public toggleLock() { + this._locked = !this._locked; + this._webviewPanel.title = DynamicMarkdownPreview.getPreviewTitle(this._preview.resource, this._locked); + } + + private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string { + return locked + ? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath)) + : localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)); + } + + public get position(): vscode.ViewColumn | undefined { + return this._webviewPanel.viewColumn; + } + + public matchesResource( + otherResource: vscode.Uri, + otherPosition: vscode.ViewColumn | undefined, + otherLocked: boolean + ): boolean { + if (this.position !== otherPosition) { + return false; + } + + if (this._locked) { + return otherLocked && this._preview.isPreviewOf(otherResource); + } else { + return !otherLocked; + } + } + + public matches(otherPreview: DynamicMarkdownPreview): boolean { + return this.matchesResource(otherPreview._preview.resource, otherPreview.position, otherPreview._locked); + } + + private createPreview(resource: vscode.Uri, startingScroll?: StartingScrollLocation): MarkdownPreview { + return new MarkdownPreview(this._webviewPanel, resource, startingScroll, { + getTitle: (resource) => DynamicMarkdownPreview.getPreviewTitle(resource, this._locked), + getAdditionalState: () => { + return { + resourceColumn: this.resourceColumn, + locked: this._locked, + }; + }, + openPreviewLinkToMarkdownFile: (link: vscode.Uri, fragment?: string) => { + this.update(link, fragment ? new StartingScrollFragment(fragment) : undefined); + } + }, + this._engine, + this._contentProvider, + this._previewConfigurations, + this._logger, + this._contributionProvider); + } +} + diff --git a/extensions/markdown-language-features/src/features/previewConfig.ts b/extensions/markdown-language-features/src/features/previewConfig.ts index ed09a9ce46e..679f3c3ad77 100644 --- a/extensions/markdown-language-features/src/features/previewConfig.ts +++ b/extensions/markdown-language-features/src/features/previewConfig.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { equals } from '../util/arrays'; export class MarkdownPreviewConfiguration { public static getForResource(resource: vscode.Uri) { @@ -21,7 +22,7 @@ export class MarkdownPreviewConfiguration { public readonly lineHeight: number; public readonly fontSize: number; public readonly fontFamily: string | undefined; - public readonly styles: string[]; + public readonly styles: readonly string[]; private constructor(resource: vscode.Uri) { const editorConfig = vscode.workspace.getConfiguration('editor', resource); @@ -49,7 +50,7 @@ export class MarkdownPreviewConfiguration { } public isEqualTo(otherConfig: MarkdownPreviewConfiguration) { - for (let key in this) { + for (const key in this) { if (this.hasOwnProperty(key) && key !== 'styles') { if (this[key] !== otherConfig[key]) { return false; @@ -57,17 +58,7 @@ export class MarkdownPreviewConfiguration { } } - // Check styles - if (this.styles.length !== otherConfig.styles.length) { - return false; - } - for (let i = 0; i < this.styles.length; ++i) { - if (this.styles[i] !== otherConfig.styles[i]) { - return false; - } - } - - return true; + return equals(this.styles, otherConfig.styles); } [key: string]: any; diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 0be66d1d630..a8a64e5270d 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -111,7 +111,7 @@ export class MarkdownContentProvider { private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string { const webviewResource = resourceProvider.asWebviewUri( - vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))); + vscode.Uri.joinPath(this.context.extensionUri, 'media', mediaFile)); return webviewResource.toString(); } @@ -132,7 +132,7 @@ export class MarkdownContentProvider { // Use a workspace relative path if there is a workspace const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { - return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.joinPath(root.uri, href)).toString(); } // Otherwise look relative to the markdown file @@ -152,9 +152,9 @@ export class MarkdownContentProvider { private getSettingsOverrideStyles(config: MarkdownPreviewConfiguration): string { return [ - config.fontFamily ? `--vscode-markdown-font-family: ${config.fontFamily};` : '', - isNaN(config.fontSize) ? '' : `--vscode-markdown-font-size: ${config.fontSize}px;`, - isNaN(config.lineHeight) ? '' : `--vscode-markdown-line-height: ${config.lineHeight};`, + config.fontFamily ? `--markdown-font-family: ${config.fontFamily};` : '', + isNaN(config.fontSize) ? '' : `--markdown-font-size: ${config.fontSize}px;`, + isNaN(config.lineHeight) ? '' : `--markdown-line-height: ${config.lineHeight};`, ].join(' '); } diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 333a0651a44..3c8ddd77446 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -5,10 +5,11 @@ import * as vscode from 'vscode'; import { Logger } from '../logger'; +import { MarkdownEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; -import { disposeAll, Disposable } from '../util/dispose'; +import { Disposable, disposeAll } from '../util/dispose'; import { TopmostLineMonitor } from '../util/topmostLineMonitor'; -import { DynamicMarkdownPreview } from './preview'; +import { DynamicMarkdownPreview, ManagedMarkdownPreview, StaticMarkdownPreview } from './preview'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContentProvider } from './previewContentProvider'; @@ -18,9 +19,9 @@ export interface DynamicPreviewSettings { readonly locked: boolean; } -class PreviewStore extends Disposable { +class PreviewStore<T extends ManagedMarkdownPreview> extends Disposable { - private readonly _previews = new Set<DynamicMarkdownPreview>(); + private readonly _previews = new Set<T>(); public dispose(): void { super.dispose(); @@ -30,11 +31,11 @@ class PreviewStore extends Disposable { this._previews.clear(); } - [Symbol.iterator](): Iterator<DynamicMarkdownPreview> { + [Symbol.iterator](): Iterator<T> { return this._previews[Symbol.iterator](); } - public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): DynamicMarkdownPreview | undefined { + public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): T | undefined { for (const preview of this._previews) { if (preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)) { return preview; @@ -43,34 +44,37 @@ class PreviewStore extends Disposable { return undefined; } - public add(preview: DynamicMarkdownPreview) { + public add(preview: T) { this._previews.add(preview); } - public delete(preview: DynamicMarkdownPreview) { + public delete(preview: T) { this._previews.delete(preview); } } -export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.WebviewCustomEditorProvider { +export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.CustomTextEditorProvider { private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus'; private readonly _topmostLineMonitor = new TopmostLineMonitor(); private readonly _previewConfigurations = new MarkdownPreviewConfigurationManager(); - private readonly _dynamicPreviews = this._register(new PreviewStore()); - private readonly _staticPreviews = this._register(new PreviewStore()); + private readonly _dynamicPreviews = this._register(new PreviewStore<DynamicMarkdownPreview>()); + private readonly _staticPreviews = this._register(new PreviewStore<StaticMarkdownPreview>()); - private _activePreview: DynamicMarkdownPreview | undefined = undefined; + private _activePreview: ManagedMarkdownPreview | undefined = undefined; + + private readonly customEditorViewType = 'vscode.markdown.preview.editor'; public constructor( private readonly _contentProvider: MarkdownContentProvider, private readonly _logger: Logger, - private readonly _contributions: MarkdownContributionProvider + private readonly _contributions: MarkdownContributionProvider, + private readonly _engine: MarkdownEngine, ) { super(); this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this)); - this._register(vscode.window.registerWebviewCustomEditorProvider('vscode.markdown.preview.editor', this)); + this._register(vscode.window.registerCustomEditorProvider(this.customEditorViewType, this)); } public refresh() { @@ -115,7 +119,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview public toggleLock() { const preview = this._activePreview; - if (preview) { + if (preview instanceof DynamicMarkdownPreview) { preview.toggleLock(); // Close any previews that are now redundant, such as having two dynamic previews in the same editor group @@ -143,23 +147,24 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.registerDynamicPreview(preview); } - public async resolveWebviewEditor( - resource: vscode.Uri, + public async resolveCustomTextEditor( + document: vscode.TextDocument, webview: vscode.WebviewPanel ): Promise<void> { - const preview = DynamicMarkdownPreview.revive( - { resource, locked: false, resourceColumn: vscode.ViewColumn.One }, + const preview = StaticMarkdownPreview.revive( + document.uri, webview, this._contentProvider, this._previewConfigurations, this._logger, - this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.registerStaticPreview(preview); } @@ -178,7 +183,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.setPreviewActiveContext(true); this._activePreview = preview; @@ -201,7 +207,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview return preview; } - private registerStaticPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview { + private registerStaticPreview(preview: StaticMarkdownPreview): StaticMarkdownPreview { this._staticPreviews.add(preview); preview.onDispose(() => { @@ -212,7 +218,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview return preview; } - private trackActive(preview: DynamicMarkdownPreview): void { + private trackActive(preview: ManagedMarkdownPreview): void { preview.onDidChangeViewState(({ webviewPanel }) => { this.setPreviewActiveContext(webviewPanel.active); this._activePreview = webviewPanel.active ? preview : undefined; diff --git a/extensions/markdown-language-features/src/features/smartSelect.ts b/extensions/markdown-language-features/src/features/smartSelect.ts new file mode 100644 index 00000000000..0be50d0cae1 --- /dev/null +++ b/extensions/markdown-language-features/src/features/smartSelect.ts @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Token } from 'markdown-it'; +import * as vscode from 'vscode'; +import { MarkdownEngine } from '../markdownEngine'; +import { TableOfContentsProvider, TocEntry } from '../tableOfContentsProvider'; + +export default class MarkdownSmartSelect implements vscode.SelectionRangeProvider { + + constructor( + private readonly engine: MarkdownEngine + ) { } + + public async provideSelectionRanges(document: vscode.TextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise<vscode.SelectionRange[] | undefined> { + let promises = await Promise.all(positions.map((position) => { + return this.provideSelectionRange(document, position, _token); + })); + return promises.filter(item => item !== undefined) as vscode.SelectionRange[]; + } + + private async provideSelectionRange(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.SelectionRange | undefined> { + const headerRange = await this.getHeaderSelectionRange(document, position); + const blockRange = await this.getBlockSelectionRange(document, position, headerRange); + return blockRange ? blockRange : headerRange ? headerRange : undefined; + } + + private async getBlockSelectionRange(document: vscode.TextDocument, position: vscode.Position, headerRange?: vscode.SelectionRange): Promise<vscode.SelectionRange | undefined> { + + const tokens = await this.engine.parse(document); + + let blockTokens = getTokensForPosition(tokens, position); + + if (blockTokens.length === 0) { + return undefined; + } + + let parentRange = headerRange ? headerRange : createBlockRange(document, position.line, blockTokens.shift()); + let currentRange: vscode.SelectionRange | undefined; + + for (const token of blockTokens) { + currentRange = createBlockRange(document, position.line, token, parentRange); + if (currentRange) { + parentRange = currentRange; + } + } + if (currentRange) { + return currentRange; + } else { + return parentRange; + } + } + + private async getHeaderSelectionRange(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.SelectionRange | undefined> { + const tocProvider = new TableOfContentsProvider(this.engine, document); + const toc = await tocProvider.getToc(); + + let headerInfo = getHeadersForPosition(toc, position); + + let headers = headerInfo.headers; + + let parentRange: vscode.SelectionRange | undefined; + let currentRange: vscode.SelectionRange | undefined; + + for (let i = 0; i < headers.length; i++) { + currentRange = createHeaderRange(i === headers.length - 1, headerInfo.headerOnThisLine, headers[i], parentRange, getFirstChildHeader(document, headers[i], toc)); + if (currentRange && currentRange.parent) { + parentRange = currentRange; + } + } + return currentRange; + } +} + +function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, toc?: TocEntry[]): vscode.Position | undefined { + let childRange: vscode.Position | undefined; + if (header && toc) { + let children = toc.filter(t => header.location.range.contains(t.location.range) && t.location.range.start.line > header.location.range.start.line).sort((t1, t2) => t1.line - t2.line); + if (children.length > 0) { + childRange = children[0].location.range.start; + let lineText = document.lineAt(childRange.line - 1).text; + return childRange ? childRange.translate(-1, lineText.length) : undefined; + } + } + return undefined; +} + +function getTokensForPosition(tokens: Token[], position: vscode.Position): Token[] { + let enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && isBlockElement(token)); + + if (enclosingTokens.length === 0) { + return []; + } + + let sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0])); + return sortedTokens; +} + +function getHeadersForPosition(toc: TocEntry[], position: vscode.Position): { headers: TocEntry[], headerOnThisLine: boolean } { + let enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line); + let sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line)); + let onThisLine = toc.find(header => header.line === position.line) !== undefined; + return { + headers: sortedHeaders, + headerOnThisLine: onThisLine + }; +} + +function isBlockElement(token: Token): boolean { + return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type); +} + +function createHeaderRange(isClosestHeaderToPosition: boolean, onHeaderLine: boolean, header?: TocEntry, parent?: vscode.SelectionRange, childStart?: vscode.Position): vscode.SelectionRange | undefined { + if (header) { + let contentRange = new vscode.Range(header.location.range.start.translate(1), header.location.range.end); + let headerPlusContentRange = header.location.range; + let partialContentRange = childStart && isClosestHeaderToPosition ? contentRange.with(undefined, childStart) : undefined; + if (onHeaderLine && isClosestHeaderToPosition && childStart) { + return new vscode.SelectionRange(header.location.range.with(undefined, childStart), new vscode.SelectionRange(header.location.range, parent)); + } else if (onHeaderLine && isClosestHeaderToPosition) { + return new vscode.SelectionRange(header.location.range, parent); + } else if (parent && parent.range.contains(headerPlusContentRange)) { + if (partialContentRange) { + return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange, parent)))); + } else { + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange, parent)); + } + } else if (partialContentRange) { + return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange)))); + } else { + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange)); + } + } else { + return undefined; + } +} + +function createBlockRange(document: vscode.TextDocument, cursorLine: number, block?: Token, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + if (block) { + if (block.type === 'fence') { + return createFencedRange(block, cursorLine, document, parent); + } else { + let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0]; + let endLine = startLine !== block.map[1] && isList(block.type) ? block.map[1] - 1 : block.map[1]; + let startPos = new vscode.Position(startLine, 0); + let endPos = new vscode.Position(endLine, getEndCharacter(document, startLine, endLine)); + let range = new vscode.Range(startPos, endPos); + if (parent && parent.range.contains(range) && !parent.range.isEqual(range)) { + return new vscode.SelectionRange(range, parent); + } else if (parent) { + if (rangeLinesEqual(range, parent.range)) { + return range.end.character > parent.range.end.character ? new vscode.SelectionRange(range) : parent; + } else if (parent.range.end.line + 1 === range.end.line) { + let adjustedRange = new vscode.Range(range.start, range.end.translate(-1, parent.range.end.character)); + if (adjustedRange.isEqual(parent.range)) { + return parent; + } else { + return new vscode.SelectionRange(adjustedRange, parent); + } + } else if (parent.range.end.line === range.end.line) { + let adjustedRange = new vscode.Range(parent.range.start, range.end.translate(undefined, parent.range.end.character)); + if (adjustedRange.isEqual(parent.range)) { + return parent; + } else { + return new vscode.SelectionRange(adjustedRange, parent.parent); + } + } else { + return parent; + } + } else { + return new vscode.SelectionRange(range); + } + } + } else { + return undefined; + } +} + +function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange { + const startLine = token.map[0]; + const endLine = token.map[1] - 1; + let onFenceLine = cursorLine === startLine || cursorLine === endLine; + let fenceRange = new vscode.Range(new vscode.Position(startLine, 0), new vscode.Position(endLine, document.lineAt(endLine).text.length)); + let contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(new vscode.Position(startLine + 1, 0), new vscode.Position(endLine - 1, getEndCharacter(document, startLine + 1, endLine))) : undefined; + if (parent && contentRange) { + if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) { + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); + } else if (parent.range.isEqual(fenceRange)) { + return new vscode.SelectionRange(contentRange, parent); + } else if (rangeLinesEqual(fenceRange, parent.range)) { + let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range; + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(revisedRange, getRealParent(parent, revisedRange))); + } else if (parent.range.end.line === fenceRange.end.line) { + parent.range.end.translate(undefined, fenceRange.end.character); + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); + } + } else if (contentRange) { + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange)); + } else if (parent) { + if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) { + return new vscode.SelectionRange(fenceRange, parent); + } else if (parent.range.isEqual(fenceRange)) { + return parent; + } else if (rangeLinesEqual(fenceRange, parent.range)) { + let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range; + return new vscode.SelectionRange(revisedRange, parent.parent); + } else if (parent.range.end.line === fenceRange.end.line) { + parent.range.end.translate(undefined, fenceRange.end.character); + return new vscode.SelectionRange(fenceRange, parent); + } + } + return new vscode.SelectionRange(fenceRange, parent); +} + +function isList(type: string): boolean { + return type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(type) : false; +} + +function getEndCharacter(document: vscode.TextDocument, startLine: number, endLine: number): number { + let startLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0; + let endLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0; + let endChar = Math.max(startLength, endLength); + return startLine !== endLine ? 0 : endChar; +} + +function getRealParent(parent: vscode.SelectionRange, range: vscode.Range) { + let currentParent: vscode.SelectionRange | undefined = parent; + while (currentParent && !currentParent.range.contains(range)) { + currentParent = currentParent.parent; + } + return currentParent; +} + +function rangeLinesEqual(range: vscode.Range, parent: vscode.Range) { + return range.start.line === parent.start.line && range.end.line === parent.end.line; +} diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 33cb220b449..8bb509d4565 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; -import * as path from 'path'; import { MarkdownIt, Token } from 'markdown-it'; +import * as path from 'path'; import * as vscode from 'vscode'; import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; import { SkinnyTextDocument } from './tableOfContentsProvider'; -import { Schemes, isOfScheme } from './util/links'; +import { hash } from './util/hash'; +import { isOfScheme, MarkdownFileExtensions, Schemes } from './util/links'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; @@ -129,7 +129,6 @@ export class MarkdownEngine { } this.currentDocument = document.uri; - this._slugCount = new Map<string, number>(); const tokens = this.tokenizeString(document.getText(), engine); this._tokenCache.update(document, config, tokens); @@ -137,6 +136,8 @@ export class MarkdownEngine { } private tokenizeString(text: string, engine: MarkdownIt) { + this._slugCount = new Map<string, number>(); + return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); } @@ -197,9 +198,7 @@ export class MarkdownEngine { const src = token.attrGet('src'); if (src) { - const hash = crypto.createHash('sha256'); - hash.update(src); - const imgHash = hash.digest('hex'); + const imgHash = hash(src); token.attrSet('id', `image-hash-${imgHash}`); } @@ -242,7 +241,7 @@ export class MarkdownEngine { if (uri.path[0] === '/') { const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!); if (root) { - const fileUri = vscode.Uri.file(path.join(root.uri.fsPath, uri.fsPath)); + const fileUri = vscode.Uri.joinPath(root.uri, uri.fsPath); uri = fileUri.with({ scheme: uri.scheme, fragment: uri.fragment, @@ -251,7 +250,9 @@ export class MarkdownEngine { } } - if (uri.fragment) { + const extname = path.extname(uri.fsPath); + + if (uri.fragment && (extname === '' || MarkdownFileExtensions.includes(extname))) { uri = uri.with({ fragment: this.slugifier.fromHeading(uri.fragment).value }); @@ -355,4 +356,3 @@ function normalizeHighlightLang(lang: string | undefined) { return lang; } } - diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index b03c5df80bd..387a41388c0 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as path from 'path'; -import { Disposable } from './util/dispose'; import * as arrays from './util/arrays'; +import { Disposable } from './util/dispose'; const resolveExtensionResource = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => { - return vscode.Uri.file(path.join(extension.extensionPath, resourcePath)); + return vscode.Uri.joinPath(extension.extensionUri, resourcePath); }; const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePaths: unknown): vscode.Uri[] => { @@ -71,7 +70,7 @@ export namespace MarkdownContributions { const previewStyles = getContributedStyles(contributions, extension); const previewScripts = getContributedScripts(contributions, extension); - const previewResourceRoots = previewStyles.length || previewScripts.length ? [vscode.Uri.file(extension.extensionPath)] : []; + const previewResourceRoots = previewStyles.length || previewScripts.length ? [extension.extensionUri] : []; const markdownItPlugins = getContributedMarkdownItPlugins(contributions, extension); return { @@ -114,7 +113,8 @@ export namespace MarkdownContributions { } export interface MarkdownContributionProvider { - readonly extensionPath: string; + readonly extensionUri: vscode.Uri; + readonly contributions: MarkdownContributions; readonly onContributionsChanged: vscode.Event<this>; @@ -125,7 +125,7 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements private _contributions?: MarkdownContributions; public constructor( - public readonly extensionPath: string, + private readonly _extensionContext: vscode.ExtensionContext, ) { super(); @@ -139,6 +139,8 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements }, undefined, this._disposables); } + public get extensionUri() { return this._extensionContext.extensionUri; } + private readonly _onContributionsChanged = this._register(new vscode.EventEmitter<this>()); public readonly onContributionsChanged = this._onContributionsChanged.event; @@ -157,5 +159,5 @@ class VSCodeExtensionMarkdownContributionProvider extends Disposable implements } export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider { - return new VSCodeExtensionMarkdownContributionProvider(context.extensionPath); -} \ No newline at end of file + return new VSCodeExtensionMarkdownContributionProvider(context); +} diff --git a/extensions/markdown-language-features/src/tableOfContentsProvider.ts b/extensions/markdown-language-features/src/tableOfContentsProvider.ts index 6a3a5b62867..3e1de6f6779 100644 --- a/extensions/markdown-language-features/src/tableOfContentsProvider.ts +++ b/extensions/markdown-language-features/src/tableOfContentsProvider.ts @@ -57,19 +57,19 @@ export class TableOfContentsProvider { const toc: TocEntry[] = []; const tokens = await this.engine.parse(document); - const slugCount = new Map<string, number>(); + const existingSlugEntries = new Map<string, { count: number }>(); for (const heading of tokens.filter(token => token.type === 'heading_open')) { const lineNumber = heading.map[0]; const line = document.lineAt(lineNumber); let slug = githubSlugifier.fromHeading(line.text); - if (slugCount.has(slug.value)) { - const count = slugCount.get(slug.value)!; - slugCount.set(slug.value, count + 1); - slug = githubSlugifier.fromHeading(slug.value + '-' + (count + 1)); + const existingSlugEntry = existingSlugEntries.get(slug.value); + if (existingSlugEntry) { + ++existingSlugEntry.count; + slug = githubSlugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); } else { - slugCount.set(slug.value, 0); + existingSlugEntries.set(slug.value, { count: 0 }); } toc.push({ @@ -91,7 +91,7 @@ export class TableOfContentsProvider { break; } } - const endLine = end !== undefined ? end : document.lineCount - 1; + const endLine = end ?? document.lineCount - 1; return { ...entry, location: new vscode.Location(document.uri, diff --git a/extensions/markdown-language-features/src/telemetryReporter.ts b/extensions/markdown-language-features/src/telemetryReporter.ts index d00dca386d1..1104332512d 100644 --- a/extensions/markdown-language-features/src/telemetryReporter.ts +++ b/extensions/markdown-language-features/src/telemetryReporter.ts @@ -48,12 +48,12 @@ export function loadDefaultTelemetryReporter(): TelemetryReporter { } function getPackageInfo(): IPackageInfo | null { - const extention = vscode.extensions.getExtension('Microsoft.vscode-markdown'); - if (extention && extention.packageJSON) { + const extension = vscode.extensions.getExtension('Microsoft.vscode-markdown'); + if (extension && extension.packageJSON) { return { - name: extention.packageJSON.name, - version: extention.packageJSON.version, - aiKey: extention.packageJSON.aiKey + name: extension.packageJSON.name, + version: extension.packageJSON.version, + aiKey: extension.packageJSON.aiKey }; } return null; diff --git a/extensions/markdown-language-features/src/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts new file mode 100644 index 00000000000..9b26d995f27 --- /dev/null +++ b/extensions/markdown-language-features/src/test/documentLink.test.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { joinLines } from './util'; + +const testFileA = workspaceFile('a.md'); + +function workspaceFile(...segments: string[]) { + return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); +} + +async function getLinksForFile(file: vscode.Uri): Promise<vscode.DocumentLink[]> { + return (await vscode.commands.executeCommand<vscode.DocumentLink[]>('vscode.executeLinkProvider', file))!; +} + +suite('Markdown Document links', () => { + + teardown(async () => { + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Should navigate to markdown file', async () => { + await withFileContents(testFileA, '[b](b.md)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file with leading ./', async () => { + await withFileContents(testFileA, '[b](./b.md)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file with leading /', async () => { + await withFileContents(testFileA, '[b](./b.md)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file without file extension', async () => { + await withFileContents(testFileA, '[b](b)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file in directory', async () => { + await withFileContents(testFileA, '[b](sub/c)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('sub', 'c.md')); + }); + + test('Should navigate to fragment by title in file', async () => { + await withFileContents(testFileA, '[b](sub/c#second)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('sub', 'c.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); + }); + + test('Should navigate to fragment by line', async () => { + await withFileContents(testFileA, '[b](sub/c#L2)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('sub', 'c.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); + }); + + test('Should navigate to fragment within current file', async () => { + await withFileContents(testFileA, joinLines( + '[](a#header)', + '[](#header)', + '# Header')); + + const links = await getLinksForFile(testFileA); + { + await executeLink(links[0]); + assertActiveDocumentUri(workspaceFile('a.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 2); + } + { + await executeLink(links[1]); + assertActiveDocumentUri(workspaceFile('a.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 2); + } + }); + + test('Should navigate to fragment within current untitled file', async () => { + const testFile = workspaceFile('x.md').with({ scheme: 'untitled' }); + await withFileContents(testFile, joinLines( + '[](#second)', + '# Second')); + + const [link] = await getLinksForFile(testFile); + await executeLink(link); + + assertActiveDocumentUri(testFile); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); + }); +}); + + +function assertActiveDocumentUri(expectedUri: vscode.Uri) { + assert.strictEqual( + vscode.window.activeTextEditor!.document.uri.fsPath, + expectedUri.fsPath + ); +} + +async function withFileContents(file: vscode.Uri, contents: string): Promise<void> { + const document = await vscode.workspace.openTextDocument(file); + const editor = await vscode.window.showTextDocument(document); + await editor.edit(edit => { + edit.replace(new vscode.Range(0, 0, 1000, 0), contents); + }); +} + +async function executeLink(link: vscode.DocumentLink) { + const args = JSON.parse(decodeURIComponent(link.target!.query)); + await vscode.commands.executeCommand(link.target!.path, args); +} + diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index ba6cfa2567f..fbcaf251204 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -10,7 +10,7 @@ import LinkProvider from '../features/documentLinkProvider'; import { InMemoryDocument } from './inMemoryDocument'; -const testFileName = vscode.Uri.file('test.md'); +const testFile = vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, 'x.md'); const noopToken = new class implements vscode.CancellationToken { private _onCancellationRequestedEmitter = new vscode.EventEmitter<void>(); @@ -20,7 +20,7 @@ const noopToken = new class implements vscode.CancellationToken { }; function getLinksForFile(fileContents: string) { - const doc = new InMemoryDocument(testFileName, fileContents); + const doc = new InMemoryDocument(testFile, fileContents); const provider = new LinkProvider(); return provider.provideDocumentLinks(doc, noopToken); } @@ -118,26 +118,32 @@ suite('markdown.DocumentLinkProvider', () => { const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); assert.strictEqual(links.length, 2); const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0,13,0,22)); - assertRangeEqual(link2.range, new vscode.Range(0,25,0,44)); + assertRangeEqual(link1.range, new vscode.Range(0, 13, 0, 22)); + assertRangeEqual(link2.range, new vscode.Range(0, 25, 0, 44)); } { const links = getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); assert.strictEqual(links.length, 2); const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0,7,0,21)); - assertRangeEqual(link2.range, new vscode.Range(0,26,0,48)); + assertRangeEqual(link1.range, new vscode.Range(0, 7, 0, 21)); + assertRangeEqual(link2.range, new vscode.Range(0, 26, 0, 48)); } { const links = getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); assert.strictEqual(links.length, 4); const [link1, link2, link3, link4] = links; - assertRangeEqual(link1.range, new vscode.Range(0,6,0,14)); - assertRangeEqual(link2.range, new vscode.Range(0,17,0,26)); - assertRangeEqual(link3.range, new vscode.Range(0,39,0,47)); - assertRangeEqual(link4.range, new vscode.Range(0,50,0,59)); + assertRangeEqual(link1.range, new vscode.Range(0, 6, 0, 14)); + assertRangeEqual(link2.range, new vscode.Range(0, 17, 0, 26)); + assertRangeEqual(link3.range, new vscode.Range(0, 39, 0, 47)); + assertRangeEqual(link4.range, new vscode.Range(0, 50, 0, 59)); } }); + + // #107471 + test('Should not consider link references starting with ^ character valid', () => { + const links = getLinksForFile('[^reference]: https://example.com'); + assert.strictEqual(links.length, 0); + }); }); diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index cb74a5b695f..e75be4abac5 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -10,7 +10,7 @@ import { githubSlugifier } from '../slugify'; import { Disposable } from '../util/dispose'; const emptyContributions = new class extends Disposable implements MarkdownContributionProvider { - readonly extensionPath = ''; + readonly extensionUri = vscode.Uri.file('/'); readonly contributions = MarkdownContributions.Empty; readonly onContributionsChanged = this._register(new vscode.EventEmitter<this>()).event; }; diff --git a/extensions/markdown-language-features/src/test/foldingProvider.test.ts b/extensions/markdown-language-features/src/test/foldingProvider.test.ts index 2ad8444d967..11a4c60752d 100644 --- a/extensions/markdown-language-features/src/test/foldingProvider.test.ts +++ b/extensions/markdown-language-features/src/test/foldingProvider.test.ts @@ -175,6 +175,18 @@ a`); assert.strictEqual(firstFold.start, 1); assert.strictEqual(firstFold.end, 3); }); + + test('Should fold html block comments', async () => { + const folds = await getFoldsForDocument(`x +<!-- +fa +-->`); + assert.strictEqual(folds.length, 1); + const firstFold = folds[0]; + assert.strictEqual(firstFold.start, 1); + assert.strictEqual(firstFold.end, 3); + assert.strictEqual(firstFold.kind, vscode.FoldingRangeKind.Comment); + }); }); diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index c2472e5a4ec..052216f90f5 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -22,6 +22,7 @@ export class InMemoryDocument implements vscode.TextDocument { isDirty: boolean = false; isClosed: boolean = false; eol: vscode.EndOfLine = vscode.EndOfLine.LF; + notebook: undefined; get fileName(): string { return this.uri.fsPath; @@ -66,4 +67,4 @@ export class InMemoryDocument implements vscode.TextDocument { save(): never { throw new Error('Method not implemented.'); } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/src/test/index.ts b/extensions/markdown-language-features/src/test/index.ts index 77019228745..0eb9bc92487 100644 --- a/extensions/markdown-language-features/src/test/index.ts +++ b/extensions/markdown-language-features/src/test/index.ts @@ -6,21 +6,31 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Markdown Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Markdown Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Markdown Tests'; +} else { + suite = 'Integration Markdown Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { reporterEnabled: 'spec, mocha-junit-reporter', mochaJunitReporterReporterOptions: { testsuitesTitle: `${suite} ${process.platform}`, - mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) } }; } diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts new file mode 100644 index 00000000000..787df59acc3 --- /dev/null +++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts @@ -0,0 +1,411 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +import MarkdownSmartSelect from '../features/smartSelect'; +import { InMemoryDocument } from './inMemoryDocument'; +import { createNewMarkdownEngine } from './engine'; +import { joinLines } from './util'; +const CURSOR = '$$CURSOR$$'; + +const testFileName = vscode.Uri.file('test.md'); + +suite.only('markdown.SmartSelect', () => { + test('Smart select single word', async () => { + const ranges = await getSelectionRangesForDocument(`Hel${CURSOR}lo`); + assertNestedRangesEqual(ranges![0], [0, 1]); + }); + test('Smart select multi-line paragraph', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. `, + `For example, the[node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter]`, + `(https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).` + )); + assertNestedRangesEqual(ranges![0], [0, 3]); + }); + test('Smart select paragraph', async () => { + const ranges = await getSelectionRangesForDocument(`Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).`); + + assertNestedRangesEqual(ranges![0], [0, 1]); + }); + test('Smart select html block', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `<p align="center">`, + `${CURSOR}<img alt="VS Code in action" src="https://user-images.githubusercontent.com/1487073/58344409-70473b80-7e0a-11e9-8570-b2efc6f8fa44.png">`, + `</p>`)); + + assertNestedRangesEqual(ranges![0], [0, 3]); + }); + test('Smart select header on header line', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# Header${CURSOR}`, + `Hello`)); + + assertNestedRangesEqual(ranges![0], [0, 1]); + + }); + test('Smart select single word w grandparent header on text line', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `## ParentHeader`, + `# Header`, + `${CURSOR}Hello` + )); + + assertNestedRangesEqual(ranges![0], [2, 2], [1, 2]); + }); + test('Smart select html block w parent header', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# Header`, + `${CURSOR}<p align="center">`, + `<img alt="VS Code in action" src="https://user-images.githubusercontent.com/1487073/58344409-70473b80-7e0a-11e9-8570-b2efc6f8fa44.png">`, + `</p>`)); + + assertNestedRangesEqual(ranges![0], [1, 3], [1, 3], [0, 3]); + }); + test('Smart select fenced code block', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `~~~`, + `a${CURSOR}`, + `~~~`)); + + assertNestedRangesEqual(ranges![0], [0, 2]); + }); + test('Smart select list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `- item 1`, + `- ${CURSOR}item 2`, + `- item 3`, + `- item 4`)); + + assertNestedRangesEqual(ranges![0], [1, 1], [0, 3]); + }); + test('Smart select list with fenced code block', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `- item 1`, + `- ~~~`, + ` ${CURSOR}a`, + ` ~~~`, + `- item 3`, + `- item 4`)); + + assertNestedRangesEqual(ranges![0], [1, 3], [0, 5]); + }); + test('Smart select multi cursor', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `- ${CURSOR}item 1`, + `- ~~~`, + ` a`, + ` ~~~`, + `- ${CURSOR}item 3`, + `- item 4`)); + + assertNestedRangesEqual(ranges![0], [0, 0], [0, 5]); + assertNestedRangesEqual(ranges![1], [4, 4], [0, 5]); + }); + test('Smart select nested block quotes', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `> item 1`, + `> item 2`, + `>> ${CURSOR}item 3`, + `>> item 4`)); + assertNestedRangesEqual(ranges![0], [2, 4], [0, 4]); + }); + test('Smart select multi nested block quotes', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `> item 1`, + `>> item 2`, + `>>> ${CURSOR}item 3`, + `>>>> item 4`)); + + assertNestedRangesEqual(ranges![0], [2, 3], [2, 4], [1, 4], [0, 4]); + }); + test('Smart select subheader content', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + `content 1`, + `## sub header 1`, + `${CURSOR}content 2`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]); + }); + test('Smart select subheader line', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + `content 1`, + `## sub header 1${CURSOR}`, + `content 2`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [0, 3]); + }); + test('Smart select blank line', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + `content 1`, + `${CURSOR} `, + `content 2`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [1, 3], [0, 3]); + }); + test('Smart select line between paragraphs', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `paragraph 1`, + `${CURSOR}`, + `paragraph 2`)); + + assertNestedRangesEqual(ranges![0], [0, 3]); + }); + test('Smart select empty document', async () => { + const ranges = await getSelectionRangesForDocument(``, [new vscode.Position(0, 0)]); + assert.strictEqual(ranges!.length, 0); + }); + test('Smart select fenced code block then list then subheader content then subheader then header content then header', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + `content 1`, + `## sub header 1`, + `- item 1`, + `- ~~~`, + ` ${CURSOR}a`, + ` ~~~`, + `- item 3`, + `- item 4`, + ``, + `more content`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]); + }); + test('Smart select list with one element without selecting child subheader', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list ${CURSOR}`, + ``, + `## sub header`, + ``, + `content 2`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [1, 6], [0, 6]); + }); + test('Smart select content under header then subheaders and their content', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main ${CURSOR}header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + ``, + `content 2`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [0, 3], [0, 6]); + }); + test('Smart select last blockquote element under header then subheaders and their content', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `> block`, + `> block`, + `>> block`, + `>> ${CURSOR}block`, + ``, + `paragraph`, + `## sub header`, + ``, + `content 2`, + `# main header 2`)); + + assertNestedRangesEqual(ranges![0], [4, 6], [2, 6], [1, 7], [1, 10], [0, 10]); + }); + test('Smart select content of subheader then subheader then content of main header then main header', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `> block`, + `> block`, + `>> block`, + `>> block`, + ``, + `paragraph`, + `## sub header`, + ``, + ``, + `${CURSOR}`, + ``, + `### main header 2`, + `- content 2`, + `- content 2`, + `- content 2`, + `content 2`)); + + assertNestedRangesEqual(ranges![0], [11, 12], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]); + }); + test('Smart select last line content of subheader then subheader then content of main header then main header', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `> block`, + `> block`, + `>> block`, + `>> block`, + ``, + `paragraph`, + `## sub header`, + ``, + ``, + ``, + ``, + `### main header 2`, + `- content 2`, + `- content 2`, + `- content 2`, + `${CURSOR}content 2`)); + + assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); + }); + test('Smart select last line content after content of subheader then subheader then content of main header then main header', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `> block`, + `> block`, + `>> block`, + `>> block`, + ``, + `paragraph`, + `## sub header`, + ``, + ``, + ``, + ``, + `### main header 2`, + `- content 2`, + `- content 2`, + `- content 2`, + `content 2${CURSOR}`)); + + assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); + }); + test('Smart select fenced code block then list then rest of content', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `> block`, + `> block`, + `>> block`, + `>> block`, + ``, + `- paragraph`, + `- ~~~`, + ` my`, + ` ${CURSOR}code`, + ` goes here`, + ` ~~~`, + `- content`, + `- content 2`, + `- content 2`, + `- content 2`, + `- content 2`)); + + assertNestedRangesEqual(ranges![0], [9, 11], [8, 12], [7, 17], [1, 17], [0, 17]); + }); + test('Smart select fenced code block then list then rest of content on fenced line', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `> block`, + `> block`, + `>> block`, + `>> block`, + ``, + `- paragraph`, + `- ~~~${CURSOR}`, + ` my`, + ` code`, + ` goes here`, + ` ~~~`, + `- content`, + `- content 2`, + `- content 2`, + `- content 2`, + `- content 2`)); + + assertNestedRangesEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]); + }); +}); + +function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) { + const lineage = getLineage(range); + assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length}`); + for (let i = 0; i < lineage.length; i++) { + assertRangesEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`); + } +} + +function getLineage(range: vscode.SelectionRange): vscode.SelectionRange[] { + const result: vscode.SelectionRange[] = []; + let currentRange: vscode.SelectionRange | undefined = range; + while (currentRange) { + result.push(currentRange); + currentRange = currentRange.parent; + } + return result; +} + +function assertRangesEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) { + assert.strictEqual(selectionRange.range.start.line, startLine, `failed on start line ${message}`); + assert.strictEqual(selectionRange.range.end.line, endLine, `failed on end line ${message}`); +} + +async function getSelectionRangesForDocument(contents: string, pos?: vscode.Position[]) { + const doc = new InMemoryDocument(testFileName, contents); + const provider = new MarkdownSmartSelect(createNewMarkdownEngine()); + const positions = pos ? pos : getCursorPositions(contents, doc); + return await provider.provideSelectionRanges(doc, positions, new vscode.CancellationTokenSource().token); +} + +let getCursorPositions = (contents: string, doc: InMemoryDocument): vscode.Position[] => { + let positions: vscode.Position[] = []; + let index = 0; + let wordLength = 0; + while (index !== -1) { + index = contents.indexOf(CURSOR, index + wordLength); + if (index !== -1) { + positions.push(doc.positionAt(index)); + } + wordLength = CURSOR.length; + } + return positions; +}; diff --git a/extensions/markdown-language-features/src/test/util.ts b/extensions/markdown-language-features/src/test/util.ts new file mode 100644 index 00000000000..d50e9ca5db2 --- /dev/null +++ b/extensions/markdown-language-features/src/test/util.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; + +export const joinLines = (...args: string[]) => + args.join(os.platform() === 'win32' ? '\r\n' : '\n'); diff --git a/extensions/markdown-language-features/src/util/hash.ts b/extensions/markdown-language-features/src/util/hash.ts new file mode 100644 index 00000000000..b009808968d --- /dev/null +++ b/extensions/markdown-language-features/src/util/hash.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Return a hash value for an object. + */ +export function hash(obj: any, hashVal = 0): number { + switch (typeof obj) { + case 'object': + if (obj === null) { + return numberHash(349, hashVal); + } else if (Array.isArray(obj)) { + return arrayHash(obj, hashVal); + } + return objectHash(obj, hashVal); + case 'string': + return stringHash(obj, hashVal); + case 'boolean': + return booleanHash(obj, hashVal); + case 'number': + return numberHash(obj, hashVal); + case 'undefined': + return 937 * 31; + default: + return numberHash(obj, 617); + } +} + +function numberHash(val: number, initialHashVal: number): number { + return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32 +} + +function booleanHash(b: boolean, initialHashVal: number): number { + return numberHash(b ? 433 : 863, initialHashVal); +} + +function stringHash(s: string, hashVal: number) { + hashVal = numberHash(149417, hashVal); + for (let i = 0, length = s.length; i < length; i++) { + hashVal = numberHash(s.charCodeAt(i), hashVal); + } + return hashVal; +} + +function arrayHash(arr: any[], initialHashVal: number): number { + initialHashVal = numberHash(104579, initialHashVal); + return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal); +} + +function objectHash(obj: any, initialHashVal: number): number { + initialHashVal = numberHash(181387, initialHashVal); + return Object.keys(obj).sort().reduce((hashVal, key) => { + hashVal = stringHash(key, hashVal); + return hash(obj[key], hashVal); + }, initialHashVal); +} diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index 74383466f72..3545d03a54d 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -9,6 +9,7 @@ export const Schemes = { http: 'http:', https: 'https:', file: 'file:', + untitled: 'untitled', mailto: 'mailto:', data: 'data:', vscode: 'vscode:', @@ -32,3 +33,15 @@ export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | export function isOfScheme(scheme: string, link: string): boolean { return link.toLowerCase().startsWith(scheme); } + +export const MarkdownFileExtensions: readonly string[] = [ + '.md', + '.mkd', + '.mdwn', + '.mdown', + '.markdown', + '.markdn', + '.mdtxt', + '.mdtext', + '.workbook', +]; diff --git a/extensions/markdown-language-features/test-workspace/a.md b/extensions/markdown-language-features/test-workspace/a.md new file mode 100644 index 00000000000..9d70918067b --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/a.md @@ -0,0 +1,4 @@ +[b](b) +[b](b.md) +[b](./b.md) +[b](/b.md) diff --git a/extensions/markdown-language-features/test-workspace/b.md b/extensions/markdown-language-features/test-workspace/b.md new file mode 100644 index 00000000000..730f53ee6ce --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/b.md @@ -0,0 +1,3 @@ +# b + +[](./a) \ No newline at end of file diff --git a/extensions/markdown-language-features/test-workspace/sub/c.md b/extensions/markdown-language-features/test-workspace/sub/c.md new file mode 100644 index 00000000000..f5c9bec43ac --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/sub/c.md @@ -0,0 +1,6 @@ +# First +# Second + +[b](/b.md) +[b](../b.md) +[b](./../b.md) diff --git a/src/vs/base/test/node/encoding/fixtures/empty.txt b/extensions/markdown-language-features/test-workspace/sub/d.md similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/empty.txt rename to extensions/markdown-language-features/test-workspace/sub/d.md diff --git a/extensions/markdown-language-features/webpack.config.js b/extensions/markdown-language-features/webpack.config.js index 58996c3eb7d..865444125fe 100644 --- a/extensions/markdown-language-features/webpack.config.js +++ b/extensions/markdown-language-features/webpack.config.js @@ -21,9 +21,8 @@ module.exports = { resolve: { extensions: ['.tsx', '.ts', '.js'] }, - devtool: 'inline-source-map', output: { filename: '[name].js', path: path.resolve(__dirname, 'media') } -}; \ No newline at end of file +}; diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 8ec43a6d0ec..5f1895e968d 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -191,9 +191,9 @@ abbrev@1: integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== acorn@^6.2.1: - version "6.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" - integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== ajv-errors@^1.0.0: version "1.0.1" @@ -538,9 +538,9 @@ bluebird@^3.5.5: integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== boom@2.x.x: version "2.10.1" @@ -1265,9 +1265,9 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" elliptic@^6.0.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" - integrity sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8= + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -2099,12 +2099,12 @@ hash-base@^3.0.0: safe-buffer "^5.0.1" hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" - integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" - minimalistic-assert "^1.0.0" + minimalistic-assert "^1.0.1" hawk@3.1.3, hawk@~3.1.3: version "3.1.3" @@ -2221,16 +2221,21 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" @@ -2773,9 +2778,9 @@ lodash.throttle@^4.1.1: integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= lodash@^4.16.4: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== lru-cache@^5.1.1: version "5.1.1" @@ -2821,10 +2826,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it-front-matter@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20" - integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA= +markdown-it-front-matter@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.2.1.tgz#dca49a827bb3cebb0528452c1d87dff276eb28dc" + integrity sha512-ydUIqlKfDscRpRUTRcA3maeeUKn3Cl5EaKZSA+I/f0KOGCBurW7e+bbz59sxqkC3FA9Q2S2+t4mpkH9T0BCM6A== markdown-it@^10.0.0: version "10.0.0" @@ -2981,10 +2986,10 @@ mimic-fn@^2.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimalistic-assert@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" - integrity sha1-cCvi3aazf0g2vLP121ZkG2Sh09M= +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" @@ -4923,9 +4928,9 @@ yallist@^3.0.2: integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yargs-parser@^13.1.0: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" diff --git a/extensions/merge-conflict/.vscodeignore b/extensions/merge-conflict/.vscodeignore index 36e8b0714fa..f071cfb7c71 100644 --- a/extensions/merge-conflict/.vscodeignore +++ b/extensions/merge-conflict/.vscodeignore @@ -2,4 +2,5 @@ src/** tsconfig.json out/** extension.webpack.config.js -yarn.lock \ No newline at end of file +extension-browser.webpack.config.js +yarn.lock diff --git a/extensions/merge-conflict/extension-browser.webpack.config.js b/extensions/merge-conflict/extension-browser.webpack.config.js new file mode 100644 index 00000000000..e4171bed927 --- /dev/null +++ b/extensions/merge-conflict/extension-browser.webpack.config.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/mergeConflictMain.ts' + }, + output: { + filename: 'mergeConflictMain.js' + } +}); diff --git a/extensions/merge-conflict/extension.webpack.config.js b/extensions/merge-conflict/extension.webpack.config.js index 45600607fc5..7a04ca98e97 100644 --- a/extensions/merge-conflict/extension.webpack.config.js +++ b/extensions/merge-conflict/extension.webpack.config.js @@ -12,6 +12,9 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/extension.ts' - } + extension: './src/mergeConflictMain.ts' + }, + output: { + filename: 'mergeConflictMain.js' + }, }); diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index 815a412492a..d438b96ca61 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -16,7 +16,8 @@ "activationEvents": [ "*" ], - "main": "./out/extension", + "main": "./out/mergeConflictMain", + "browser": "./dist/browser/mergeConflictMain", "scripts": { "compile": "gulp compile-extension:merge-conflict", "watch": "gulp watch-extension:merge-conflict" diff --git a/extensions/merge-conflict/package.nls.json b/extensions/merge-conflict/package.nls.json index 3310dac7e21..3353f40b316 100644 --- a/extensions/merge-conflict/package.nls.json +++ b/extensions/merge-conflict/package.nls.json @@ -14,10 +14,10 @@ "command.compare": "Compare Current Conflict", "config.title": "Merge Conflict", "config.autoNavigateNextConflictEnabled": "Whether to automatically navigate to the next merge conflict after resolving a merge conflict.", - "config.codeLensEnabled": "Create a Code Lens for merge conflict blocks within editor.", + "config.codeLensEnabled": "Create a CodeLens for merge conflict blocks within editor.", "config.decoratorsEnabled": "Create decorators for merge conflict blocks within editor.", "config.diffViewPosition": "Controls where the diff view should be opened when comparing changes in merge conflicts.", "config.diffViewPosition.current": "Open the diff view in the current editor group.", "config.diffViewPosition.beside": "Open the diff view next to the current editor group.", "config.diffViewPosition.below": "Open the diff view below the current editor group." -} \ No newline at end of file +} diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index f79c6650af0..ca45b9a16f8 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import * as interfaces from './interfaces'; import ContentProvider from './contentProvider'; -import * as path from 'path'; import { loadMessageBundle } from 'vscode-nls'; const localize = loadMessageBundle(); @@ -86,7 +85,6 @@ export default class CommandHandler implements vscode.Disposable { } async compare(editor: vscode.TextEditor, conflict: interfaces.IDocumentMergeConflict | null) { - const fileName = path.basename(editor.document.uri.fsPath); // No conflict, command executed from command palette if (!conflict) { @@ -134,6 +132,8 @@ export default class CommandHandler implements vscode.Disposable { conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character ); + const docPath = editor.document.uri.path; + const fileName = docPath.substring(docPath.lastIndexOf('/') + 1); // avoid NodeJS path to keep browser webpack small const title = localize('compareChangesTitle', '{0}: Current Changes ⟷ Incoming Changes', fileName); const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict'); const openToTheSide = mergeConflictConfig.get<string>('diffViewPosition'); @@ -365,4 +365,4 @@ export default class CommandHandler implements vscode.Disposable { conflict: fallback() }; } -} \ No newline at end of file +} diff --git a/extensions/merge-conflict/src/delayer.ts b/extensions/merge-conflict/src/delayer.ts index 3b8f8e01c51..e4ef18e09c9 100644 --- a/extensions/merge-conflict/src/delayer.ts +++ b/extensions/merge-conflict/src/delayer.ts @@ -12,7 +12,7 @@ export class Delayer<T> { public defaultDelay: number; private timeout: any; // Timer private completionPromise: Promise<T> | null; - private onSuccess: ((value?: T | Thenable<T> | undefined) => void) | null; + private onSuccess: ((value: T | PromiseLike<T> | undefined) => void) | null; private task: ITask<T> | null; constructor(defaultDelay: number) { @@ -30,7 +30,7 @@ export class Delayer<T> { } if (!this.completionPromise) { - this.completionPromise = new Promise<T>((resolve) => { + this.completionPromise = new Promise<T | undefined>((resolve) => { this.onSuccess = resolve; }).then(() => { this.completionPromise = null; @@ -76,4 +76,4 @@ export class Delayer<T> { this.timeout = null; } } -} \ No newline at end of file +} diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index 41be7a803e2..74a7d9dfe99 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -49,7 +49,7 @@ class OriginDocumentMergeConflictTracker implements interfaces.IDocumentMergeCon export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTrackerService { private cache: Map<string, ScanTask> = new Map(); - private delayExpireTime: number = 250; + private delayExpireTime: number = 0; getConflicts(document: vscode.TextDocument, origin: string): PromiseLike<interfaces.IDocumentMergeConflict[]> { // Attempt from cache diff --git a/extensions/merge-conflict/src/extension.ts b/extensions/merge-conflict/src/mergeConflictMain.ts similarity index 100% rename from extensions/merge-conflict/src/extension.ts rename to extensions/merge-conflict/src/mergeConflictMain.ts diff --git a/extensions/vscode-account/.vscodeignore b/extensions/microsoft-authentication/.vscodeignore similarity index 58% rename from extensions/vscode-account/.vscodeignore rename to extensions/microsoft-authentication/.vscodeignore index ed3f9d37c1f..46f23a20dba 100644 --- a/extensions/vscode-account/.vscodeignore +++ b/extensions/microsoft-authentication/.vscodeignore @@ -1,10 +1,14 @@ .vscode/** .vscode-test/** out/test/** +out/** +extension.webpack.config.js +extension-browser.webpack.config.js +yarn.lock src/** .gitignore vsc-extension-quickstart.md **/tsconfig.json **/tslint.json **/*.map -**/*.ts \ No newline at end of file +**/*.ts diff --git a/extensions/microsoft-authentication/extension-browser.webpack.config.js b/extensions/microsoft-authentication/extension-browser.webpack.config.js new file mode 100644 index 00000000000..d2f4d9ee94b --- /dev/null +++ b/extensions/microsoft-authentication/extension-browser.webpack.config.js @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const path = require('path'); +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + node: false, + entry: { + extension: './src/extension.ts', + }, + externals: { + 'keytar': 'commonjs keytar' + }, + resolve: { + alias: { + './env/node': path.resolve(__dirname, 'src/env/browser'), + './authServer': path.resolve(__dirname, 'src/env/browser/authServer'), + 'buffer': path.resolve(__dirname, 'node_modules/buffer/index.js'), + 'node-fetch': path.resolve(__dirname, 'node_modules/node-fetch/browser.js'), + 'randombytes': path.resolve(__dirname, 'node_modules/randombytes/browser.js'), + 'stream': path.resolve(__dirname, 'node_modules/stream/index.js'), + 'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js') + } + } +}); diff --git a/extensions/microsoft-authentication/extension.webpack.config.js b/extensions/microsoft-authentication/extension.webpack.config.js new file mode 100644 index 00000000000..e18229a8e17 --- /dev/null +++ b/extensions/microsoft-authentication/extension.webpack.config.js @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const path = require('path'); +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts', + }, + externals: { + 'keytar': 'commonjs keytar' + } +}); diff --git a/extensions/microsoft-authentication/media/auth.css b/extensions/microsoft-authentication/media/auth.css new file mode 100644 index 00000000000..45c42c75ad5 --- /dev/null +++ b/extensions/microsoft-authentication/media/auth.css @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +html { + height: 100%; +} + +body { + box-sizing: border-box; + min-height: 100%; + margin: 0; + padding: 15px 30px; + display: flex; + flex-direction: column; + color: white; + font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; + background-color: #2C2C32; +} + +.branding { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAlqADAAQAAAABAAAAlgAAAADkcSUjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAxaElEQVR4Ae19CbgdRbXu6j2cecoECTIkICCGzAg+7qeQ9544QFQgiXpVEJTEe59ALsbMwE5AMZCQELgKeSoqDlyiQogCSUAC6FNCQhIwQMALCbNMGc68p37/v7prnz47++yzzzl76OCu851d3dXV1VXVf69atWqtVZaUg397IPJwSCKT46zgx16wh+19XSYnO6PjE7H4MDuZ7BSxXhQJ/On5KTWbTSMm3bYlvHXmJNxj2SatFLFVioeWn5lDDwAgMvOU2NG/3DMo2h691rLtr4bqBtVX1NVJRUOVBIIiyYSI3RkXO9q5NZmU5S98tu7XpmQF2AwAzCoNwMrAMm/CT3HkrgqJTI/KwodOD4ZDa5MVdUPtfe+KxONKiSob6+SIE46XqsYGK5FIBoLVtQEJiSRaWp+xktaNuzpqfirTLcBOZNJtdnjrDCk6wMrA8hOgWJcZoFSrT4nJogfPESu4Dv8AVLTTEqsC9MkSDHB2NIYfkZGnjJOqpiZQrkTCAmWyKqtDgXBAEs0tLyZtWRl7953/u/uiUR0sVgH2xuKERCJJnhc6lIFV6B7OuXyAJrIpqDzVlQ99TUKVt0scAEomQaXskBAONlEFrgpvLdkZk6r6Wjn6IxMx2qVeY0IsSVrhqnCgMiiJA62v4a5Vkmz/4a7PDWtmVXSIfGMSAGYVFGCpGvGh5VCiHohEAiJXi77sKx+eLZU1N0hHCyuTAJCCDqCAKIUCYxv4AcY6YnL0qeOkZuhQSeoo6b5OG+AC4ZJQRThQFQbAWt7Bvf8ZSNg3P3deA8ZUF2AzADCrMAArA4u9XMoQsQMp6nHlH5dKdcMcad+fJGVCCKRAxfMU1XKB1dYpI8acKI0jj5FkDNSti3LpzSgjCQQmrFA4HKiuIMAO2EnrtkBb+8pdXxr2OjOd+bAd2vQ2cro8mXPjwH/LwBp4H/a/hLvuCsr06cpky6KHfiK1jRdJ235M80ClABMtmMMfKJQDNB4j1VCstg4ZcTKANWpkZmCxAJZiABYIhgO1VRwi21HGj63OxI27pje9xGz5BlgZWOzVUgSPjEqufOheqWmcIq37YwBCWKsDDGlQULnAUh6L4CLFAh/fCmCNOSE7sNxi3IilxiUQCAdrayR+oCWBcn4q0eSy56c2PMc80+6yg2t4MEAKVgYWO7HYwcz8Ig9XSTz5kNQ0nC7tB6KoBmZ+bnAZdaVUhIMHVDxWQtQCYI3thWKZ8rrHLICiCwCslmIKFGn/GoPm9S+cW7ddswJgZw4Ta9NkC/n6HsrA6nufDewOV/Ap8x8cggHvMUztTpLO5igkCV2g4hMIJI3dY56mqBdwgXOlWP0DlhbNkhVgtoQDELza7Z1iJ2J3J2P2DX8/r+Evmsm2A2dukkBfAYbZSDkUrQco+IQ0XeY9eKwEraekovYkzP4OBhUrlGLEXYB1q6QnzXPYLUtuJ5SLcei1k62tMYDKDtTWnRusrf5/J9zbcv9xa5snc9aooLJti3xYbsVy1lEOxekBI01f8NBECVnbJVx1hHS2YSrnGf4y1SQTwAyYTJzpvr6lAWA2AWYlW1vidrQjGait/VS4pvaPx9/bvOn43+4/GwCzDcAoC0P+rKNd1ot9q1s5d489YIa/BQ9+Ap/yAxKsgJSJ8gGXUc94I1BD4Og/j91zDodYGFTmvf88VsYnpiWSt7ICNXWcoQoo2uNJ217298/X/8bky7ZcVKZYppcKE0OaDg0FDn9XPfwFCYc3YEYGUEUpTQeFUNTgyZniDBUyfJdecu9hVJjAYS+YbGuJ8z9QXXNaqL5uzQlrm3d88O79X+Ujt860MIu17ExDZJliFealkMJYMn1NQNZATnXlQ9+ScPXNEm3H02zKrZQK9PpoxQ5+PBJ3pVwqbkBJpFhjPiSNx47sWY7V60Nyy4BaQDQhdqCyJgRWX5LNLbuA8xUfaKz7sTtEkkhhMuBoU5QpVm792rdclKbzkyWoFj64WCrrAKo2SNMxhukSjb4CvoZe/pGBebyfP88ZTNx14KQX6BdV4McQSna2JZItLXGrsurEUEPdra8daPnv43934CIy+Qoqth2hDKx8v4hpkKbrAi++3AUbf4AlmqukvTmuiytgWfr3OBdg/bs533cpwMDgJ6BFEQtUVB4dGlz/kxPuab5TH8S2A1z9bGi+6/o+KY/8FKkUw6KH/ktqm/4Ngk8y6UHltvVCH39IrQx1SsXmoI9l5Tc72xS2Y50AWGtnaEjdF46/p3mtPuJqqmOUQ356YMZtYai8xITrfzsGb4Dg8392W6IZ8FOygImXslwe8KOzF0Dti2D8vZbO0KC6z37wdwfm/d2yvu8dvbPfXr7acw8YcULkrw0Sa34EoBovHZCmq3Jez7dlvwKkGMCoqIHn+E8tQtse5n1UwZn37HXVq0nOeO14sj0Ws44tD4U59FjWLEaaPue+IyXWskMqa11QUfCZQgaK6OtxhqeaIkpInjLUyiRBjJKMhRrrqkOh5IXlodB0S3/ilDR9/cngyx+RcOVg6WjtXZqe67OUUqVnJroQsgu+nTzF/oXUlsIUTIk/WaZY/e18A6r5D34MXbkV0vTBEoVKZ1Zpen8flv2+FCHLnq0YV4N2Zwcwb48pU6z+dDfVXiKnRKGh8HksJt+tIp44penZlmj68yBzD6DjRQ8pmQbwWR4u2aSau0oQg2JRT9EaUqZYfep9vEaKFGhFs2DDN6Si8m594Yk4RQyF+0gVMRlgwyTzb9qRIZu51C02+Uzc7WLaSS55eIvJh1WHwnVGWt0O+VM1eFgMg4dIXBY8OF+qar8HlRd0JciHRRutEoXUy/Q836R5kjIemnwmzpjJTcwlD7O6+crAytaZ5ppK013B5/wNy6W6/goIPkmlMBD1V5puCu8h5gs66GV6E7zHPZRRsmSrgOS7ZI3K84O9Bg/zN/wcuulfVYMHZ+3Mw+Hk+bmmuHT8pPgrZki/aG4qfVymWNneAfmp6Y5TDpm34T6s+306v9L0bA/ntSzAMZdK45qht4qXKVaPPeTM/GISWVcjnZV/xPB3mho8WL1ofPZY4AAuGHmWAZMpKv3cpPsgLlOsTC/BLNHMXn+YRAOPglE/ERoK3a1oMt1XrDQdDsne4YEEl/kv1vOzPcetUxlY6Z3kLNFEZdGG42Gk/hh00w9Xg4fedNPTy8n7OdBjgKRl+5RcudUqy7G8ADDS9DkPnApQbYNjjsOlsz1/SzTeZ/V2bKiQiZk/HUvp572VWcTrfaNYVLddvTUkgyYl5Rk2c1NARtRbUkIHX3nrKyNNn/fgp6BCcL8E0DUxuHShNL3ULzBFqVCR1KwwlZi3LshnQbkDizMkS61i2dkmqDa2zMQpr7tuDc3FQyOmNH0TtD4hTZ97/1ckFLxDEniBCbpvUZMofzQjHdzp5/6oZaoWuQHLMLOrXmgQO34hvugzUMJgfMnvIH5I9rXfgRfTJpqv9P4vU63r7cAYPESmx2X+A7NgQLoCtn68q7BLNL3VK3U9DT1pp6lsPjzoBVj8mqFWSzOfW549B7ZwP4W67RB1fgm7ACh24T84DZTsKlm2/WKZOX499Z1l512OdYoPG5yqkho8qBAoIXMf+K5UNiyAch4axbdXwiWaVAU9BwZQqdHPJCAPD82/55aSHbp17BlY/JrXQLVmOoa/VTtnSqjqVuFI2LI3itkJmX6nCLrICVceIRVVD8iNTy2RK6yrtVGGypWshVke7Bg8kCqJzF2/Gk45LoGMynEf5HGPl6WEIl/yAIlPTjstcmWyP86tW+ZZoWvCo65sbvrblVLVcCuGCFtiUb6MCjQshH/oOquAtQJMbgKzp6TUD7lKVjy9Ua57bJAaaUZ2dnd0kb1KxblKXtAYPMxd/zss0VwibTB4sEGlaF7sl8AXZP61TjhxX1qqirYO2alTPx0c3JHetbGbdq6SmqZLpXUfAEUnAo7NWA8NYLNjkFBXQJj4NpxkTpMrJj6ixgXPTLNTXut6uLkoyTR4WD0zpg5kB7+3EbrpZ6g0veQyqh5abyTuRBSP1dsM8iYwnMBTn91ph4afdKw0jDrWtqForq+oh6KKmWyDoegOLO/MbuXOO6V20BekdS9ngRwyu+ftsaZ2DPIfmI8je7Rtvlwx9vuatdRDo/FJNe/3gzDZg8FD3RjH4KE/SzTsinTy0WOH9OFCWrl8hD4GPwZYSSj/wmzBqm2y7Ff23H7cZ856Lzxk6LfjLc30nFw69R1PKwmsrqGQL57iAg6DK3c+CCadoOIyhnoh8dzXy6EVhvvohMQ7bQyN18mNf1sHUURdSYdGCj6pnDd349H4RmDwUD0AULH5hQBVT+WaZwF0YEYkGA5KKGRZrfsWyS++fHGgtvofYEpArArjpLaXl93jZYd5N9SE4oTkM5vAd0yQln1k0vvLI/HLsZXa1TbCX7n1nNyw43z5zujHBZ7iVLhaYHfQqRYbafrcB8ZhDHkEk5BG6YD7oP63LVV04Q8AKhIxGuZjsAOVBZtx4C1JJKYmb/78Y/r8aKLKrsYR1A0NBAtfryxPcIkuPPbajjeUVTuOhBcUfM11E6Aa4iy4sqb9/4fPJVCvNrhADFd+QCqr/irLd8zSCYFjht3zjDRLvft0SakwdniYd/+ZuG+LhMKNKk03fj77VFiJMiuDjnGwuoG860a4ED1RAKojbttSwxrB7zYGHuc1MS55AF4IGUjLIUO46RksuNp/lYpqmC/Bw1x+mVln1mhB07Ju6AoMjf8iL738FTy3UzhrjIzm8/IfyKjTfdCc+6bC1eYa/Z7jMcfggS33BsPamDjTtfQ0c55eFtO95XiPzT0m5jUGU0Z6XhvyQ3g6ViXVtr2LZeWUiOZH2044YVL0dT3x5w8oFhys2rIeqiEwX2rPN6hMqx0n+G37YuDdpsqoY56V67eNV1DphCHSxeuZO/obU/7GMjn7m/PANyFNX6MC3XiCzvgpJjk4mDQTe3P0lMb0TNd4rzfde+wt1+TzXk8d66gWxUYC8P4f2yvtLWfJjQAV9e7JA7NtPg8haTzsPzD7GyUt72GbMqksYH35PWJoxDBbUTUK85ptsvypb8q3x96mz/TOSPtbCTV4wGvlJGTu+kWwSr7GFwYPfWoP51RoA4e+1n2PSqdMlZs/97aKSCKngOJGUvDrXiyTDQnsfqX4Z7bOCqc6DsHwNRcnYGjsgJQbHVE3+FZZ/vQdcC7u7CGDnar6XQUtI+L4aJpz/01gdK8BTwL5Gx9Egwd2vJ//teUY+kIBsCRBgGqpLPvMGXLzZxxQcVbrNCCti5wFBCfRL+2juMGW0QoseAzRd8D30O2fzUlP6+nc5DWxN583jUMSCm2Dw/y6QV+R007aKUu3nqRrkpxMcDjrS6BQ10jT59z/K6luvAyg4syPQ6wLqr4UWIq8Nih5NYe+Vuk48FmAap72Az8YB1Q9VwrdfNA3w9wm3RybOD2d5yakX2N6+nWTZtLNPeYc17lXC10yp93NcxM8uU1Sj7HJa2JvRm+aHjueelsh1qiqOREzx7/Jsh0X6mSC7gY5NOYSyKRz2xC24zv3rccQ8iXsRYOv2/ECnEsRpc2jQ19ch77Ols0ST5woy6as06GPn5f5YLJV0tu1Jp83zRybuKc83uvm2Bt7jzOVwTTmwT9dGu6ERS+TvDSV58UKFViH5EaNAfB6PwXftVofTD6J4oJsQaXpYGRnr6+V79y/GUs0ZykPR9/lbgN9G7NdNjeoDGLoqw2h3jfJDWefJiumvJbaszBX0VTf6Hu2Hs3bNQwT1m+kgiIRiB1KFSxYZFNLghoGdYMvkRuf3iHXbzlOxQUEV6ahcZorTZ/1wAjoiO3AyzkFwx+c8XORHKjiv5+DLRz6sA8h9nZuOzBdbvjMLK1uLkOfn9vl1g3A2r8CM8I9mEFV4qU4MiXvOzHH3th7zIK85+nH5rynfCYdW2ugnJBK/Ctrx0qw6hm5fvsXFFwcGimxN4HS9DUQfH573YfwPTwFg4fjpJM7PKjmhcnlV3Bx1ucOfe07xI6dJDees0Y4pNPFRy5DX1cLfXsUkCtOxxZjyU/Br9M+fEFUiekClwFFeszmpKeZc+8102xeM9dNbPJ5z538FbrkEghWQOZ1pyx7apUmczcqUi/VTQeoZq37H3g9T8J90FDpgMGDoVR8kLdMf1EuZ8ivqgthdWO1LPv0eFn22ZecoY+yKX+syjivYWC/kLxjFjYba3k37RwHbYQ/gU85qgDS977WMgyhZhL1SGJovFSWPf1ReIs7T2aOe1ULunTt2Vgp+L0EQcSiEF1YWDqiSoluDwKGg++HE0vDexhwmfO+1iYv+S0ubVHUksTQ9zVZfvYdWqzK71SU0L+nOFIv517vB9W/0gZ+l9vHkOSCtyIluHz0yxK1x0GV5GmdoeCVDfwpAyqBogJ82Zg1Vtd/RIKBF8B7nSUX/OJf4drz90qVYjHOBimecKgUY90ShOfuMaJU8B6nEgt+gFePpZlqLCBH25/DBzNaQUVAkXfMhwEK21Wath3ceW5d+PIkxSTPH7tXmk+eiOn6I1Dwo2ZDqcHF2nHxNY79jaskWLleRh33S2irQqVQfVIFHSDh3XHWrpQJLdPGeWMee/9ZbFECJkSY7VbVYcWh+eey7AmAaspz+iETUO4uDgOuSUkpcebad8mKuGCrZFlnh2fKsr/9FtP/86TNVfSDWkbmIoqQylljPEaib0lTExaGwra8/lpQosB9GE1QIOEqPxPW0tSUMa9pwEnqnIkmk3s5/1EUCo8V0E0TiBJmyvJzVusj2Mfs6/d5cCiWaSS/IkqxGWaffD70qWBoMIgyIcq4Uq9Irxfzx3myA5t43JKamoCMHClSXw+aSlkoMpihzwyFhKFJ1/vdPE5ZXdfy3Q5oTqFI6E5BRTvW8SK2lx+voNKPNgLWA338TxC6A4sNphRbjSkw/s8+eaa07fsurFhI2dhhfF2lDWTQoaggQVTpA0eJHHY4XiPOwetrDVNgwrnqiBtApRDl5DOtYP78BQ5vAlCFsSxzl7zcClHClB2iyoYAVARrmf8koWso9DZYFfHwdX0Y9oHTRy/CrOxt8AkrIZJALjpcpm51Xl+I9+m9H3MU05UQxEOHiVRXibwO7SRSrwoOjahbgJkQWE0e8oVztmji9Fkj0wcWMPRVVKiKTtuBy+XGsx0xiVKqyYXlVdlG/vshuN2YGVisoH5dnLWoOOImyJPeAc/wC3QcGGa4xnUMLErfFFaltk5k5CiRN94QaW4GuCBrVPGDCyRvLfkC2Hj+6zEO9Nh9MzzOOWhm3hhXteGOlldBTc/HssxmxzrpGbsYQ182omua21OT3FZrFzBPT+e85u0ak8+km3OVxOGkZ2DxDn7iEXQaxREzx/4SDD1M6m3sEAqO2ewQakrU/O6Pvije7p5785hrvGSu89jk8V7PlMdcN/eS0pih8UgMje++LfI2qoklOLXUZkt1wOcDkFfv1x8cI874Vkw+VgCB2RlMHXmsRejegwGIZ8JYjlon1rtfkhUXtOrQNx1C3CIFVstbtfTHZrtm8qbnST9nvkxp6enMw/+DeSzmTA86Y4Qa8eyT16NHT8UMrRVGCWTqM89uTA3MU7zlmWtMM9fT09Lzm3zmHhOn0vGWafJPkAw9TOQoAIygUb8eyJxi6HE9xXchnflT/ywUQctkunPaleY552HStZgJhi3M+uZBNgVVF4CKSzOR4oGKVTG457FfQm7AYm2pm04d9dknPwFqMB4znjexvkjtg6J9mVk7jUBiIPWqw2zxmFGC2SNqh+oRPBnB5QJIQcRjPXDKMYBzzry/zARVH7Q9EXsL1uEfx1rfUkdZ8dBQG/Y2plDHuQOLNTDgumL03/Gmxkln6y61fPYLuFhHMzSGgfkjj3aYezL1pGgMBjA8V+rlUjoDKr3uZE3l7zoFakHLqDbc0bIRVk0nyopzHtO1Pi4eF8ukras+vj3qG7DYDIKLPNd3xr0lweYJ4C0ehx2iX6T0TkcTXAZIFEccBYBxFqgyL2QheMyQmKJkSNc0FpGJenHoCwWx3hfAWl8EVOosWXnuPh36etPwZJH/ZKEX5r2H3lCeC7PFKyzunv1Ruf6pP0BK/xnIvEAaqFvFt1jqgCpw0OLQWN8gMhLKjG+8JtIK/1ecNbKGJGLpNeU9BCbTnWOagsZgMQOFRGiAWPHpECVsVIuZnR+Gh8PpmflM3F6UwDawnvz3S0Bd+k6xTOW5eG10pOaMPRuLxXCuTyl90hkuTL6Sx0AIlxXDIKpHjRQZMsShXPTa56VcqSHQTdcXBbJnQ2BWSWPR1kelDUPfcoBKVXci9vtFd6oQr6j/wGJtqCNFjUeGOWMuxBLQcixekwriO/KRLwHv0Hj4CPBeRzqgov0qAaT8Fqps+C4m2kkYY0BmEa4KghIvlZXnnCG3nveWZ+hT6LHp5XBwD/RvKPSWo0wrZkMMc6zZGBbfgT+t66DRyY6nkagDPM1Qwh+Ci4FDYwMWsisprcfQ2IGhkYw+KRazONloLIq1vvZWiFa+BJP2dchgybQ1MBYt8dCnjfD/z8AolmkfZ0NXA0JcvpgDt0Wdzd/Al45XFODSD4dG/wQCjOCiAcnRI0WaBrtMPb8D12KmAgvIHa2bpS3+IQUVhz6G94nasLalwD8Dp1imgo5ukSulH/NjuQGUywrdA78JQch7yGLmB8TmeQONCS6CbPgRWDSGu5Z/vAF+CkNfTS1mfXtXya1TL9dHqCXQIaLmwm/DBFJe7znT09PMuYl7upfXGdLLc1K7/7p58/+yOWNUccTYtVhS+QgsaPaBcPE5BJd/gndoHDTYlpHHBeB36oC89drZCirObA8VixkltmldmwkE6Wnm3MSmiEzn6Wkmb3rMfPjPP7D4oK3u0/78Z5FX9jhMsVq551o79/5iRDo0gomvAs818liR8ad1PXVa12H5qA89AKqVf2BxrYwCw5lrPieHD31C2jub5OUXk2CCAw7L5UNwcUxMQKHLCjTAKvsPct2Om5Bkq24aqW859LkH8ggsDB1k3uli59/v/jqMYO9Rvj2AmWFHR0BefglMMhza0LKGMzD/hQDUgRyj2fpBl8l12zdL5K9HpuwBVHTvv0r7tUb5AZb6bVoMYEFL8lv3zINu0o/AsBM9CTCMWAbBHCEOATXB1Y7pvU4WcZk5/PXfZTRLy6Cq6ufke09+VsHFehq1bb++TR/Va+CzQjK4EXe/5MvWLoerSeyXDPdBAVCwpOvFlxQqRHBhJrZnt7M4TOU8MzPzUYe4VaG4gS6FatGetfK97ddjaJyLa47RrB+NIQh8PwTOClGXgVEsgsrIdi5f+zOs+l8Bwahj7UuzJz5E//mDgDVcPX9lt8iB/Y7euj+HRdbWMZqNtiakfvAc8F2PSmTHYV1Do7aE+UobONf2C6jYE6gL33b/gUV+KgWqdX+AT6oLoOnARWhnhwcDKM669JhPRSCPxf/XXhHZS01PHPuqZ7SW5of9E3CNZj8mVbJLrtl+loIr4oojTM4Sxn7CFbuB9enfUKhakpNj8h93VUuyGvsl130UVilw323R94MTCCg9YYygp7hI1RQaOpB6vQEDCA6H1PrkOp03v97kgx/HnpL6V1CbqWgCv7hevrttsSy0Ilq7EgtQ2Wump33QW6kq9B1YpiMvvW8YhJ+PqdO0Ds9+yYoj/Ojam56kHqYHmgRwMaZFzVtvOuAaNtzBlR/B5bSAGqPYGAFbi9QPuVq+t+NfJJqcJpEJ+4QuLrlDWjmkeqBvQ6HZ4eGydcdLAO6DKqpPhI4SVZOp6OeARYHhAsekKZjwo8OiJ+Z16kbRAOLNV3nm5NElO5RhyvJLTHea3MiJ/iRq6v+3VFjPy7VPflxBxRmj2mNqK/7pf3IHltnh4fJ7P4IZ3zZoUg6Hdxp+pQ6oTFfqEOieKKBwzDh17AKLwyH/mR/meLJ/L7QN9jhDIvdB9CtTr0qMNneIiKEPhmH7lEfku9vnOoa+WIw3C9amP4oR+/AbzA1Y7Cxanlz++08CCI9DRbcWPgnIqGeWSnvBpR0L8JiYh/qPH+bTf6RREa8FNoFcAqKtoJ/B5bQFewZhO7141IaD3u9DJLFOZu+o1VUHP26np3Uu3k8vwDLSdCzRzFr7ZciiHgAQsPwBuyobfEW2LyVFokxjCCIee8BkQMV02v8RXNSPenk3zOYxwhpBKm/LFkw9TB6eDySY+03MsjIeu3I6qmTXYM+gwfYuWbLlNLULoHZtUYdGVtD8pzc+vfLefOnXvPem5zN5M6V3v9YzsChNp2Ibpemz7p0l4dpfqOeUJFSPnQ0wvTXo4ViR5FxLgYqnBlxpMTuGSnc02SK4OqBST3AZIwfTnvTYeUL3fk3P05dzlsf8DOa+no45a6QzXe4ZFKr6APjOv8o12y4v6p5BWlHvj6m0idMrb/Jma6S5Zu5Nvyc9vft5ZmDpEk3E0em+7N5rIX1eIdFWzGyVq6bgKfdAqmSCF1wpapUJXHgEh8NXXoILoBa/C1JN6+CyqBOUPI7t9AatxKzxLrn0vkp1bPdPODQeDCxdook4QqVZa2/D7GchZDjcL5mwQH7zFfQh5p0KKjc2YPOCy0vF+Koo5+KzXtkNfwz7HHBpAp/r2wALJdSNGyPUNE6DEuGzsuTJcY49JgTKzpYsvq18PivWHVjdpOlrfwvd9RmuM/787Jes4HIB6QUaW6Tgw4+Czb1IqTyZ+NdeFtn3btKR0pMZ85GhxsFvg5V39wyqHgWB6na5ZutMZSnoaIV9XKrQn2+yP/egfV2NNNJ0xjXDN0ql2S8Z0vR8BgLHK0pgxV0cOY/xnDAflU8tGIu+9aYzWRgCt0UJOF9znMH1bVjOZzt6L8vZM4h9zD2Drt3+LxIf97WUz9d8LWSbBYtcAZBrPm/7+noP8jsUS5XzoEc16+4mqR6+RSrqzkjtl8xCc/lnRUw+7zHTTEgde8Bz0LCIzLys/5oPFjPwkxCQDnn+2bPgYO1fwSTjOrh6G4Ya5pn+jDE0grpS5lXb9FUJP7VTIls+5Kw1gnL5wrDXvJz8xpj1oYFUzrt83dGSCMAZf81Y8FQHO+Pv7bkp0CCj95j3mZdujhl7yZSCCyDSYZCXcBzACwnAKLYGxqLxts2QPxwvd39zo8w/+ddQGDxHqR73Rwa3rMX59kc3RjBD44cw690JvusCHRqppVrKobGAfeaIEy5feyImfNg2pOooSNO7lmgK+GAt2lArnihxYkyAYeijAUYl9pjpaF4lt00/TX70lVfVWJSqwgvH/AE5T8eQ2AEzMw7n7jpdOqJxJWPIlC/XtPQCM92XMQ/ccbt7BtU1/QwiidWai+Kc96H6c0C+xh1W7fWQvzTBNVHxQGX6vhu4lGphjxnsLCrJKDZvmp4yw1KLGVBW9RsBd0oLxv4FIJyAOr+NZRXkx7ZsGviie/tnxvQ8uablcl9PecBv2VDj0D2DBl0i127bLpEnjtU26VIQBNLvkxCQ+v1XQJfqGHxNUEjHGthBHZ7eSQU4d7qTbChcLtLbMPaYSdgnyQ/Py7zHjHGntGDscxhaMHS3/re6asy0XYvBi/eFsQnpwdsscy0930DymHsdvha7fWEhu7J2HDZAfxYAm+bsSYih0fjDMHXoT2yeZervPc+WxmeZ6+bYe6+pS7Y0Nw+1PKfiq+dp1wzRFFC8OA5+Co72MfS1H1gtt5w7Xn54/otZ95gx7pTmjH5T4vvGw2nHFvVbZcDFupsO8B6bjjPXTOxtqzfNHDP2BpPONHOcLY+5tysvvdeg3dgzqKr+Llmy/SbNYvYMMvlzibvK7A4M3ptep2xpJq8pL/3ZmdLT09xzzgpH61INfSyYTMWMsYCDWV4Iin5JqDVfAFDN1PbopAJrlNmCDos2BI+TW2Th2FMByg3ujhrZ78tWZlGvweWTs2cQttNrugx81+MQS3xAh0blu3IbGv04ftIqJTOqC93BxtF+pTrafw6mYqNl1bl36CyJ03AytbkE406JM6xF4z8Jge6vAK6wTgDcvWNzKaaEefhxO0NjVd2pON4li7dOUXDx3ZC37CUwmwnmdZrYpDP2ppljE5t86efp96XnY34TzDFjSh//Bv/kvAYd4aIFGFzgj3vMdGCPmSHbR8stU59z/E4BUI4fiNwr43WntHDcl8G/rJJqgIu+rfwtpfe2EbNG1zKouv5eiWxbqv1Au4JeZo0GDObFegvNdC1bPnNvT/eZdJOPsTfNlM09oddgys7ruVEI5hxIsDj0VYKfAwFvb54Jby4XYg3NWeoYiMtFvgCqqZDaLRp/OcB1JZakKOci3YLDD1Ta7/9JWgbBRq6TlkGD5sjiJx+V2X/2n2VQDu8/IC17V2L42A1xA/z64KUXKujQR5eL2F4tFn0RBHI8+KnVOvSpNkWOQ1+2+tGdEgP5s0Xjr8WOW/8GgS9oozqOKCZFzlbL7NccXjfoWAY1fEzqq3fJVds+4YhZImARXF9k3lL89MGwXqhPQH56UQcsts6CuOE93ahR5UF5rimXXshhco+Z9pY1MrTzJLn5vB3gHypSi7PejhrIsUqzAVLKha4afytEF1NVpyug6hLFocoDqT/vdWR7mDU2O5ZBVdUb5KqtEYey4+Pxqj87n9JAn5i/+wkdBGdJZ9WUFyCQxA6rHS/hCyfDxSWd/AwdOuurgHYEFNw7sMfMLZ+frmrOpCrc17lQgcMqeZNF434LmdhkDDHc8zAEgB8iM0b2P3eOhWVQtD0J58FXy5VPbpBZ25oo83r++TfyqxyQ5/fgLOlwEfrm818Vq3U8KNc2LEI74BrYwwBNDH0sK9b5GmZ9p4FKrdJZTr6Gvt7qZ6T0V47ZBHP5SRCr7JMwpfQ+B5f71euXzaGRE522/di0oP4TUpvcJXM3f/z1mUdAhxvB5jDvv+BUiovQpCA3f+WADN1xCuRJD6oEnIx2/4KjdVCFPWY6WtZJdcuJcvO5m3XoUyZbFQn7V3Jf7zJS+oVjnsIEcTx2Z31FKqAtQUGqUmXi32f/rJjWCY1lHeFgCb/caTYGqnsY+OFHAnO3LGJXBMIWWBkeaU498MOPUyVTE4LLyI++dc+vAa4vYvji0EGpfPe85p70mGAM0CoCIR6dJ/953lI99patCUX+4bBICjbvsUFwB/kIpPRj8KL44fhzSCGgGBh3Axn41aQdsGqHWPZbr99+zLgT3gsNGvrtROuBBF5QrzIvLbPQP6jzwWDxOvr41t2rAK5LIWvijIp5s5FdKi9j6KvlUsVbYECnQpTwmA59o6fZUHArPZtJptfwXv8IboT68Bk6xPgRXClg4cALLvZiEjIVKDraiXBo2PB6qR9xmG1z59kMr7PQGMpYPup7MFCcocqRB91y7mUA1VVYLOVyDypuPCBrS1EmYwam47OiFL2zZSPoG4Y+BVWFOg7xA6hYTYKKlJOU66oJZ0rr/rtTS0CHgpReuxs/jmVQCPywI0JxztlC34SDKZapGgWN02H+RaB963ffxHreDzGzwteC3bNtBSTvZZOgkIehjzxkLBqRH5y7WIswWqmmPD/FNIfnFsUMS7bDYKRxBgSqFEVwKOm5T5i/WKELROxl/W7R06RW+g+KJXZnTIYdNVzqh9ONBprjVUEqVj0zPQd1660TIZB7GIaXkAv9+2+mADy3Y2gcgoVTp6E0dCCgOg5g1pe8WH4wbQNkLQHhHjMEpJ8DBY0RvjK8osg27HvduACCYjNcH0zJi90W1Ayd7DyVkbGtzAiswwAsfBeHELCchhnqM+OuRqkIXoD2noEL8LxvvY3GPCTh2B2yYnq7sx3ITH75bo84t/v210uVl2ybBf5wBfhDNAvrpqXeUUN70O1Gwt0w8OnAOhIUa8ShCiwio7dZXW/X/YsuUOVNDlVevA1uBCp+4Qz5HPd1NlyamucCrA53KDykgcXu5Rc+c3VI9g5KymhspE3m//UR2FptBumw+3mV5j0M+KlGHBHZ+ikM7/dj32sMP7EYKBcFqsUP2YCFncu681igWLQcTx8KWUZPzE62awNtLcru6bEDLfrQvJ+m8CpQ3X4q3tzD0PqogXYt5XjFB1cmYPXIvPtvKCw9k+onCBopfWT8ZkjksLzV+aYadqSk9KgsX3gx/k2/pD/LpPs8LgMr/QUZXfprJ2Bh3sK+123P6/JWIVWK0utgzg2ozPkhNL6UgWVemjemAJU8VwT7XjcMHQ8h8eNqqFEIlaKeyB9ngb0FD/ByyN1baXm9fgh9A3ltd26FRWysnVqcHWJW/OR9kNJ/GoLU4vBcBilGzGBil8/iAhkFpEMpIPXhrLBMsbJBzBhqKLAmcjN17HsNXXpHjdu8+mwl9P9apk9eKRR+0p9szvU6HlnK2G1xGVi9vXqvoUZk4oUA140w8KW2B2hGARfWDThYP++x1tckmNjNo9dK/ONWKdN3UeKa+fTxRtecC+pXbZkPcH0PqsPU5GdX5v8DZakMZgg0cZrkXYfC4Yf7bkkn/x3idMf775eAigBEXGFYcgo3U79EQtXQ7cSCKXX6CxIMunoo3KUOPVwtaXIZWH3qfi5Yu4YakQk/go3A51XafUi4U+pTQweWGSpIZWD1pwtVrwtS+iUT1ko8/nFVJaKtZKENNQyFMnF/6l7Ye2wrCIc62GukDKz+drSR0l8z6TG4s5yIPXbew9BIXXqKI7oYbgOCvsbeepl7vWneY/JfDMqH4bhEMRj2hAXjZxCsp8vAcl5J/34NuK4av1OsTpjPte+GQa5jqNG/ErvuUjDhVbmY6bqQ6cgFU6ZLRUyzEdRnbFIeKANroB1vloAiH31VAlEsAbVsd6T0/bZw8tQoJ1Qhvy8m99ysPZxs2d8mVujnZWB5XmO/D7kEREONyEcPyMkTTsEuFX+ERir2boRdZSGDUrVCPqAPZYMFCNbX84Yluy+qe7MMrD70XdasxlCDAtVrJv4vWP+sgadkDIsElzI9uN0gIdfYPNGlXOnFmMsljdFeLNAHmxorE/v33bv7oqalaK4/rWhL2k8DeThFEcaf1ZKJ0+HY4wegXOS5YAuI31zx5OJI8cib9L5UImpojgdS2QHfS0DFrFA4GKxrrEjs3/9fuy8e9DktdbFaQgz4AeUCvD2g5nOuO6VrJv0fDItL4E4JogjVsOUScm7BYMfEB93FC+b/oIuFTEiAcMYx+wsGahvDyVjnq4nmfReDUn1RH6pGKlayPBQW4hUYO0qamV0z8WpYMV0KhUH0tY4Q+ZfSG/AZnBUmTuDTiFsVNcFgbWPIjna8YLfun1H38uvHgVLdnvJN5rbdF9OJQrxbn5RJf1Yw1IDqzZVPfhEbiP5aN1BLUkG9F0MNBQt+UiBxj71rhdCaHno07QoLt1YIgGA7M9BbGC1zM5Bk+4GnwUIt3XNxw69cKixnPmyHNk121Yvcji9TrMIiEEtA6HAqDV4z8U5I6T+hzmyD3GW9Hx5vCDIGAzZzrIn5/eFwhxITVlVdKFBVF7Q7W59IdLSev/viprF7vt74S4JqEtsFlKWDijUpU6z8vo+eS0sZamyBlN7aBEONejXUsGCoYQDjvVvT8GNAZGaEXoqVUvTLH8VS/snCnmvVDQFa/tjR1sdsO3AdwHS/Wz0LgAptnTmJZkGZaq7ZysDyvsxCHxtwLdoxCiPMn2AgewQc2mb2eJMJWAQV0xHzlXZpkA4YWCiNQ5kdCtQ0WuCf4NmscwOmsd/f8/VBD2u3gDKduUmCmahTpm4rAytTrxQyLWW/uGUoKNejMNQ4CTr13cFF8GggenDgpVY89wLr6BHgsQ7DOjidTff5dbI0DnlhAEqSHdjNNh5fiw3Llu75+pC/IB0q2aqoAHcE3XkovZblp881yVJW+VKuPWDcKV36QqU0qJT+dPiN6AIXX7cGHPA4G7COGiF1AFaffDfQsw55KMsCoBok2bqfT7sTz7l+zzcGbdNHc+uVZzZh8jGZwOtzKAOrz12Wpxu8LgkWbr0XUvopEKimGWp4gEUJmAGZl2L1DVgsAYAKhAO19ZJs3o9j+Zkkg8v3XFL/rLaM9frwmbZulj6AppaBNYDOG/CtpApcAmJYuPUnMNS4SNr2gmO2HP/0TDdgMjEBRmDxkjLvOVEs3pWA92hQqDpJtuxrR7k/hl/AFS9f3PQirolAZCBvI9XURxP7/1MGVv/7Lj93upJqLWzh1qWQ0s/pcqdECT6umKGQxwAV05R5j9L8C8A6vMehUAFlBcNhq7oGgNp/wLKTt8bDVatevbDmNT6TIoOtg15MpvyFMTEPoQysPHTigIugTzG5Gowy9OoXPPEdMPTXwykwAUVqxi2KFUwKMgUWKRYYpWhchn/wGGCxAU5BkLWLeQegrATW8cJWVTUB9Q6YqlsqrI4f/P3iEW+zvpNus8Nb3wAVM6sEA25E9wLKwOreHyU8gycf405pwdavwZ3S7ZIAP5+EOyUbUvoUuFxQxeLY76FSjjj+WC+giK6kFa4IWxVVYMr3vQ5ErgzG965+ceZxyqErhXpjUsEAZTqwDCzTE36JzYxx/tZzYFS2DkpzdMHZiRcF7866L5DjFhJE7IhRx8CNfi135COg7EBFdQig4izvJSwdLU80t/7k1SuOamfTCk2h0ruvDKz0HvHDuRGkLtx2OpistXZlw1C7+T2w3xBWwV9yuKpKhh0xApuIYJ+gZDJgVdUGaMQAQD0HfN2w59Wmnxu5kwOoxaBQEfJbRQtlYBWtq/v4IJdyjbx9b1Pzu3uvxXB4QbCmqT4MnqmiBgYL4Mq4aZ7diQleLPokhKM37P5G453mKTrkzcCyi6OuY5KLFpeBVbSu7seDPLKuCffZw5rfjU6OJ2LjIQyFm2S7E2B7MRBI/OmlbwzdbEpXQPWyjmfyFjL+/4JPu45FLkyEAAAAAElFTkSuQmCC'); + background-size: 24px; + background-repeat: no-repeat; + background-position: left center; + padding-left: 36px; + font-size: 20px; + letter-spacing: -0.04rem; + font-weight: 400; + color: white; + text-decoration: none; +} + +.message-container { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + margin: 0 30px; +} + +.message { + font-weight: 300; + font-size: 1.4rem; +} + +body.error .message { + display: none; +} + +body.error .error-message { + display: block; +} + +.error-message { + display: none; + font-weight: 300; + font-size: 1.3rem; +} + +.error-text { + color: red; + font-size: 1rem; +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); + font-weight: 200 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); + font-weight: 300 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); + font-weight: 400 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); + font-weight: 600 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); + font-weight: 700 +} diff --git a/extensions/vscode-account/media/auth.html b/extensions/microsoft-authentication/media/auth.html similarity index 100% rename from extensions/vscode-account/media/auth.html rename to extensions/microsoft-authentication/media/auth.html diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json new file mode 100644 index 00000000000..64becb50468 --- /dev/null +++ b/extensions/microsoft-authentication/package.json @@ -0,0 +1,59 @@ +{ + "name": "microsoft-authentication", + "publisher": "vscode", + "displayName": "%displayName%", + "description": "%description%", + "version": "0.0.1", + "engines": { + "vscode": "^1.42.0" + }, + "categories": [ + "Other" + ], + "enableProposedApi": true, + "activationEvents": [ + "onAuthenticationRequest:microsoft" + ], + "extensionKind": [ + "ui", + "workspace", + "web" + ], + "contributes": { + "authentication": [ + { + "label": "Microsoft", + "id": "microsoft" + } + ] + }, + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "main": "./out/extension.js", + "browser": "./dist/browser/extension.js", + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "gulp compile-extension:microsoft-authentication", + "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", + "watch": "gulp watch-extension:microsoft-authentication", + "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" + }, + "devDependencies": { + "@types/keytar": "^4.0.1", + "@types/node": "^10.12.21", + "@types/node-fetch": "^2.5.7", + "@types/randombytes": "^2.0.0", + "@types/sha.js": "^2.4.0", + "@types/uuid": "^8.0.0", + "typescript": "^3.7.4" + }, + "dependencies": { + "buffer": "^5.6.0", + "node-fetch": "^2.6.0", + "randombytes": "github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971", + "sha.js": "2.4.11", + "stream": "0.0.2", + "uuid": "^8.2.0", + "vscode-extension-telemetry": "0.1.1", + "vscode-nls": "^4.1.1" + } +} diff --git a/extensions/microsoft-authentication/package.nls.json b/extensions/microsoft-authentication/package.nls.json new file mode 100644 index 00000000000..c0bb4c4a6a0 --- /dev/null +++ b/extensions/microsoft-authentication/package.nls.json @@ -0,0 +1,6 @@ +{ + "displayName": "Microsoft Account", + "description": "Microsoft authentication provider", + "signIn": "Sign In", + "signOut": "Sign Out" +} diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts new file mode 100644 index 00000000000..8220adc6cb4 --- /dev/null +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -0,0 +1,671 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as randomBytes from 'randombytes'; +import * as querystring from 'querystring'; +import { Buffer } from 'buffer'; +import * as vscode from 'vscode'; +import { createServer, startServer } from './authServer'; + +import { v4 as uuid } from 'uuid'; +import { keychain } from './keychain'; +import Logger from './logger'; +import { toBase64UrlEncoding } from './utils'; +import fetch, { Response } from 'node-fetch'; +import { sha256 } from './env/node/sha256'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +const redirectUrl = 'https://vscode-redirect.azurewebsites.net/'; +const loginEndpointUrl = 'https://login.microsoftonline.com/'; +const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; +const tenant = 'organizations'; + +interface IToken { + accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined + + expiresIn?: number; // How long access token is valid, in seconds + expiresAt?: number; // UNIX epoch time at which token will expire + refreshToken: string; + + account: { + label: string; + id: string; + }; + scope: string; + sessionId: string; // The account id + the scope +} + +interface ITokenClaims { + tid: string; + email?: string; + unique_name?: string; + preferred_username?: string; + oid?: string; + altsecid?: string; + ipd?: string; + scp: string; +} + +interface IStoredSession { + id: string; + refreshToken: string; + scope: string; // Scopes are alphabetized and joined with a space + account: { + label?: string; + displayName?: string, + id: string + } +} + +export interface ITokenResponse { + access_token: string; + expires_in: number; + ext_expires_in: number; + refresh_token: string; + scope: string; + token_type: string; + id_token?: string; +} + +function parseQuery(uri: vscode.Uri) { + return uri.query.split('&').reduce((prev: any, current) => { + const queryString = current.split('='); + prev[queryString[0]] = queryString[1]; + return prev; + }, {}); +} + +export const onDidChangeSessions = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>(); + +export const REFRESH_NETWORK_FAILURE = 'Network failure'; + +class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler { + public handleUri(uri: vscode.Uri) { + this.fire(uri); + } +} + +export class AzureActiveDirectoryService { + private _tokens: IToken[] = []; + private _refreshTimeouts: Map<string, NodeJS.Timeout> = new Map<string, NodeJS.Timeout>(); + private _uriHandler: UriEventHandler; + private _disposables: vscode.Disposable[] = []; + + // Used to keep track of current requests when not using the local server approach. + private _pendingStates = new Map<string, string[]>(); + private _codeExchangePromises = new Map<string, Promise<vscode.AuthenticationSession>>(); + private _codeVerfifiers = new Map<string, string>(); + + constructor() { + this._uriHandler = new UriEventHandler(); + this._disposables.push(vscode.window.registerUriHandler(this._uriHandler)); + } + + public async initialize(): Promise<void> { + const storedData = await keychain.getToken() || await keychain.tryMigrate(); + if (storedData) { + try { + const sessions = this.parseStoredData(storedData); + const refreshes = sessions.map(async session => { + if (!session.refreshToken) { + return Promise.resolve(); + } + + try { + await this.refreshToken(session.refreshToken, session.scope, session.id); + } catch (e) { + if (e.message === REFRESH_NETWORK_FAILURE) { + const didSucceedOnRetry = await this.handleRefreshNetworkError(session.id, session.refreshToken, session.scope); + if (!didSucceedOnRetry) { + this._tokens.push({ + accessToken: undefined, + refreshToken: session.refreshToken, + account: { + label: session.account.label ?? session.account.displayName!, + id: session.account.id + }, + scope: session.scope, + sessionId: session.id + }); + this.pollForReconnect(session.id, session.refreshToken, session.scope); + } + } else { + await this.logout(session.id); + } + } + }); + + await Promise.all(refreshes); + } catch (e) { + Logger.info('Failed to initialize stored data'); + await this.clearSessions(); + } + } + + this._disposables.push(vscode.authentication.onDidChangePassword(() => this.checkForUpdates)); + } + + private parseStoredData(data: string): IStoredSession[] { + return JSON.parse(data); + } + + private async storeTokenData(): Promise<void> { + const serializedData: IStoredSession[] = this._tokens.map(token => { + return { + id: token.sessionId, + refreshToken: token.refreshToken, + scope: token.scope, + account: token.account + }; + }); + + await keychain.setToken(JSON.stringify(serializedData)); + } + + private async checkForUpdates(): Promise<void> { + const addedIds: string[] = []; + let removedIds: string[] = []; + const storedData = await keychain.getToken(); + if (storedData) { + try { + const sessions = this.parseStoredData(storedData); + let promises = sessions.map(async session => { + const matchesExisting = this._tokens.some(token => token.scope === session.scope && token.sessionId === session.id); + if (!matchesExisting && session.refreshToken) { + try { + await this.refreshToken(session.refreshToken, session.scope, session.id); + addedIds.push(session.id); + } catch (e) { + if (e.message === REFRESH_NETWORK_FAILURE) { + // Ignore, will automatically retry on next poll. + } else { + await this.logout(session.id); + } + } + } + }); + + promises = promises.concat(this._tokens.map(async token => { + const matchesExisting = sessions.some(session => token.scope === session.scope && token.sessionId === session.id); + if (!matchesExisting) { + await this.logout(token.sessionId); + removedIds.push(token.sessionId); + } + })); + + await Promise.all(promises); + } catch (e) { + Logger.error(e.message); + // if data is improperly formatted, remove all of it and send change event + removedIds = this._tokens.map(token => token.sessionId); + this.clearSessions(); + } + } else { + if (this._tokens.length) { + // Log out all, remove all local data + removedIds = this._tokens.map(token => token.sessionId); + Logger.info('No stored keychain data, clearing local data'); + + this._tokens = []; + + this._refreshTimeouts.forEach(timeout => { + clearTimeout(timeout); + }); + + this._refreshTimeouts.clear(); + } + } + + if (addedIds.length || removedIds.length) { + onDidChangeSessions.fire({ added: addedIds, removed: removedIds, changed: [] }); + } + } + + private async convertToSession(token: IToken): Promise<vscode.AuthenticationSession> { + const resolvedToken = await this.resolveAccessToken(token); + return { + id: token.sessionId, + accessToken: resolvedToken, + account: token.account, + scopes: token.scope.split(' ') + }; + } + + private async resolveAccessToken(token: IToken): Promise<string> { + if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) { + token.expiresAt + ? Logger.info(`Token available from cache, expires in ${token.expiresAt - Date.now()} milliseconds`) + : Logger.info('Token available from cache'); + return Promise.resolve(token.accessToken); + } + + try { + Logger.info('Token expired or unavailable, trying refresh'); + const refreshedToken = await this.refreshToken(token.refreshToken, token.scope, token.sessionId); + if (refreshedToken.accessToken) { + return refreshedToken.accessToken; + } else { + throw new Error(); + } + } catch (e) { + throw new Error('Unavailable due to network problems'); + } + } + + private getTokenClaims(accessToken: string): ITokenClaims { + try { + return JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString()); + } catch (e) { + Logger.error(e.message); + throw new Error('Unable to read token claims'); + } + } + + get sessions(): Promise<vscode.AuthenticationSession[]> { + return Promise.all(this._tokens.map(token => this.convertToSession(token))); + } + + public async login(scope: string): Promise<vscode.AuthenticationSession> { + Logger.info('Logging in...'); + if (!scope.includes('offline_access')) { + Logger.info('Warning: The \'offline_access\' scope was not included, so the generated token will not be able to be refreshed.'); + } + + return new Promise(async (resolve, reject) => { + if (vscode.env.uiKind === vscode.UIKind.Web) { + resolve(this.loginWithoutLocalServer(scope)); + return; + } + + const nonce = randomBytes(16).toString('base64'); + const { server, redirectPromise, codePromise } = createServer(nonce); + + let token: IToken | undefined; + try { + const port = await startServer(server); + vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`)); + + const redirectReq = await redirectPromise; + if ('err' in redirectReq) { + const { err, res } = redirectReq; + res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); + res.end(); + throw err; + } + + const host = redirectReq.req.headers.host || ''; + const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1]; + const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; + + const state = `${updatedPort},${encodeURIComponent(nonce)}`; + + const codeVerifier = toBase64UrlEncoding(randomBytes(32).toString('base64')); + const codeChallenge = toBase64UrlEncoding(await sha256(codeVerifier)); + const loginUrl = `${loginEndpointUrl}${tenant}/oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${state}&scope=${encodeURIComponent(scope)}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}`; + + await redirectReq.res.writeHead(302, { Location: loginUrl }); + redirectReq.res.end(); + + const codeRes = await codePromise; + const res = codeRes.res; + + try { + if ('err' in codeRes) { + throw codeRes.err; + } + token = await this.exchangeCodeForToken(codeRes.code, codeVerifier, scope); + this.setToken(token, scope); + Logger.info('Login successful'); + res.writeHead(302, { Location: '/' }); + const session = await this.convertToSession(token); + resolve(session); + res.end(); + } catch (err) { + res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); + res.end(); + reject(err.message); + } + } catch (e) { + Logger.error(e.message); + + // If the error was about starting the server, try directly hitting the login endpoint instead + if (e.message === 'Error listening to server' || e.message === 'Closed' || e.message === 'Timeout waiting for port') { + await this.loginWithoutLocalServer(scope); + } + + reject(e.message); + } finally { + setTimeout(() => { + server.close(); + }, 5000); + } + }); + } + + public dispose(): void { + this._disposables.forEach(disposable => disposable.dispose()); + this._disposables = []; + } + + private getCallbackEnvironment(callbackUri: vscode.Uri): string { + if (callbackUri.authority.endsWith('.workspaces.github.com') || callbackUri.authority.endsWith('.github.dev')) { + return `${callbackUri.authority},`; + } + + switch (callbackUri.authority) { + case 'online.visualstudio.com': + return 'vso,'; + case 'online-ppe.core.vsengsaas.visualstudio.com': + return 'vsoppe,'; + case 'online.dev.core.vsengsaas.visualstudio.com': + return 'vsodev,'; + default: + return `${callbackUri.scheme},`; + } + } + + private async loginWithoutLocalServer(scope: string): Promise<vscode.AuthenticationSession> { + const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.microsoft-authentication`)); + const nonce = randomBytes(16).toString('base64'); + const port = (callbackUri.authority.match(/:([0-9]*)$/) || [])[1] || (callbackUri.scheme === 'https' ? 443 : 80); + const callbackEnvironment = this.getCallbackEnvironment(callbackUri); + const state = `${callbackEnvironment}${port},${encodeURIComponent(nonce)},${encodeURIComponent(callbackUri.query)}`; + const signInUrl = `${loginEndpointUrl}${tenant}/oauth2/v2.0/authorize`; + let uri = vscode.Uri.parse(signInUrl); + const codeVerifier = toBase64UrlEncoding(randomBytes(32).toString('base64')); + const codeChallenge = toBase64UrlEncoding(await sha256(codeVerifier)); + uri = uri.with({ + query: `response_type=code&client_id=${encodeURIComponent(clientId)}&response_mode=query&redirect_uri=${redirectUrl}&state=${state}&scope=${scope}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}` + }); + vscode.env.openExternal(uri); + + const timeoutPromise = new Promise((_: (value: vscode.AuthenticationSession) => void, reject) => { + const wait = setTimeout(() => { + clearTimeout(wait); + reject('Login timed out.'); + }, 1000 * 60 * 5); + }); + + const existingStates = this._pendingStates.get(scope) || []; + this._pendingStates.set(scope, [...existingStates, state]); + + // Register a single listener for the URI callback, in case the user starts the login process multiple times + // before completing it. + let existingPromise = this._codeExchangePromises.get(scope); + if (!existingPromise) { + existingPromise = this.handleCodeResponse(scope); + this._codeExchangePromises.set(scope, existingPromise); + } + + this._codeVerfifiers.set(state, codeVerifier); + + return Promise.race([existingPromise, timeoutPromise]) + .finally(() => { + this._pendingStates.delete(scope); + this._codeExchangePromises.delete(scope); + this._codeVerfifiers.delete(state); + }); + } + + private async handleCodeResponse(scope: string): Promise<vscode.AuthenticationSession> { + let uriEventListener: vscode.Disposable; + return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => { + uriEventListener = this._uriHandler.event(async (uri: vscode.Uri) => { + try { + const query = parseQuery(uri); + const code = query.code; + + const acceptedStates = this._pendingStates.get(scope) || []; + // Workaround double encoding issues of state in web + if (!acceptedStates.includes(query.state) && !acceptedStates.includes(decodeURIComponent(query.state))) { + throw new Error('State does not match.'); + } + + const verifier = this._codeVerfifiers.get(query.state) ?? this._codeVerfifiers.get(decodeURIComponent(query.state)); + if (!verifier) { + throw new Error('No available code verifier'); + } + + const token = await this.exchangeCodeForToken(code, verifier, scope); + this.setToken(token, scope); + + const session = await this.convertToSession(token); + resolve(session); + } catch (err) { + reject(err); + } + }); + }).then(result => { + uriEventListener.dispose(); + return result; + }).catch(err => { + uriEventListener.dispose(); + throw err; + }); + } + + private async setToken(token: IToken, scope: string): Promise<void> { + const existingTokenIndex = this._tokens.findIndex(t => t.sessionId === token.sessionId); + if (existingTokenIndex > -1) { + this._tokens.splice(existingTokenIndex, 1, token); + } else { + this._tokens.push(token); + } + + this.clearSessionTimeout(token.sessionId); + + if (token.expiresIn) { + this._refreshTimeouts.set(token.sessionId, setTimeout(async () => { + try { + await this.refreshToken(token.refreshToken, scope, token.sessionId); + onDidChangeSessions.fire({ added: [], removed: [], changed: [token.sessionId] }); + } catch (e) { + if (e.message === REFRESH_NETWORK_FAILURE) { + const didSucceedOnRetry = await this.handleRefreshNetworkError(token.sessionId, token.refreshToken, scope); + if (!didSucceedOnRetry) { + this.pollForReconnect(token.sessionId, token.refreshToken, token.scope); + } + } else { + await this.logout(token.sessionId); + onDidChangeSessions.fire({ added: [], removed: [token.sessionId], changed: [] }); + } + } + }, 1000 * (token.expiresIn - 30))); + } + + this.storeTokenData(); + } + + private getTokenFromResponse(json: ITokenResponse, scope: string, existingId?: string): IToken { + let claims = undefined; + + try { + claims = this.getTokenClaims(json.access_token); + } catch (e) { + if (json.id_token) { + Logger.info('Failed to fetch token claims from access_token. Attempting to parse id_token instead'); + claims = this.getTokenClaims(json.id_token); + } else { + throw e; + } + } + + return { + expiresIn: json.expires_in, + expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined, + accessToken: json.access_token, + refreshToken: json.refresh_token, + scope, + sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`, + account: { + label: claims.email || claims.unique_name || claims.preferred_username || 'user@example.com', + id: `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}` + } + }; + } + + private async exchangeCodeForToken(code: string, codeVerifier: string, scope: string): Promise<IToken> { + Logger.info('Exchanging login code for token'); + try { + const postData = querystring.stringify({ + grant_type: 'authorization_code', + code: code, + client_id: clientId, + scope: scope, + code_verifier: codeVerifier, + redirect_uri: redirectUrl + }); + + const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); + const endpoint = proxyEndpoints && proxyEndpoints['microsoft'] || `${loginEndpointUrl}${tenant}/oauth2/v2.0/token`; + + const result = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length.toString() + }, + body: postData + }); + + if (result.ok) { + Logger.info('Exchanging login code for token success'); + const json = await result.json(); + return this.getTokenFromResponse(json, scope); + } else { + Logger.error('Exchanging login code for token failed'); + throw new Error('Unable to login.'); + } + } catch (e) { + Logger.error(e.message); + throw e; + } + } + + private async refreshToken(refreshToken: string, scope: string, sessionId: string): Promise<IToken> { + Logger.info('Refreshing token...'); + const postData = querystring.stringify({ + refresh_token: refreshToken, + client_id: clientId, + grant_type: 'refresh_token', + scope: scope + }); + + let result: Response; + try { + result = await fetch(`https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length.toString() + }, + body: postData + }); + } catch (e) { + Logger.error('Refreshing token failed'); + throw new Error(REFRESH_NETWORK_FAILURE); + } + + try { + if (result.ok) { + const json = await result.json(); + const token = this.getTokenFromResponse(json, scope, sessionId); + this.setToken(token, scope); + Logger.info('Token refresh success'); + return token; + } else { + throw new Error('Bad request.'); + } + } catch (e) { + vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); + Logger.error(`Refreshing token failed: ${result.statusText}`); + throw new Error('Refreshing token failed'); + } + } + + private clearSessionTimeout(sessionId: string): void { + const timeout = this._refreshTimeouts.get(sessionId); + if (timeout) { + clearTimeout(timeout); + this._refreshTimeouts.delete(sessionId); + } + } + + private removeInMemorySessionData(sessionId: string) { + const tokenIndex = this._tokens.findIndex(token => token.sessionId === sessionId); + if (tokenIndex > -1) { + this._tokens.splice(tokenIndex, 1); + } + + this.clearSessionTimeout(sessionId); + } + + private pollForReconnect(sessionId: string, refreshToken: string, scope: string): void { + this.clearSessionTimeout(sessionId); + + this._refreshTimeouts.set(sessionId, setTimeout(async () => { + try { + await this.refreshToken(refreshToken, scope, sessionId); + } catch (e) { + this.pollForReconnect(sessionId, refreshToken, scope); + } + }, 1000 * 60 * 30)); + } + + private handleRefreshNetworkError(sessionId: string, refreshToken: string, scope: string, attempts: number = 1): Promise<boolean> { + return new Promise((resolve, _) => { + if (attempts === 3) { + Logger.error('Token refresh failed after 3 attempts'); + return resolve(false); + } + + if (attempts === 1) { + const token = this._tokens.find(token => token.sessionId === sessionId); + if (token) { + token.accessToken = undefined; + onDidChangeSessions.fire({ added: [], removed: [], changed: [token.sessionId] }); + } + } + + const delayBeforeRetry = 5 * attempts * attempts; + + this.clearSessionTimeout(sessionId); + + this._refreshTimeouts.set(sessionId, setTimeout(async () => { + try { + await this.refreshToken(refreshToken, scope, sessionId); + return resolve(true); + } catch (e) { + return resolve(await this.handleRefreshNetworkError(sessionId, refreshToken, scope, attempts + 1)); + } + }, 1000 * delayBeforeRetry)); + }); + } + + public async logout(sessionId: string) { + Logger.info(`Logging out of session '${sessionId}'`); + this.removeInMemorySessionData(sessionId); + + if (this._tokens.length === 0) { + await keychain.deleteToken(); + } else { + this.storeTokenData(); + } + } + + public async clearSessions() { + Logger.info('Logging out of all sessions'); + this._tokens = []; + await keychain.deleteToken(); + + this._refreshTimeouts.forEach(timeout => { + clearTimeout(timeout); + }); + + this._refreshTimeouts.clear(); + } +} diff --git a/extensions/vscode-account/src/authServer.ts b/extensions/microsoft-authentication/src/authServer.ts similarity index 78% rename from extensions/vscode-account/src/authServer.ts rename to extensions/microsoft-authentication/src/authServer.ts index b78803cec16..caae72ba52c 100644 --- a/extensions/vscode-account/src/authServer.ts +++ b/extensions/microsoft-authentication/src/authServer.ts @@ -6,7 +6,6 @@ import * as http from 'http'; import * as url from 'url'; import * as fs from 'fs'; -import * as net from 'net'; import * as path from 'path'; interface Deferred<T> { @@ -14,58 +13,17 @@ interface Deferred<T> { reject: (reason: any) => void; } -const _typeof = { - number: 'number', - string: 'string', - undefined: 'undefined', - object: 'object', - function: 'function' -}; - -/** - * @returns whether the provided parameter is undefined. - */ -export function isUndefined(obj: any): obj is undefined { - return typeof (obj) === _typeof.undefined; -} - -/** - * @returns whether the provided parameter is undefined or null. - */ -export function isUndefinedOrNull(obj: any): obj is undefined | null { - return isUndefined(obj) || obj === null; -} - /** * Asserts that the argument passed in is neither undefined nor null. */ -export function assertIsDefined<T>(arg: T | null | undefined): T { - if (isUndefinedOrNull(arg)) { +function assertIsDefined<T>(arg: T | null | undefined): T { + if (typeof (arg) === 'undefined' || arg === null) { throw new Error('Assertion Failed: argument is undefined or null'); } return arg; } -export function createTerminateServer(server: http.Server) { - const sockets: Record<number, net.Socket> = {}; - let socketCount = 0; - server.on('connection', socket => { - const id = socketCount++; - sockets[id] = socket; - socket.on('close', () => { - delete sockets[id]; - }); - }); - return async () => { - const result = new Promise<Error | undefined>(resolve => server.close(resolve)); - for (const id in sockets) { - sockets[id].destroy(); - } - return result; - }; -} - export async function startServer(server: http.Server): Promise<string> { let portTimer: NodeJS.Timer; @@ -87,8 +45,8 @@ export async function startServer(server: http.Server): Promise<string> { } }); - server.on('error', err => { - reject(err); + server.on('error', _ => { + reject(new Error('Error listening to server')); }); server.on('close', () => { diff --git a/src/vs/workbench/contrib/files/browser/media/fileactions.css b/extensions/microsoft-authentication/src/env/browser/authServer.ts similarity index 68% rename from src/vs/workbench/contrib/files/browser/media/fileactions.css rename to extensions/microsoft-authentication/src/env/browser/authServer.ts index a6320e959bf..60b53c713a8 100644 --- a/src/vs/workbench/contrib/files/browser/media/fileactions.css +++ b/extensions/microsoft-authentication/src/env/browser/authServer.ts @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.dirty:not(:hover) > .monaco-action-bar .codicon-close::before { - content: "\ea71"; +export function startServer(_: any): any { + throw new Error('Not implemented'); +} + +export function createServer(_: any): any { + throw new Error('Not implemented'); } diff --git a/extensions/microsoft-authentication/src/env/browser/sha256.ts b/extensions/microsoft-authentication/src/env/browser/sha256.ts new file mode 100644 index 00000000000..369a1533cf9 --- /dev/null +++ b/extensions/microsoft-authentication/src/env/browser/sha256.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export async function sha256(s: string | Uint8Array): Promise<string> { + const createHash = require('sha.js'); + return createHash('sha256').update(s).digest('base64'); +} diff --git a/extensions/microsoft-authentication/src/env/node/sha256.ts b/extensions/microsoft-authentication/src/env/node/sha256.ts new file mode 100644 index 00000000000..691f7eedaf9 --- /dev/null +++ b/extensions/microsoft-authentication/src/env/node/sha256.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export async function sha256(s: string | Uint8Array): Promise<string> { + return (require('crypto')).createHash('sha256').update(s).digest('base64'); +} diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts new file mode 100644 index 00000000000..f31d72d8467 --- /dev/null +++ b/extensions/microsoft-authentication/src/extension.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper'; +import TelemetryReporter from 'vscode-extension-telemetry'; + +export const DEFAULT_SCOPES = 'https://management.core.windows.net/.default offline_access'; + +export async function activate(context: vscode.ExtensionContext) { + const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string }; + const telemetryReporter = new TelemetryReporter(name, version, aiKey); + + const loginService = new AzureActiveDirectoryService(); + context.subscriptions.push(loginService); + + await loginService.initialize(); + + context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({ + id: 'microsoft', + label: 'Microsoft', + supportsMultipleAccounts: true, + onDidChangeSessions: onDidChangeSessions.event, + getSessions: () => Promise.resolve(loginService.sessions), + login: async (scopes: string[]) => { + try { + /* __GDPR__ + "login" : { } + */ + telemetryReporter.sendTelemetryEvent('login'); + + const session = await loginService.login(scopes.sort().join(' ')); + onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); + return session; + } catch (e) { + /* __GDPR__ + "loginFailed" : { } + */ + telemetryReporter.sendTelemetryEvent('loginFailed'); + + throw e; + } + }, + logout: async (id: string) => { + try { + /* __GDPR__ + "logout" : { } + */ + telemetryReporter.sendTelemetryEvent('logout'); + + await loginService.logout(id); + onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); + } catch (e) { + /* __GDPR__ + "logoutFailed" : { } + */ + telemetryReporter.sendTelemetryEvent('logoutFailed'); + } + } + })); + + return; +} + +// this method is called when your extension is deactivated +export function deactivate() { } diff --git a/extensions/microsoft-authentication/src/keychain.ts b/extensions/microsoft-authentication/src/keychain.ts new file mode 100644 index 00000000000..f0e487760eb --- /dev/null +++ b/extensions/microsoft-authentication/src/keychain.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// keytar depends on a native module shipped in vscode, so this is +// how we load it +import * as keytarType from 'keytar'; +import * as vscode from 'vscode'; +import Logger from './logger'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +function getKeytar(): Keytar | undefined { + try { + return require('keytar'); + } catch (err) { + console.log(err); + } + + return undefined; +} + +export type Keytar = { + getPassword: typeof keytarType['getPassword']; + setPassword: typeof keytarType['setPassword']; + deletePassword: typeof keytarType['deletePassword']; +}; + +const OLD_SERVICE_ID = `${vscode.env.uriScheme}-microsoft.login`; +const SERVICE_ID = `microsoft.login`; +const ACCOUNT_ID = 'account'; + +export class Keychain { + private keytar: Keytar; + + constructor() { + const keytar = getKeytar(); + if (!keytar) { + throw new Error('System keychain unavailable'); + } + + this.keytar = keytar; + } + + + async setToken(token: string): Promise<void> { + try { + return await vscode.authentication.setPassword(SERVICE_ID, token); + } catch (e) { + Logger.error(`Setting token failed: ${e}`); + + // Temporary fix for #94005 + // This happens when processes write simulatenously to the keychain, most + // likely when trying to refresh the token. Ignore the error since additional + // writes after the first one do not matter. Should actually be fixed upstream. + if (e.message === 'The specified item already exists in the keychain.') { + return; + } + + const troubleshooting = localize('troubleshooting', "Troubleshooting Guide"); + const result = await vscode.window.showErrorMessage(localize('keychainWriteError', "Writing login information to the keychain failed with error '{0}'.", e.message), troubleshooting); + if (result === troubleshooting) { + vscode.env.openExternal(vscode.Uri.parse('https://code.visualstudio.com/docs/editor/settings-sync#_troubleshooting-keychain-issues')); + } + } + } + + async getToken(): Promise<string | null | undefined> { + try { + return await vscode.authentication.getPassword(SERVICE_ID); + } catch (e) { + // Ignore + Logger.error(`Getting token failed: ${e}`); + return Promise.resolve(undefined); + } + } + + async deleteToken(): Promise<void> { + try { + return await vscode.authentication.deletePassword(SERVICE_ID); + } catch (e) { + // Ignore + Logger.error(`Deleting token failed: ${e}`); + return Promise.resolve(undefined); + } + } + + async tryMigrate(): Promise<string | null> { + try { + const oldValue = await this.keytar.getPassword(OLD_SERVICE_ID, ACCOUNT_ID); + if (oldValue) { + await this.setToken(oldValue); + await this.keytar.deletePassword(OLD_SERVICE_ID, ACCOUNT_ID); + } + + return oldValue; + } catch (_) { + // Ignore + return Promise.resolve(null); + } + } +} + +export const keychain = new Keychain(); diff --git a/extensions/microsoft-authentication/src/logger.ts b/extensions/microsoft-authentication/src/logger.ts new file mode 100644 index 00000000000..b9ed27bd993 --- /dev/null +++ b/extensions/microsoft-authentication/src/logger.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +type LogLevel = 'Info' | 'Error'; + +class Log { + private output: vscode.OutputChannel; + + constructor() { + this.output = vscode.window.createOutputChannel('Microsoft Authentication'); + } + + private data2String(data: any): string { + if (data instanceof Error) { + return data.stack || data.message; + } + if (data.success === false && data.message) { + return data.message; + } + return data.toString(); + } + + public info(message: string, data?: any): void { + this.logLevel('Info', message, data); + } + + public error(message: string, data?: any): void { + this.logLevel('Error', message, data); + } + + public logLevel(level: LogLevel, message: string, data?: any): void { + this.output.appendLine(`[${level} - ${this.now()}] ${message}`); + if (data) { + this.output.appendLine(this.data2String(data)); + } + } + + private now(): string { + const now = new Date(); + return padLeft(now.getUTCHours() + '', 2, '0') + + ':' + padLeft(now.getMinutes() + '', 2, '0') + + ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds(); + } +} + +function padLeft(s: string, n: number, pad = ' ') { + return pad.repeat(Math.max(0, n - s.length)) + s; +} + +const Logger = new Log(); +export default Logger; diff --git a/extensions/microsoft-authentication/src/typings/refs.d.ts b/extensions/microsoft-authentication/src/typings/refs.d.ts new file mode 100644 index 00000000000..c9849d48e08 --- /dev/null +++ b/extensions/microsoft-authentication/src/typings/refs.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// <reference path='../../../../src/vs/vscode.d.ts'/> +/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/> diff --git a/extensions/vscode-account/src/utils.ts b/extensions/microsoft-authentication/src/utils.ts similarity index 100% rename from extensions/vscode-account/src/utils.ts rename to extensions/microsoft-authentication/src/utils.ts diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json new file mode 100644 index 00000000000..86c288d3c6e --- /dev/null +++ b/extensions/microsoft-authentication/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "lib": ["es2019"], + "module": "commonjs", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "dist", + "resolveJsonModule": true, + "rootDir": "src", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "es2019" + }, + "exclude": ["node_modules"] +} diff --git a/extensions/vscode-account/yarn.lock b/extensions/microsoft-authentication/yarn.lock similarity index 68% rename from extensions/vscode-account/yarn.lock rename to extensions/microsoft-authentication/yarn.lock index 4fc295de4b9..df970ce40d8 100644 --- a/extensions/vscode-account/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -2,22 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - "@types/keytar@^4.0.1": version "4.4.2" resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.2.tgz#49ef917d6cbb4f19241c0ab50cd35097b5729b32" @@ -25,15 +9,42 @@ dependencies: keytar "*" +"@types/node-fetch@^2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" + integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "14.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806" + integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw== + "@types/node@^10.12.21": version "10.17.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== -"@types/vscode@^1.41.0": - version "1.41.0" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.41.0.tgz#b0d75920220f84e07093285e59180c0f11d336cd" - integrity sha512-7SfeY5u9jgiELwxyLB3z7l6l/GbN9CqpCQGkcRlB7tKRFBxzbz2PoBfGrLxI1vRfUCIq5+hg5vtDHExwq5j3+A== +"@types/randombytes@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/randombytes/-/randombytes-2.0.0.tgz#0087ff5e60ae68023b9bc4398b406fea7ad18304" + integrity sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA== + dependencies: + "@types/node" "*" + +"@types/sha.js@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/sha.js/-/sha.js-2.4.0.tgz#bce682ef860b40f419d024fa08600c3b8d24bb01" + integrity sha512-amxKgPy6WJTKuw8mpUwjX2BSxuBtBmZfRwIUDIuPJKNwGN8CWDli8JTg5ONTWOtcTkHIstvT7oAhhYXqEjStHQ== + dependencies: + "@types/node" "*" + +"@types/uuid@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" + integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== ansi-regex@^2.0.0: version "2.1.1" @@ -45,12 +56,14 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: - color-convert "^1.9.0" + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "0.2.1" + zone.js "0.7.6" aproba@^1.0.3: version "1.2.0" @@ -65,17 +78,15 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== bl@^3.0.0: version "3.0.0" @@ -84,27 +95,13 @@ bl@^3.0.0: dependencies: readable-stream "^3.0.1" -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +buffer@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - -chalk@^2.0.0, chalk@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + base64-js "^1.0.2" + ieee754 "^1.1.4" chownr@^1.1.1: version "1.1.3" @@ -116,27 +113,12 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -commander@^2.12.1: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + delayed-stream "~1.0.0" console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" @@ -160,6 +142,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -170,10 +157,22 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diagnostic-channel-publishers@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= + +diagnostic-channel@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= + dependencies: + semver "^5.3.0" + +emitter-component@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" + integrity sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY= end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" @@ -182,36 +181,25 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - expand-template@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -231,37 +219,17 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob@^7.1.1: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - 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@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -288,19 +256,6 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - keytar@*: version "5.0.0" resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.0.0.tgz#c89b6b7a4608fd7af633d9f8474b1a7eb97cbe6f" @@ -309,18 +264,23 @@ keytar@*: nan "2.14.0" prebuild-install "5.3.3" +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + mimic-response@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -355,6 +315,11 @@ node-abi@^2.7.0: dependencies: semver "^5.4.1" +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" @@ -380,23 +345,13 @@ object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - prebuild-install@5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" @@ -431,6 +386,12 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +"randombytes@github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971": + version "2.1.0" + resolved "https://codeload.github.com/rmacfarlane/randombytes/tar.gz/b28d4ecee46262801ea09f15fa1f1513a05c5971" + dependencies: + safe-buffer "^5.1.0" + rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -463,18 +424,16 @@ readable-stream@^3.0.1, readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -resolve@^1.3.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" - integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== - dependencies: - path-parse "^1.0.6" - safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -490,6 +449,14 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +sha.js@2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -509,10 +476,12 @@ simple-get@^3.0.3: once "^1.3.1" simple-concat "^1.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stream@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" + integrity sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8= + dependencies: + emitter-component "^1.1.1" string-width@^1.0.1: version "1.0.2" @@ -564,13 +533,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - tar-fs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" @@ -592,37 +554,6 @@ tar-stream@^2.0.0: inherits "^2.0.3" readable-stream "^3.1.1" -tslib@^1.8.0, tslib@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tslint@^5.12.1: - version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" - integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== - dependencies: - "@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" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -640,6 +571,23 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +uuid@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e" + integrity sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q== + +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== + dependencies: + applicationinsights "1.0.8" + +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + which-pm-runs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" @@ -656,3 +604,8 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +zone.js@0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/extensions/npm/.vscodeignore b/extensions/npm/.vscodeignore index 27bb76ffd24..7700b94ebb0 100644 --- a/extensions/npm/.vscodeignore +++ b/extensions/npm/.vscodeignore @@ -3,4 +3,5 @@ out/** tsconfig.json .vscode/** extension.webpack.config.js -yarn.lock \ No newline at end of file +extension-browser.webpack.config.js +yarn.lock diff --git a/extensions/npm/README.md b/extensions/npm/README.md index f3707f85d32..82730c7e82a 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -34,7 +34,7 @@ The extension fetches data from https://registry.npmjs.org and https://registry. - `npm.autoDetect` - Enable detecting scripts as tasks, the default is `on`. - `npm.runSilent` - Run npm script with the `--silent` option, the default is `false`. -- `npm.packageManager` - The package manager used to run the scripts: `npm` or `yarn`, the default is `npm`. +- `npm.packageManager` - The package manager used to run the scripts: `auto`, `npm`, `yarn` or `pnpm`, the default is `auto`, which detects your package manager based on your files. - `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '**/test/**'. - `npm.enableScriptExplorer` - Enable an explorer view for npm scripts. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. diff --git a/extensions/npm/extension-browser.webpack.config.js b/extensions/npm/extension-browser.webpack.config.js new file mode 100644 index 00000000000..b0c70c96d48 --- /dev/null +++ b/extensions/npm/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/npmBrowserMain.ts' + }, + output: { + filename: 'npmBrowserMain.js' + }, + node: { + 'child_process': 'empty' + } +}); diff --git a/extensions/npm/extension.webpack.config.js b/extensions/npm/extension.webpack.config.js index 56a1589f460..1c6d9493e33 100644 --- a/extensions/npm/extension.webpack.config.js +++ b/extensions/npm/extension.webpack.config.js @@ -14,12 +14,10 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/main.ts', + extension: './src/npmMain.ts', }, output: { - filename: 'main.js', - path: path.join(__dirname, 'dist'), - libraryTarget: 'commonjs', + filename: 'npmMain.js', }, resolve: { mainFields: ['module', 'main'], diff --git a/extensions/npm/images/code.svg b/extensions/npm/images/code.svg new file mode 100644 index 00000000000..f776259aad0 --- /dev/null +++ b/extensions/npm/images/code.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M4.32798 5.00905L1.2384 8.09746L4.32798 11.1859L3.5016 12.0123L0 8.51065V7.68427L3.5016 4.18267L4.32798 5.00905ZM12.4984 4.18267L11.672 5.00905L14.7616 8.09746L11.672 11.1859L12.4984 12.0123L16 8.51065V7.68427L12.4984 4.18267ZM4.56142 13.672L5.6049 14.1949L11.4409 2.52291L10.3974 2L4.56142 13.672V13.672Z" fill="black"/> +</svg> diff --git a/extensions/npm/package.json b/extensions/npm/package.json index cbd9645341a..52a22ec0db6 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -18,16 +18,20 @@ "watch": "gulp watch-extension:npm" }, "dependencies": { - "jsonc-parser": "^2.1.1", + "find-up": "^4.1.0", + "find-yarn-workspace-root": "^2.0.0", + "jsonc-parser": "^2.2.1", "minimatch": "^3.0.4", - "request-light": "^0.2.5", - "vscode-nls": "^4.0.0" + "request-light": "^0.4.0", + "vscode-nls": "^4.1.1", + "which-pm": "^2.0.0" }, "devDependencies": { "@types/minimatch": "^3.0.3", "@types/node": "^12.11.7" }, - "main": "./out/main", + "main": "./out/npmMain", + "browser": "./dist/browser/npmBrowserMain", "activationEvents": [ "onCommand:workbench.action.tasks.runTask", "onCommand:npm.runScriptFromFolder", @@ -39,9 +43,15 @@ "languages": [ { "id": "ignore", - "filenames": [ + "extensions": [ ".npmignore" ] + }, + { + "id": "properties", + "extensions": [ + ".npmrc" + ] } ], "views": { @@ -49,7 +59,8 @@ { "id": "npm", "name": "%view.name%", - "when": "npm:showScriptExplorer || config.npm.enableScriptExplorer" + "icon": "images/code.svg", + "visibility": "hidden" } ] }, @@ -57,18 +68,12 @@ { "command": "npm.runScript", "title": "%command.run%", - "icon": { - "light": "resources/light/continue.svg", - "dark": "resources/dark/continue.svg" - } + "icon": "$(run)" }, { "command": "npm.debugScript", "title": "%command.debug%", - "icon": { - "light": "resources/light/debug.svg", - "dark": "resources/dark/debug.svg" - } + "icon": "$(debug)" }, { "command": "npm.openScript", @@ -81,10 +86,7 @@ { "command": "npm.refresh", "title": "%command.refresh%", - "icon": { - "light": "resources/light/refresh.svg", - "dark": "resources/dark/refresh.svg" - } + "icon": "$(refresh)" }, { "command": "npm.runSelectedScript", @@ -93,6 +95,10 @@ { "command": "npm.runScriptFromFolder", "title": "%command.runScriptFromFolder%" + }, + { + "command": "npm.packageManager", + "title": "%command.packageManager" } ], "menus": { @@ -124,12 +130,16 @@ { "command": "npm.runScriptFromFolder", "when": "false" + }, + { + "command": "npm.packageManager", + "when": "false" } ], "editor/context": [ { "command": "npm.runSelectedScript", - "when": "resourceFilename == 'package.json'", + "when": "resourceFilename == 'package.json' && resourceScheme == file", "group": "navigation@+1" } ], @@ -166,14 +176,9 @@ "when": "view == npm && viewItem == script", "group": "inline" }, - { - "command": "npm.runScript", - "when": "view == npm && viewItem == debugScript", - "group": "inline" - }, { "command": "npm.debugScript", - "when": "view == npm && viewItem == debugScript", + "when": "view == npm && viewItem == script", "group": "inline" }, { @@ -183,12 +188,12 @@ } ], "explorer/context": [ - { - "when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder", + { + "when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder && resourceScheme == file", "command": "npm.runScriptFromFolder", - "group": "2_workspace" - } - ] + "group": "2_workspace" + } + ] }, "configuration": { "id": "npm", @@ -215,10 +220,12 @@ "scope": "resource", "type": "string", "enum": [ + "auto", "npm", - "yarn" + "yarn", + "pnpm" ], - "default": "npm", + "default": "auto", "description": "%config.npm.packageManager%" }, "npm.exclude": { @@ -236,6 +243,7 @@ "type": "boolean", "default": false, "scope": "resource", + "deprecationMessage": "The NPM Script Explorer is now available in 'Views' menu in the Explorer in all folders.", "description": "%config.npm.enableScriptExplorer%" }, "npm.enableRunFromFolder": { @@ -268,11 +276,11 @@ "jsonValidation": [ { "fileMatch": "package.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/package.json" + "url": "https://json.schemastore.org/package" }, { "fileMatch": "bower.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/bower.json" + "url": "https://json.schemastore.org/bower" } ], "taskDefinitions": [ @@ -290,7 +298,8 @@ "type": "string", "description": "%taskdef.path%" } - } + }, + "when": "shellExecutionSupported" } ] } diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 51756d241f1..8ecf3746281 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -19,5 +19,6 @@ "command.openScript": "Open", "command.runInstall": "Run Install", "command.runSelectedScript": "Run Script", - "command.runScriptFromFolder": "Run NPM Script in Folder..." + "command.runScriptFromFolder": "Run NPM Script in Folder...", + "command.packageManager": "Get Configured Package Manager" } diff --git a/extensions/npm/resources/dark/continue.svg b/extensions/npm/resources/dark/continue.svg deleted file mode 100644 index 8b0a58eca9b..00000000000 --- a/extensions/npm/resources/dark/continue.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M4 2V14.4805L12.9146 8.24024L4 2ZM11.1809 8.24024L4.995 12.5684V3.91209L11.1809 8.24024Z" fill="#C5C5C5"/> -</svg> diff --git a/extensions/npm/resources/dark/debug.svg b/extensions/npm/resources/dark/debug.svg deleted file mode 100644 index e4c1b7a927b..00000000000 --- a/extensions/npm/resources/dark/debug.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M3.01996 8.50166L3.01725 8.46085C3.01812 8.47446 3.01903 8.48807 3.01996 8.50166Z" fill="#C5C5C5"/> -<path d="M7.2577 8.90466L5.4876 7.12558L6.07922 6.53397L7.95097 8.41518L9.79605 6.57011L10.3877 7.16171L8.61769 8.93167L10.3878 10.7108L9.79619 11.3024L7.92445 9.42114L6.07936 11.2662L5.48775 10.6746L7.2577 8.90466Z" fill="#C5C5C5"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M10.8775 3.91833V4.5H11.6624L13.3203 2.8421L13.9119 3.43371L12.3421 5.00354L12.361 5.05303C12.6914 5.91515 12.8775 6.88815 12.8775 7.91833C12.8775 8.11403 12.8708 8.30766 12.8576 8.49886L12.8546 8.5425H14.92V9.37916H12.7495L12.7434 9.41265C12.567 10.3771 12.2239 11.2564 11.7565 11.9964L11.7218 12.0515L13.7182 14.058L13.1251 14.6481L11.2153 12.7287L11.1576 12.7932C10.2949 13.7582 9.17697 14.3367 7.95917 14.3367C6.72251 14.3367 5.58881 13.7401 4.72075 12.748L4.66326 12.6823L2.79157 14.554L2.19995 13.9624L4.16317 11.9992L4.12918 11.9442C3.6785 11.2152 3.34718 10.3545 3.17494 9.41265L3.16882 9.37916H1V8.5425H3.0637L3.0607 8.49886C3.04755 8.30766 3.04084 8.11403 3.04084 7.91833C3.04084 6.90159 3.22212 5.94055 3.54446 5.08683L3.56303 5.03764L1.95216 3.41862L2.54527 2.8285L4.20835 4.5H5.04084V3.91833C5.04084 2.30658 6.34742 1 7.95917 1C9.57092 1 10.8775 2.30658 10.8775 3.91833ZM5.87751 3.91833V4.5H10.0408V3.91833C10.0408 2.76866 9.10884 1.83667 7.95917 1.83667C6.8095 1.83667 5.87751 2.76866 5.87751 3.91833ZM11.5938 5.38957L11.5739 5.33667H4.34441L4.32451 5.38957C4.0411 6.1427 3.8775 7.00011 3.8775 7.91833C3.8775 9.52826 4.38048 10.9522 5.15153 11.9546C5.9219 12.9561 6.9225 13.5 7.95917 13.5C8.99584 13.5 9.99644 12.9561 10.7668 11.9546C11.5379 10.9522 12.0408 9.52826 12.0408 7.91833C12.0408 7.00011 11.8772 6.14269 11.5938 5.38957Z" fill="#C5C5C5"/> -</svg> diff --git a/extensions/npm/resources/dark/prepostscript.svg b/extensions/npm/resources/dark/prepostscript.svg deleted file mode 100644 index a8c87f2d8f6..00000000000 --- a/extensions/npm/resources/dark/prepostscript.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<g opacity="0.5"> -<path d="M2.80723 14.9754C2.57119 14.9721 2.33826 14.9211 2.12247 14.8254C1.90667 14.7297 1.71248 14.5913 1.55158 14.4186C1.2385 14.1334 1.04433 13.7408 1.00775 13.3189C0.966225 12.8828 1.09269 12.4473 1.36133 12.1013C2.56779 10.8289 4.9473 8.4494 6.67811 6.75479C6.30983 5.75887 6.32704 4.66127 6.72637 3.67739C7.05474 2.85876 7.63869 2.16805 8.39129 1.70807C8.9817 1.31706 9.66031 1.07944 10.3657 1.01673C11.0711 0.954022 11.7809 1.06819 12.4311 1.34892L13.0482 1.6162L10.1824 4.56738L11.4371 5.82582L14.3809 2.94887L14.6482 3.56788C14.8735 4.08976 14.993 4.65119 14.9997 5.21961C15.0064 5.78802 14.9002 6.35211 14.6872 6.87915C14.476 7.40029 14.1623 7.87368 13.7647 8.27122C13.5394 8.49169 13.2904 8.68653 13.0222 8.85218C12.4673 9.22275 11.8324 9.45636 11.1697 9.5338C10.5069 9.61124 9.83521 9.5303 9.20982 9.29764C8.11194 10.4113 5.37142 13.1704 3.89119 14.5522C3.59426 14.8219 3.20832 14.9726 2.80723 14.9754ZM10.7448 1.92802C10.087 1.92637 9.44359 2.12018 8.89614 2.48485C8.68265 2.6152 8.48437 2.76897 8.30498 2.9433C7.82789 3.42423 7.50926 4.03953 7.39182 4.70669C7.27437 5.37385 7.36374 6.06098 7.64792 6.67591L7.78342 6.97288L7.55048 7.20025C5.81224 8.89672 3.28146 11.4201 2.06479 12.7045C1.95646 12.8658 1.91012 13.0608 1.93435 13.2535C1.95857 13.4463 2.05171 13.6238 2.19657 13.7532C2.28005 13.8462 2.38177 13.9211 2.49541 13.9731C2.59557 14.0184 2.70383 14.043 2.81373 14.0455C2.98064 14.0413 3.14044 13.977 3.26383 13.8646C4.83687 12.3964 7.87622 9.32641 8.76807 8.42435L8.9973 8.19326L9.29242 8.32783C9.80618 8.56732 10.3731 8.66985 10.9382 8.62545C11.5033 8.58106 12.0473 8.39125 12.5174 8.07447C12.7313 7.9426 12.9296 7.78694 13.1085 7.61045C13.4183 7.30153 13.6631 6.93374 13.8286 6.52874C13.994 6.12375 14.0767 5.68974 14.0719 5.25228C14.0719 5.03662 14.0505 4.82148 14.0078 4.61007L11.4306 7.12508L8.87944 4.57759L11.3944 1.98834C11.1804 1.94674 10.9628 1.92653 10.7448 1.92802Z" fill="#C5C5C5"/> -</g> -</svg> diff --git a/extensions/npm/resources/dark/refresh.svg b/extensions/npm/resources/dark/refresh.svg deleted file mode 100644 index ec0c43f0bc3..00000000000 --- a/extensions/npm/resources/dark/refresh.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M5.56253 2.51574C3.46348 3.45007 2 5.55411 2 7.99996C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 7.99996C14 5.32516 12.2497 3.05916 9.83199 2.28479L9.52968 3.23829C11.5429 3.88451 13 5.77207 13 7.99996C13 10.7614 10.7614 13 8 13C5.23858 13 3 10.7614 3 7.99996C3 6.31101 3.83742 4.81764 5.11969 3.91242L5.56253 2.51574Z" fill="#C5C5C5"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3H2V2H5.5L6 2.5V6H5V3Z" fill="#C5C5C5"/> -</svg> diff --git a/extensions/npm/resources/dark/script.svg b/extensions/npm/resources/dark/script.svg deleted file mode 100644 index 7137a9d7bb5..00000000000 --- a/extensions/npm/resources/dark/script.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M2.80723 14.9754C2.57119 14.9721 2.33826 14.9211 2.12247 14.8254C1.90667 14.7297 1.71248 14.5913 1.55158 14.4186C1.2385 14.1334 1.04433 13.7408 1.00775 13.3189C0.966225 12.8828 1.09269 12.4473 1.36133 12.1013C2.56779 10.8289 4.9473 8.4494 6.67811 6.75479C6.30983 5.75887 6.32704 4.66127 6.72637 3.67739C7.05474 2.85876 7.63869 2.16805 8.39129 1.70807C8.9817 1.31706 9.66031 1.07944 10.3657 1.01673C11.0711 0.954022 11.7809 1.06819 12.4311 1.34892L13.0482 1.6162L10.1824 4.56738L11.4371 5.82582L14.3809 2.94887L14.6482 3.56788C14.8735 4.08976 14.993 4.65119 14.9997 5.21961C15.0064 5.78802 14.9002 6.35211 14.6872 6.87915C14.476 7.40029 14.1623 7.87368 13.7647 8.27122C13.5394 8.49169 13.2904 8.68653 13.0222 8.85218C12.4673 9.22275 11.8324 9.45636 11.1697 9.5338C10.5069 9.61124 9.83521 9.5303 9.20982 9.29764C8.11194 10.4113 5.37142 13.1704 3.89119 14.5522C3.59426 14.8219 3.20832 14.9726 2.80723 14.9754ZM10.7448 1.92802C10.087 1.92637 9.44359 2.12018 8.89614 2.48485C8.68265 2.6152 8.48437 2.76897 8.30498 2.9433C7.82789 3.42423 7.50926 4.03953 7.39182 4.70669C7.27438 5.37385 7.36374 6.06098 7.64792 6.67591L7.78342 6.97288L7.55048 7.20025C5.81224 8.89672 3.28146 11.4201 2.06479 12.7045C1.95646 12.8658 1.91012 13.0608 1.93435 13.2535C1.95857 13.4463 2.05171 13.6238 2.19657 13.7532C2.28005 13.8462 2.38177 13.9211 2.49541 13.9731C2.59557 14.0184 2.70383 14.043 2.81373 14.0455C2.98064 14.0413 3.14044 13.977 3.26383 13.8646C4.83687 12.3964 7.87622 9.32641 8.76807 8.42435L8.9973 8.19326L9.29242 8.32783C9.80617 8.56732 10.3731 8.66985 10.9382 8.62545C11.5033 8.58106 12.0473 8.39125 12.5174 8.07447C12.7313 7.9426 12.9296 7.78694 13.1085 7.61045C13.4183 7.30153 13.6631 6.93374 13.8286 6.52874C13.994 6.12375 14.0767 5.68974 14.0719 5.25228C14.0719 5.03662 14.0505 4.82148 14.0078 4.61007L11.4306 7.12508L8.87944 4.57759L11.3944 1.98834C11.1804 1.94674 10.9628 1.92653 10.7448 1.92802Z" fill="#C5C5C5"/> -</svg> diff --git a/extensions/npm/resources/light/continue.svg b/extensions/npm/resources/light/continue.svg deleted file mode 100644 index 2563bfa114b..00000000000 --- a/extensions/npm/resources/light/continue.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M4 2V14.4805L12.9146 8.24024L4 2ZM11.1809 8.24024L4.995 12.5684V3.91209L11.1809 8.24024Z" fill="#424242"/> -</svg> diff --git a/extensions/npm/resources/light/debug.svg b/extensions/npm/resources/light/debug.svg deleted file mode 100644 index 81a5ffb6b11..00000000000 --- a/extensions/npm/resources/light/debug.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M3.01996 8.50166L3.01725 8.46085C3.01812 8.47446 3.01903 8.48807 3.01996 8.50166Z" fill="#424242"/> -<path d="M7.2577 8.90466L5.4876 7.12558L6.07922 6.53397L7.95097 8.41518L9.79605 6.57011L10.3877 7.16171L8.61769 8.93167L10.3878 10.7108L9.79619 11.3024L7.92445 9.42114L6.07936 11.2662L5.48775 10.6746L7.2577 8.90466Z" fill="#424242"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M10.8775 3.91833V4.5H11.6624L13.3203 2.8421L13.9119 3.43371L12.3421 5.00354L12.361 5.05303C12.6914 5.91515 12.8775 6.88815 12.8775 7.91833C12.8775 8.11403 12.8708 8.30766 12.8576 8.49886L12.8546 8.5425H14.92V9.37916H12.7495L12.7434 9.41265C12.567 10.3771 12.2239 11.2564 11.7565 11.9964L11.7218 12.0515L13.7182 14.058L13.1251 14.6481L11.2153 12.7287L11.1576 12.7932C10.2949 13.7582 9.17697 14.3367 7.95917 14.3367C6.72251 14.3367 5.58881 13.7401 4.72075 12.748L4.66326 12.6823L2.79157 14.554L2.19995 13.9624L4.16317 11.9992L4.12918 11.9442C3.6785 11.2152 3.34718 10.3545 3.17494 9.41265L3.16882 9.37916H1V8.5425H3.0637L3.0607 8.49886C3.04755 8.30766 3.04084 8.11403 3.04084 7.91833C3.04084 6.90159 3.22212 5.94055 3.54446 5.08683L3.56303 5.03764L1.95216 3.41862L2.54527 2.8285L4.20835 4.5H5.04084V3.91833C5.04084 2.30658 6.34742 1 7.95917 1C9.57092 1 10.8775 2.30658 10.8775 3.91833ZM5.87751 3.91833V4.5H10.0408V3.91833C10.0408 2.76866 9.10884 1.83667 7.95917 1.83667C6.8095 1.83667 5.87751 2.76866 5.87751 3.91833ZM11.5938 5.38957L11.5739 5.33667H4.34441L4.32451 5.38957C4.0411 6.1427 3.8775 7.00011 3.8775 7.91833C3.8775 9.52826 4.38048 10.9522 5.15153 11.9546C5.9219 12.9561 6.9225 13.5 7.95917 13.5C8.99584 13.5 9.99644 12.9561 10.7668 11.9546C11.5379 10.9522 12.0408 9.52826 12.0408 7.91833C12.0408 7.00011 11.8772 6.14269 11.5938 5.38957Z" fill="#424242"/> -</svg> diff --git a/extensions/npm/resources/light/prepostscript.svg b/extensions/npm/resources/light/prepostscript.svg deleted file mode 100644 index 87eb59e12a6..00000000000 --- a/extensions/npm/resources/light/prepostscript.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<g opacity="0.5"> -<path d="M2.80723 14.9754C2.57119 14.9721 2.33826 14.9211 2.12247 14.8254C1.90667 14.7297 1.71248 14.5913 1.55158 14.4186C1.2385 14.1334 1.04433 13.7408 1.00775 13.3189C0.966225 12.8828 1.09269 12.4473 1.36133 12.1013C2.56779 10.8289 4.9473 8.4494 6.67811 6.75479C6.30983 5.75887 6.32704 4.66127 6.72637 3.67739C7.05474 2.85876 7.63869 2.16805 8.39129 1.70807C8.9817 1.31706 9.66031 1.07944 10.3657 1.01673C11.0711 0.954022 11.7809 1.06819 12.4311 1.34892L13.0482 1.6162L10.1824 4.56738L11.4371 5.82582L14.3809 2.94887L14.6482 3.56788C14.8735 4.08976 14.993 4.65119 14.9997 5.21961C15.0064 5.78802 14.9002 6.35211 14.6872 6.87915C14.476 7.40029 14.1623 7.87368 13.7647 8.27122C13.5394 8.49169 13.2904 8.68653 13.0222 8.85218C12.4673 9.22275 11.8324 9.45636 11.1697 9.5338C10.5069 9.61124 9.83521 9.5303 9.20982 9.29764C8.11194 10.4113 5.37142 13.1704 3.89119 14.5522C3.59426 14.8219 3.20832 14.9726 2.80723 14.9754ZM10.7448 1.92802C10.087 1.92637 9.44359 2.12018 8.89614 2.48485C8.68265 2.6152 8.48437 2.76897 8.30498 2.9433C7.82789 3.42423 7.50926 4.03953 7.39182 4.70669C7.27437 5.37385 7.36374 6.06098 7.64792 6.67591L7.78342 6.97288L7.55048 7.20025C5.81224 8.89672 3.28146 11.4201 2.06479 12.7045C1.95646 12.8658 1.91012 13.0608 1.93435 13.2535C1.95857 13.4463 2.05171 13.6238 2.19657 13.7532C2.28005 13.8462 2.38177 13.9211 2.49541 13.9731C2.59557 14.0184 2.70383 14.043 2.81373 14.0455C2.98064 14.0413 3.14044 13.977 3.26383 13.8646C4.83687 12.3964 7.87622 9.32641 8.76807 8.42435L8.9973 8.19326L9.29242 8.32783C9.80618 8.56732 10.3731 8.66985 10.9382 8.62545C11.5033 8.58106 12.0473 8.39125 12.5174 8.07447C12.7313 7.9426 12.9296 7.78694 13.1085 7.61045C13.4183 7.30153 13.6631 6.93374 13.8286 6.52874C13.994 6.12375 14.0767 5.68974 14.0719 5.25228C14.0719 5.03662 14.0505 4.82148 14.0078 4.61007L11.4306 7.12508L8.87944 4.57759L11.3944 1.98834C11.1804 1.94674 10.9628 1.92653 10.7448 1.92802Z" fill="#424242"/> -</g> -</svg> diff --git a/extensions/npm/resources/light/refresh.svg b/extensions/npm/resources/light/refresh.svg deleted file mode 100644 index a5b88123a0e..00000000000 --- a/extensions/npm/resources/light/refresh.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M5.56253 2.51574C3.46348 3.45007 2 5.55411 2 7.99996C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 7.99996C14 5.32516 12.2497 3.05916 9.83199 2.28479L9.52968 3.23829C11.5429 3.88451 13 5.77207 13 7.99996C13 10.7614 10.7614 13 8 13C5.23858 13 3 10.7614 3 7.99996C3 6.31101 3.83742 4.81764 5.11969 3.91242L5.56253 2.51574Z" fill="#424242"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3H2V2H5.5L6 2.5V6H5V3Z" fill="#424242"/> -</svg> diff --git a/extensions/npm/resources/light/script.svg b/extensions/npm/resources/light/script.svg deleted file mode 100644 index 60f77501db7..00000000000 --- a/extensions/npm/resources/light/script.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M2.80723 14.9754C2.57119 14.9721 2.33826 14.9211 2.12247 14.8254C1.90667 14.7297 1.71248 14.5913 1.55158 14.4186C1.2385 14.1334 1.04433 13.7408 1.00775 13.3189C0.966225 12.8828 1.09269 12.4473 1.36133 12.1013C2.56779 10.8289 4.9473 8.4494 6.67811 6.75479C6.30983 5.75887 6.32704 4.66127 6.72637 3.67739C7.05474 2.85876 7.63869 2.16805 8.39129 1.70807C8.9817 1.31706 9.66031 1.07944 10.3657 1.01673C11.0711 0.954022 11.7809 1.06819 12.4311 1.34892L13.0482 1.6162L10.1824 4.56738L11.4371 5.82582L14.3809 2.94887L14.6482 3.56788C14.8735 4.08976 14.993 4.65119 14.9997 5.21961C15.0064 5.78802 14.9002 6.35211 14.6872 6.87915C14.476 7.40029 14.1623 7.87368 13.7647 8.27122C13.5394 8.49169 13.2904 8.68653 13.0222 8.85218C12.4673 9.22275 11.8324 9.45636 11.1697 9.5338C10.5069 9.61124 9.83521 9.5303 9.20982 9.29764C8.11194 10.4113 5.37142 13.1704 3.89119 14.5522C3.59426 14.8219 3.20832 14.9726 2.80723 14.9754ZM10.7448 1.92802C10.087 1.92637 9.44359 2.12018 8.89614 2.48485C8.68265 2.6152 8.48437 2.76897 8.30498 2.9433C7.82789 3.42423 7.50926 4.03953 7.39182 4.70669C7.27438 5.37385 7.36374 6.06098 7.64792 6.67591L7.78342 6.97288L7.55048 7.20025C5.81224 8.89672 3.28146 11.4201 2.06479 12.7045C1.95646 12.8658 1.91012 13.0608 1.93435 13.2535C1.95857 13.4463 2.05171 13.6238 2.19657 13.7532C2.28005 13.8462 2.38177 13.9211 2.49541 13.9731C2.59557 14.0184 2.70383 14.043 2.81373 14.0455C2.98064 14.0413 3.14044 13.977 3.26383 13.8646C4.83687 12.3964 7.87622 9.32641 8.76807 8.42435L8.9973 8.19326L9.29242 8.32783C9.80617 8.56732 10.3731 8.66985 10.9382 8.62545C11.5033 8.58106 12.0473 8.39125 12.5174 8.07447C12.7313 7.9426 12.9296 7.78694 13.1085 7.61045C13.4183 7.30153 13.6631 6.93374 13.8286 6.52874C13.994 6.12375 14.0767 5.68974 14.0719 5.25228C14.0719 5.03662 14.0505 4.82148 14.0078 4.61007L11.4306 7.12508L8.87944 4.57759L11.3944 1.98834C11.1804 1.94674 10.9628 1.92653 10.7448 1.92802Z" fill="#424242"/> -</svg> diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index 705e40d34e7..c3a827fd1e1 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; +import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; -import { textToMarkedString } from './markedTextUtil'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -34,7 +33,7 @@ export class BowerJSONContribution implements IJSONContribution { return [{ language: 'json', scheme: '*', pattern: '**/bower.json' }, { language: 'json', scheme: '*', pattern: '**/.bower.json' }]; } - private onlineEnabled() { + private isEnabled() { return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); } @@ -55,8 +54,11 @@ export class BowerJSONContribution implements IJSONContribution { } public collectPropertySuggestions(_resource: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable<any> | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies']) || location.matches(['devDependencies']))) { - if (currentWord.length > 0 && this.onlineEnabled()) { + if (currentWord.length > 0) { const queryUrl = 'https://registry.bower.io/packages/search/' + encodeURIComponent(currentWord); return this.xhr({ @@ -123,7 +125,10 @@ export class BowerJSONContribution implements IJSONContribution { return null; } - public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Thenable<any> { + public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Promise<any> | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) { // not implemented. Could be do done calling the bower command. Waiting for web API: https://github.com/bower/registry/issues/26 const proposal = new CompletionItem(localize('json.bower.latest.version', 'latest')); @@ -133,7 +138,7 @@ export class BowerJSONContribution implements IJSONContribution { proposal.documentation = 'The latest version of the package'; collector.add(proposal); } - return Promise.resolve(null); + return null; } public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem | null> | null { @@ -150,10 +155,6 @@ export class BowerJSONContribution implements IJSONContribution { } private getInfo(pack: string): Thenable<string | undefined> { - if (!this.onlineEnabled()) { - return Promise.resolve(undefined); - } - const queryUrl = 'https://registry.bower.io/packages/' + encodeURIComponent(pack); return this.xhr({ @@ -181,13 +182,18 @@ export class BowerJSONContribution implements IJSONContribution { }); } - public getInfoContribution(_resource: string, location: Location): Thenable<MarkedString[] | null> | null { + public getInfoContribution(_resource: string, location: Location): Thenable<MarkdownString[] | null> | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { return this.getInfo(pack).then(documentation => { if (documentation) { - return [textToMarkedString(documentation)]; + const str = new MarkdownString(); + str.appendText(documentation); + return [str]; } return null; }); diff --git a/extensions/npm/src/features/jsonContributions.ts b/extensions/npm/src/features/jsonContributions.ts index 4a255baf823..071d57b3348 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -25,13 +25,13 @@ export interface IJSONContribution { getDocumentSelector(): DocumentSelector; getInfoContribution(fileName: string, location: Location): Thenable<MarkedString[] | null> | null; collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable<any> | null; - collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any>; + collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any> | null; collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<any>; resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem | null> | null; } -export function addJSONProviders(xhr: XHRRequest): Disposable { - const contributions = [new PackageJSONContribution(xhr), new BowerJSONContribution(xhr)]; +export function addJSONProviders(xhr: XHRRequest, canRunNPM: boolean): Disposable { + const contributions = [new PackageJSONContribution(xhr, canRunNPM), new BowerJSONContribution(xhr)]; const subscriptions: Disposable[] = []; contributions.forEach(contribution => { const selector = contribution.getDocumentSelector(); diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 4789886891c..7fc5475abcc 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; +import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString } from 'vscode'; import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; -import { textToMarkedString } from './markedTextUtil'; import * as cp from 'child_process'; import * as nls from 'vscode-nls'; @@ -28,14 +27,12 @@ export class PackageJSONContribution implements IJSONContribution { 'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench']; private knownScopes = ['@types', '@angular', '@babel', '@nuxtjs', '@vue', '@bazel']; - private xhr: XHRRequest; public getDocumentSelector(): DocumentSelector { return [{ language: 'json', scheme: '*', pattern: '**/package.json' }]; } - public constructor(xhr: XHRRequest) { - this.xhr = xhr; + public constructor(private xhr: XHRRequest, private canRunNPM: boolean) { } public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable<any> { @@ -54,6 +51,10 @@ export class PackageJSONContribution implements IJSONContribution { return Promise.resolve(null); } + private isEnabled() { + return this.canRunNPM || this.onlineEnabled(); + } + private onlineEnabled() { return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); } @@ -66,7 +67,7 @@ export class PackageJSONContribution implements IJSONContribution { isLast: boolean, collector: ISuggestionsCollector ): Thenable<any> | null { - if (!this.onlineEnabled()) { + if (!this.isEnabled()) { return null; } @@ -183,7 +184,7 @@ export class PackageJSONContribution implements IJSONContribution { } public async collectValueSuggestions(_fileName: string, location: Location, result: ISuggestionsCollector): Promise<any> { - if (!this.onlineEnabled()) { + if (!this.isEnabled()) { return null; } @@ -191,23 +192,23 @@ export class PackageJSONContribution implements IJSONContribution { const currentKey = location.path[location.path.length - 1]; if (typeof currentKey === 'string') { const info = await this.fetchPackageInfo(currentKey); - if (info && info.distTagsLatest) { + if (info && info.version) { - let name = JSON.stringify(info.distTagsLatest); + let name = JSON.stringify(info.version); let proposal = new CompletionItem(name); proposal.kind = CompletionItemKind.Property; proposal.insertText = name; proposal.documentation = localize('json.npm.latestversion', 'The currently latest version of the package'); result.add(proposal); - name = JSON.stringify('^' + info.distTagsLatest); + name = JSON.stringify('^' + info.version); proposal = new CompletionItem(name); proposal.kind = CompletionItemKind.Property; proposal.insertText = name; proposal.documentation = localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)'); result.add(proposal); - name = JSON.stringify('~' + info.distTagsLatest); + name = JSON.stringify('~' + info.version); proposal = new CompletionItem(name); proposal.kind = CompletionItemKind.Property; proposal.insertText = name; @@ -219,14 +220,27 @@ export class PackageJSONContribution implements IJSONContribution { return null; } + private getDocumentation(description: string | undefined, version: string | undefined, homepage: string | undefined): MarkdownString { + const str = new MarkdownString(); + if (description) { + str.appendText(description); + } + if (version) { + str.appendText('\n\n'); + str.appendText(localize('json.npm.version.hover', 'Latest version: {0}', version)); + } + if (homepage) { + str.appendText('\n\n'); + str.appendText(homepage); + } + return str; + } + public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem | null> | null { - if (item.kind === CompletionItemKind.Property && item.documentation === '') { - return this.getInfo(item.label).then(infos => { - if (infos.length > 0) { - item.documentation = infos[0]; - if (infos.length > 1) { - item.detail = infos[1]; - } + if (item.kind === CompletionItemKind.Property && !item.documentation) { + return this.fetchPackageInfo(item.label).then(info => { + if (info) { + item.documentation = this.getDocumentation(info.description, info.version, info.homepage); return item; } return null; @@ -235,38 +249,47 @@ export class PackageJSONContribution implements IJSONContribution { return null; } - private async getInfo(pack: string): Promise<string[]> { - let info = await this.fetchPackageInfo(pack); - if (info) { - const result: string[] = []; - result.push(info.description || ''); - result.push(info.distTagsLatest ? localize('json.npm.version.hover', 'Latest version: {0}', info.distTagsLatest) : ''); - result.push(info.homepage || ''); - return result; + private isValidNPMName(name: string): boolean { + // following rules from https://github.com/npm/validate-npm-package-name + if (!name || name.length > 214 || name.match(/^[_.]/)) { + return false; } - - return []; + const match = name.match(/^(?:@([^/]+?)[/])?([^/]+?)$/); + if (match) { + const scope = match[1]; + if (scope && encodeURIComponent(scope) !== scope) { + return false; + } + const name = match[2]; + return encodeURIComponent(name) === name; + } + return false; } private async fetchPackageInfo(pack: string): Promise<ViewPackageInfo | undefined> { - let info = await this.npmView(pack); - if (!info) { + if (!this.isValidNPMName(pack)) { + return undefined; // avoid unnecessary lookups + } + let info: ViewPackageInfo | undefined; + if (this.canRunNPM) { + info = await this.npmView(pack); + } + if (!info && this.onlineEnabled()) { info = await this.npmjsView(pack); } return info; } - private npmView(pack: string): Promise<ViewPackageInfo | undefined> { return new Promise((resolve, _reject) => { - const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage'; - cp.exec(command, (error, stdout) => { + const args = ['view', '--json', pack, 'description', 'dist-tags.latest', 'homepage', 'version']; + cp.execFile(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, (error, stdout) => { if (!error) { try { const content = JSON.parse(stdout); resolve({ description: content['description'], - distTagsLatest: content['dist-tags.latest'], + version: content['dist-tags.latest'] || content['version'], homepage: content['homepage'] }); return; @@ -280,22 +303,20 @@ export class PackageJSONContribution implements IJSONContribution { } private async npmjsView(pack: string): Promise<ViewPackageInfo | undefined> { - const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace(/%40/g, '@'); + const queryUrl = 'https://api.npms.io/v2/package/' + encodeURIComponent(pack); try { const success = await this.xhr({ url: queryUrl, agent: USER_AGENT }); const obj = JSON.parse(success.responseText); - if (obj) { - const latest = obj && obj['dist-tags'] && obj['dist-tags']['latest']; - if (latest) { - return { - description: obj.description || '', - distTagsLatest: latest, - homepage: obj.homepage || '' - }; - } + const metadata = obj?.collected?.metadata; + if (metadata) { + return { + description: metadata.description || '', + version: metadata.version, + homepage: metadata.links?.homepage || '' + }; } } catch (e) { @@ -305,12 +326,15 @@ export class PackageJSONContribution implements IJSONContribution { } public getInfoContribution(_fileName: string, location: Location): Thenable<MarkedString[] | null> | null { + if (!this.isEnabled()) { + return null; + } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { - return this.getInfo(pack).then(infos => { - if (infos.length) { - return [infos.map(textToMarkedString).join('\n\n')]; + return this.fetchPackageInfo(pack).then(info => { + if (info) { + return [this.getDocumentation(info.description, info.version, info.homepage)]; } return null; }); @@ -339,7 +363,7 @@ export class PackageJSONContribution implements IJSONContribution { proposal.kind = CompletionItemKind.Property; proposal.insertText = insertText; proposal.filterText = JSON.stringify(name); - proposal.documentation = pack.description || ''; + proposal.documentation = this.getDocumentation(pack.description, pack.version, pack?.links?.homepage); collector.add(proposal); } } @@ -349,10 +373,11 @@ interface SearchPackageInfo { name: string; description?: string; version?: string; + links?: { homepage?: string; }; } interface ViewPackageInfo { description: string; - distTagsLatest?: string; + version?: string; homepage?: string; } diff --git a/extensions/npm/src/npmBrowserMain.ts b/extensions/npm/src/npmBrowserMain.ts new file mode 100644 index 00000000000..96cfe579505 --- /dev/null +++ b/extensions/npm/src/npmBrowserMain.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as httpRequest from 'request-light'; +import * as vscode from 'vscode'; +import { addJSONProviders } from './features/jsonContributions'; + +export async function activate(context: vscode.ExtensionContext): Promise<void> { + context.subscriptions.push(addJSONProviders(httpRequest.xhr, false)); +} + +export function deactivate(): void { +} diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/npmMain.ts similarity index 71% rename from extensions/npm/src/main.ts rename to extensions/npm/src/npmMain.ts index 3777f393757..67cdf56c2d3 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/npmMain.ts @@ -8,20 +8,34 @@ import * as vscode from 'vscode'; import { addJSONProviders } from './features/jsonContributions'; import { runSelectedScript, selectAndRunScriptFromFolder } from './commands'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks'; +import { getPackageManager, invalidateTasksCache, NpmTaskProvider } from './tasks'; import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; let treeDataProvider: NpmScriptsTreeDataProvider | undefined; -export async function activate(context: vscode.ExtensionContext): Promise<void> { - registerTaskProvider(context); - treeDataProvider = registerExplorer(context); - registerHoverProvider(context); +function invalidateScriptCaches() { + invalidateHoverScriptsCache(); + invalidateTasksCache(); + if (treeDataProvider) { + treeDataProvider.refresh(); + } +} +export async function activate(context: vscode.ExtensionContext): Promise<void> { configureHttpRequest(); - let d = vscode.workspace.onDidChangeConfiguration((e) => { - configureHttpRequest(); - if (e.affectsConfiguration('npm.exclude')) { + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) { + configureHttpRequest(); + } + })); + + const canRunNPM = canRunNpmInCurrentWorkspace(); + context.subscriptions.push(addJSONProviders(httpRequest.xhr, canRunNPM)); + + treeDataProvider = registerExplorer(context); + + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect')) { invalidateTasksCache(); if (treeDataProvider) { treeDataProvider.refresh(); @@ -32,33 +46,32 @@ export async function activate(context: vscode.ExtensionContext): Promise<void> treeDataProvider.refresh(); } } - }); - context.subscriptions.push(d); + })); + + registerTaskProvider(context); + registerHoverProvider(context); - d = vscode.workspace.onDidChangeTextDocument((e) => { - invalidateHoverScriptsCache(e.document); - }); - context.subscriptions.push(d); context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); - context.subscriptions.push(addJSONProviders(httpRequest.xhr)); - - if (await hasPackageJson()) { - vscode.commands.executeCommand('setContext', 'npm:showScriptExplorer', true); - } - context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromFolder', selectAndRunScriptFromFolder)); + context.subscriptions.push(vscode.commands.registerCommand('npm.refresh', () => { + invalidateScriptCaches(); + })); + context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => { + if (args instanceof vscode.Uri) { + return getPackageManager(args); + } + return ''; + })); +} + +function canRunNpmInCurrentWorkspace() { + if (vscode.workspace.workspaceFolders) { + return vscode.workspace.workspaceFolders.some(f => f.uri.scheme === 'file'); + } + return false; } function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined { - - function invalidateScriptCaches() { - invalidateHoverScriptsCache(); - invalidateTasksCache(); - if (treeDataProvider) { - treeDataProvider.refresh(); - } - } - if (vscode.workspace.workspaceFolders) { let watcher = vscode.workspace.createFileSystemWatcher('**/package.json'); watcher.onDidChange((_e) => invalidateScriptCaches()); @@ -70,7 +83,7 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab context.subscriptions.push(workspaceWatcher); let provider: vscode.TaskProvider = new NpmTaskProvider(); - let disposable = vscode.workspace.registerTaskProvider('npm', provider); + let disposable = vscode.tasks.registerTaskProvider('npm', provider); context.subscriptions.push(disposable); return disposable; } diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index b5234c5fc1b..0c1b51804c0 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -3,18 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { JSONVisitor, visit } from 'jsonc-parser'; import * as path from 'path'; import { - Event, EventEmitter, ExtensionContext, Task2 as Task, - TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, - WorkspaceFolder, commands, window, workspace, tasks, Selection, TaskGroup + commands, Event, EventEmitter, ExtensionContext, + Selection, Task, + TaskGroup, tasks, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, + window, workspace, WorkspaceFolder } from 'vscode'; -import { visit, JSONVisitor } from 'jsonc-parser'; -import { - NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, - isWorkspaceFolder, getTaskName, createTask, extractDebugArgFromScript, startDebugging -} from './tasks'; import * as nls from 'vscode-nls'; +import { + createTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition, + startDebugging +} from './tasks'; const localize = nls.loadMessageBundle(); @@ -73,7 +74,7 @@ class NpmScript extends TreeItem { task: Task; package: PackageJSON; - constructor(context: ExtensionContext, packageJson: PackageJSON, task: Task) { + constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task) { super(task.name, TreeItemCollapsibleState.None); const command: ExplorerCommands = workspace.getConfiguration('npm').get<ExplorerCommands>('scriptExplorerAction') || 'open'; @@ -90,23 +91,14 @@ class NpmScript extends TreeItem { } }; this.contextValue = 'script'; - if (task.group && task.group === TaskGroup.Rebuild) { - this.contextValue = 'debugScript'; - } this.package = packageJson; this.task = task; this.command = commandList[command]; if (task.group && task.group === TaskGroup.Clean) { - this.iconPath = { - light: context.asAbsolutePath(path.join('resources', 'light', 'prepostscript.svg')), - dark: context.asAbsolutePath(path.join('resources', 'dark', 'prepostscript.svg')) - }; + this.iconPath = new ThemeIcon('wrench-subaction'); } else { - this.iconPath = { - light: context.asAbsolutePath(path.join('resources', 'light', 'script.svg')), - dark: context.asAbsolutePath(path.join('resources', 'dark', 'script.svg')) - }; + this.iconPath = new ThemeIcon('wrench'); } if (task.detail) { this.tooltip = task.detail; @@ -119,8 +111,8 @@ class NpmScript extends TreeItem { } class NoScripts extends TreeItem { - constructor() { - super(localize('noScripts', 'No scripts found'), TreeItemCollapsibleState.None); + constructor(message: string) { + super(message, TreeItemCollapsibleState.None); this.contextValue = 'noscripts'; } } @@ -137,7 +129,6 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> { subscriptions.push(commands.registerCommand('npm.runScript', this.runScript, this)); subscriptions.push(commands.registerCommand('npm.debugScript', this.debugScript, this)); subscriptions.push(commands.registerCommand('npm.openScript', this.openScript, this)); - subscriptions.push(commands.registerCommand('npm.refresh', this.refresh, this)); subscriptions.push(commands.registerCommand('npm.runInstall', this.runInstall, this)); } @@ -145,27 +136,8 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> { tasks.executeTask(script.task); } - private extractDebugArg(scripts: any, task: Task): [string, number] | undefined { - return extractDebugArgFromScript(scripts[task.name]); - } - private async debugScript(script: NpmScript) { - let task = script.task; - let uri = getPackageJsonUriFromTask(task); - let scripts = await getScripts(uri!); - - let debugArg = this.extractDebugArg(scripts, task); - if (!debugArg) { - let message = localize('noDebugOptions', 'Could not launch "{0}" for debugging because the scripts lacks a node debug option, e.g. "--inspect-brk".', task.name); - let learnMore = localize('learnMore', 'Learn More'); - let ok = localize('ok', 'OK'); - let result = await window.showErrorMessage(message, { modal: true }, ok, learnMore); - if (result === learnMore) { - commands.executeCommand('vscode.open', Uri.parse('https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-support-for-npm-and-other-tools')); - } - return; - } - startDebugging(task.name, debugArg[0], debugArg[1], script.getFolder()); + startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { @@ -209,7 +181,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> { if (!uri) { return; } - let task = createTask('install', 'install', selection.folder.workspaceFolder, uri, undefined, []); + let task = await createTask('install', 'install', selection.folder.workspaceFolder, uri, undefined, []); tasks.executeTask(task); } @@ -231,7 +203,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> { public refresh() { this.taskTree = null; - this._onDidChangeTreeData.fire(); + this._onDidChangeTreeData.fire(null); } getTreeItem(element: TreeItem): TreeItem { @@ -260,7 +232,11 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> { if (taskItems) { this.taskTree = this.buildTaskTree(taskItems); if (this.taskTree.length === 0) { - this.taskTree = [new NoScripts()]; + let message = localize('noScripts', 'No scripts found.'); + if (!isAutoDetectionEnabled()) { + message = localize('autoDetectIsOff', 'The setting "npm.autoDetect" is "off".'); + } + this.taskTree = [new NoScripts(message)]; } } } diff --git a/extensions/npm/src/preferred-pm.ts b/extensions/npm/src/preferred-pm.ts new file mode 100644 index 00000000000..351fb1b9afc --- /dev/null +++ b/extensions/npm/src/preferred-pm.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import findWorkspaceRoot = require('../node_modules/find-yarn-workspace-root'); +import findUp = require('find-up'); +import * as path from 'path'; +import whichPM = require('which-pm'); +import { Uri, workspace } from 'vscode'; + +async function pathExists(filePath: string) { + try { + await workspace.fs.stat(Uri.file(filePath)); + } catch { + return false; + } + return true; +} + +async function isPNPMPreferred(pkgPath: string) { + if (await pathExists(path.join(pkgPath, 'pnpm-lock.yaml'))) { + return true; + } + if (await pathExists(path.join(pkgPath, 'shrinkwrap.yaml'))) { + return true; + } + if (await findUp('pnpm-lock.yaml', { cwd: pkgPath })) { + return true; + } + + return false; +} + +async function isYarnPreferred(pkgPath: string) { + if (await pathExists(path.join(pkgPath, 'yarn.lock'))) { + return true; + } + + try { + if (typeof findWorkspaceRoot(pkgPath) === 'string') { + return true; + } + } catch (err) { } + + return false; +} + +const isNPMPreferred = (pkgPath: string) => { + return pathExists(path.join(pkgPath, 'package-lock.json')); +}; + +export async function findPreferredPM(pkgPath: string): Promise<{ name: string, multiplePMDetected: boolean }> { + const detectedPackageManagers = []; + + if (await isNPMPreferred(pkgPath)) { + detectedPackageManagers.push('npm'); + } + + if (await isYarnPreferred(pkgPath)) { + detectedPackageManagers.push('yarn'); + } + + if (await isPNPMPreferred(pkgPath)) { + detectedPackageManagers.push('pnpm'); + } + + const { name: pmUsedForInstallation } = await whichPM(pkgPath); + + if (!detectedPackageManagers.includes(pmUsedForInstallation)) { + detectedPackageManagers.push(pmUsedForInstallation); + } + + const multiplePMDetected = detectedPackageManagers.length > 1; + + return { + name: detectedPackageManagers[0] || 'npm', + multiplePMDetected + }; +} diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index 46992af2088..01c0c4b8c63 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -8,9 +8,10 @@ import { workspace, tasks, Range, HoverProvider, Hover, Position, MarkdownString, Uri } from 'vscode'; import { - createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript + createTask, startDebugging, findAllScriptRanges } from './tasks'; import * as nls from 'vscode-nls'; +import { dirname } from 'path'; const localize = nls.loadMessageBundle(); @@ -32,6 +33,9 @@ export class NpmScriptHoverProvider implements HoverProvider { constructor(context: ExtensionContext) { context.subscriptions.push(commands.registerCommand('npm.runScriptFromHover', this.runScriptFromHover, this)); context.subscriptions.push(commands.registerCommand('npm.debugScriptFromHover', this.debugScriptFromHover, this)); + context.subscriptions.push(workspace.onDidChangeTextDocument((e) => { + invalidateHoverScriptsCache(e.document); + })); } public provideHover(document: TextDocument, position: Position, _token: CancellationToken): ProviderResult<Hover> { @@ -51,11 +55,7 @@ export class NpmScriptHoverProvider implements HoverProvider { let contents: MarkdownString = new MarkdownString(); contents.isTrusted = true; contents.appendMarkdown(this.createRunScriptMarkdown(key, document.uri)); - - let debugArgs = extractDebugArgFromScript(value[2]); - if (debugArgs) { - contents.appendMarkdown(this.createDebugScriptMarkdown(key, document.uri, debugArgs[0], debugArgs[1])); - } + contents.appendMarkdown(this.createDebugScriptMarkdown(key, document.uri)); hover = new Hover(contents); } }); @@ -75,12 +75,10 @@ export class NpmScriptHoverProvider implements HoverProvider { ); } - private createDebugScriptMarkdown(script: string, documentUri: Uri, protocol: string, port: number): string { - let args = { + private createDebugScriptMarkdown(script: string, documentUri: Uri): string { + const args = { documentUri: documentUri, script: script, - protocol: protocol, - port: port }; return this.createMarkdownLink( localize('debugScript', 'Debug Script'), @@ -100,24 +98,22 @@ export class NpmScriptHoverProvider implements HoverProvider { return `${prefix}[${label}](command:${cmd}?${encodedArgs} "${tooltip}")`; } - public runScriptFromHover(args: any) { + public async runScriptFromHover(args: any) { let script = args.script; let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - let task = createTask(script, `run ${script}`, folder, documentUri); - tasks.executeTask(task); + let task = await createTask(script, `run ${script}`, folder, documentUri); + await tasks.executeTask(task); } } - public debugScriptFromHover(args: any) { + public debugScriptFromHover(args: { script: string; documentUri: Uri }) { let script = args.script; let documentUri = args.documentUri; - let protocol = args.protocol; - let port = args.port; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - startDebugging(script, protocol, port, folder); + startDebugging(script, dirname(documentUri.fsPath), folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index b9cedcc9a33..eef43a19d2f 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { - TaskDefinition, Task2 as Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, - DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem + TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, + DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem, window } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as minimatch from 'minimatch'; import * as nls from 'vscode-nls'; import { JSONVisitor, visit, ParseErrorCode } from 'jsonc-parser'; +import { findPreferredPM } from './preferred-pm'; const localize = nls.loadMessageBundle(); @@ -29,6 +30,8 @@ type AutoDetect = 'on' | 'off'; let cachedTasks: Task[] | undefined = undefined; +const INSTALL_SCRIPT = 'install'; + export class NpmTaskProvider implements TaskProvider { constructor() { @@ -38,7 +41,7 @@ export class NpmTaskProvider implements TaskProvider { return provideNpmScripts(); } - public resolveTask(_task: Task): Task | undefined { + public resolveTask(_task: Task): Promise<Task> | undefined { const npmTask = (<any>_task.definition).script; if (npmTask) { const kind: NpmTaskDefinition = (<any>_task.definition); @@ -52,7 +55,7 @@ export class NpmTaskProvider implements TaskProvider { } else { packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' }); } - return createTask(kind, `run ${kind.script}`, _task.scope, packageJsonUri); + return createTask(kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); } return undefined; } @@ -105,8 +108,20 @@ export function isWorkspaceFolder(value: any): value is WorkspaceFolder { return value && typeof value !== 'number'; } -export function getPackageManager(folder: WorkspaceFolder): string { - return workspace.getConfiguration('npm', folder.uri).get<string>('packageManager', 'npm'); +export async function getPackageManager(folder: Uri): Promise<string> { + let packageManagerName = workspace.getConfiguration('npm', folder).get<string>('packageManager', 'npm'); + + if (packageManagerName === 'auto') { + const { name, multiplePMDetected } = await findPreferredPM(folder.fsPath); + packageManagerName = name; + + if (multiplePMDetected) { + const multiplePMWarning = localize('npm.multiplePMWarning', 'Found multiple lockfiles for {0}. Using {1} as the preferred package manager.', folder.fsPath, packageManagerName); + window.showWarningMessage(multiplePMWarning); + } + } + + return packageManagerName; } export async function hasNpmScripts(): Promise<boolean> { @@ -190,8 +205,8 @@ export async function provideNpmScripts(): Promise<Task[]> { return cachedTasks; } -function isAutoDetectionEnabled(folder: WorkspaceFolder): boolean { - return workspace.getConfiguration('npm', folder.uri).get<AutoDetect>('autoDetect') === 'on'; +export function isAutoDetectionEnabled(folder?: WorkspaceFolder): boolean { + return workspace.getConfiguration('npm', folder?.uri).get<AutoDetect>('autoDetect') === 'on'; } function isExcluded(folder: WorkspaceFolder, packageJsonUri: Uri) { @@ -236,8 +251,9 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise<Task[]> const result: Task[] = []; const prePostScripts = getPrePostScripts(scripts); - Object.keys(scripts).forEach(each => { - const task = createTask(each, `run ${each}`, folder!, packageJsonUri, scripts![each]); + + for (const each of Object.keys(scripts)) { + const task = await createTask(each, `run ${each}`, folder!, packageJsonUri, scripts![each]); const lowerCaseTaskName = each.toLowerCase(); if (isBuildTask(lowerCaseTaskName)) { task.group = TaskGroup.Build; @@ -247,13 +263,16 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise<Task[]> if (prePostScripts.has(each)) { task.group = TaskGroup.Clean; // hack: use Clean group to tag pre/post scripts } + + // todo@connor4312: all scripts are now debuggable, what is a 'debug script'? if (isDebugScript(scripts![each])) { task.group = TaskGroup.Rebuild; // hack: use Rebuild group to tag debug scripts } result.push(task); - }); + } + // always add npm install (without a problem matcher) - result.push(createTask('install', 'install', folder, packageJsonUri, 'install dependencies from package', [])); + result.push(await createTask(INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', [])); return result; } @@ -264,7 +283,7 @@ export function getTaskName(script: string, relativePath: string | undefined) { return script; } -export function createTask(script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Task { +export async function createTask(script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Promise<Task> { let kind: NpmTaskDefinition; if (typeof script === 'string') { kind = { type: 'npm', script: script }; @@ -272,27 +291,27 @@ export function createTask(script: NpmTaskDefinition | string, cmd: string, fold kind = script; } - function getCommandLine(folder: WorkspaceFolder, cmd: string): string { - let packageManager = getPackageManager(folder); + const packageManager = await getPackageManager(folder.uri); + async function getCommandLine(cmd: string): Promise<string> { if (workspace.getConfiguration('npm', folder.uri).get<boolean>('runSilent')) { return `${packageManager} --silent ${cmd}`; } return `${packageManager} ${cmd}`; } - function getRelativePath(folder: WorkspaceFolder, packageJsonUri: Uri): string { + function getRelativePath(packageJsonUri: Uri): string { let rootUri = folder.uri; let absolutePath = packageJsonUri.path.substring(0, packageJsonUri.path.length - 'package.json'.length); return absolutePath.substring(rootUri.path.length + 1); } - let relativePackageJson = getRelativePath(folder, packageJsonUri); + let relativePackageJson = getRelativePath(packageJsonUri); if (relativePackageJson.length) { - kind.path = getRelativePath(folder, packageJsonUri); + kind.path = relativePackageJson; } let taskName = getTaskName(kind.script, relativePackageJson); let cwd = path.dirname(packageJsonUri.fsPath); - const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(getCommandLine(folder, cmd), { cwd: cwd }), matcher); + const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(await getCommandLine(cmd), { cwd: cwd }), matcher); task.detail = detail; return task; } @@ -344,53 +363,26 @@ async function readFile(file: string): Promise<string> { }); } -export function runScript(script: string, document: TextDocument) { +export async function runScript(script: string, document: TextDocument) { let uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); if (folder) { - let task = createTask(script, `run ${script}`, folder, uri); + let task = await createTask(script, `run ${script}`, folder, uri); tasks.executeTask(task); } } -export function extractDebugArgFromScript(scriptValue: string): [string, number] | undefined { - // matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, - // --inspect=1234, --inspect-brk, --inspect-brk=1234, - // --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234 - let match = scriptValue.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); - - if (match) { - if (match[6]) { - return [match[1], parseInt(match[6])]; - } - if (match[1] === 'inspect') { - return [match[1], 9229]; - } - if (match[1] === 'debug') { - return [match[1], 5858]; - } - } - return undefined; -} - -export function startDebugging(scriptName: string, protocol: string, port: number, folder: WorkspaceFolder) { - let p = 'inspector'; - if (protocol === 'debug') { - p = 'legacy'; - } - - let packageManager = getPackageManager(folder); +export async function startDebugging(scriptName: string, cwd: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { - type: 'node', + type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, - runtimeExecutable: packageManager, + cwd, + runtimeExecutable: await getPackageManager(folder.uri), runtimeArgs: [ 'run', scriptName, ], - port: port, - protocol: p }; if (folder) { diff --git a/extensions/npm/tsconfig.json b/extensions/npm/tsconfig.json index 296ddb38fcb..a50348dc223 100644 --- a/extensions/npm/tsconfig.json +++ b/extensions/npm/tsconfig.json @@ -6,4 +6,4 @@ "include": [ "src/**/*" ] -} \ No newline at end of file +} diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 4c6c723c3cb..0d0de5ef814 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -26,6 +26,13 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -39,6 +46,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -63,6 +77,38 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + +graceful-fs@^4.1.5: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -71,7 +117,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -https-proxy-agent@^2.2.3: +https-proxy-agent@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -79,10 +125,48 @@ https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" -jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +js-yaml@^3.13.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== + +load-yaml-file@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" + integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.13.0" + pify "^4.0.1" + strip-bom "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" minimatch@^3.0.4: version "3.0.4" @@ -96,21 +180,80 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -request-light@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746" - integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw== +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +picomatch@^2.0.5: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +request-light@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9" + integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA== dependencies: http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.3" - vscode-nls "^4.1.1" + https-proxy-agent "^2.2.4" + vscode-nls "^4.1.2" -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + +vscode-nls@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== + +which-pm@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" + integrity sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== + dependencies: + load-yaml-file "^0.2.0" + path-exists "^4.0.0" diff --git a/extensions/objective-c/cgmanifest.json b/extensions/objective-c/cgmanifest.json index 9ff092c2ab0..592ff960fb6 100644 --- a/extensions/objective-c/cgmanifest.json +++ b/extensions/objective-c/cgmanifest.json @@ -15,4 +15,4 @@ } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/objective-c/syntaxes/objective-c++.tmLanguage.json b/extensions/objective-c/syntaxes/objective-c++.tmLanguage.json index 2510f2dc743..473fd1e28b4 100644 --- a/extensions/objective-c/syntaxes/objective-c++.tmLanguage.json +++ b/extensions/objective-c/syntaxes/objective-c++.tmLanguage.json @@ -7095,4 +7095,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/objective-c/syntaxes/objective-c.tmLanguage.json b/extensions/objective-c/syntaxes/objective-c.tmLanguage.json index c4f19d8db54..63ae3d9970d 100644 --- a/extensions/objective-c/syntaxes/objective-c.tmLanguage.json +++ b/extensions/objective-c/syntaxes/objective-c.tmLanguage.json @@ -3603,4 +3603,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/package.json b/extensions/package.json index f2f1225c829..b3469e53913 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.7.5" + "typescript": "4.0.3" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/perl/test/colorize-results/test2_pl.json b/extensions/perl/test/colorize-results/test2_pl.json index 99e22463982..6fc00b0b064 100644 --- a/extensions/perl/test/colorize-results/test2_pl.json +++ b/extensions/perl/test/colorize-results/test2_pl.json @@ -1302,7 +1302,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -1324,7 +1324,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -1346,7 +1346,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -1775,7 +1775,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" diff --git a/extensions/perl/test/colorize-results/test_pl.json b/extensions/perl/test/colorize-results/test_pl.json index ecfd9660ad8..e47c0f95e84 100644 --- a/extensions/perl/test/colorize-results/test_pl.json +++ b/extensions/perl/test/colorize-results/test_pl.json @@ -389,7 +389,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -411,7 +411,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -433,7 +433,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -455,7 +455,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -477,7 +477,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -675,7 +675,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -950,7 +950,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -1445,7 +1445,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -2281,7 +2281,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index d301ca476ea..2856782f547 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -39,7 +39,7 @@ ], "default": null, "description": "%configuration.validate.executablePath%", - "scope": "machine" + "scope": "machine-overridable" }, "php.validate.run": { "type": "string", diff --git a/extensions/php-language-features/src/features/utils/async.ts b/extensions/php-language-features/src/features/utils/async.ts index f590e7d0014..866118beac0 100644 --- a/extensions/php-language-features/src/features/utils/async.ts +++ b/extensions/php-language-features/src/features/utils/async.ts @@ -105,7 +105,7 @@ export class Delayer<T> { public defaultDelay: number; private timeout: NodeJS.Timer | null; private completionPromise: Promise<T> | null; - private onResolve: ((value: T | Thenable<T> | undefined) => void) | null; + private onResolve: ((value: T | PromiseLike<T> | undefined) => void) | null; private task: ITask<T> | null; constructor(defaultDelay: number) { @@ -121,7 +121,7 @@ export class Delayer<T> { this.cancelTimeout(); if (!this.completionPromise) { - this.completionPromise = new Promise<T>((resolve) => { + this.completionPromise = new Promise<T | undefined>((resolve) => { this.onResolve = resolve; }).then(() => { this.completionPromise = null; @@ -182,4 +182,4 @@ export class ThrottledDelayer<T> extends Delayer<Promise<T>> { public trigger(promiseFactory: ITask<Promise<T>>, delay?: number): Promise<Promise<T>> { return super.trigger(() => this.throttler.queue(promiseFactory), delay); } -} \ No newline at end of file +} diff --git a/extensions/php-language-features/src/features/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index 122c108ac37..5c9d34afb59 100644 --- a/extensions/php-language-features/src/features/validationProvider.ts +++ b/extensions/php-language-features/src/features/validationProvider.ts @@ -247,7 +247,7 @@ export default class PHPValidationProvider { } }; - let options = vscode.workspace.rootPath ? { cwd: vscode.workspace.rootPath } : undefined; + let options = (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]) ? { cwd: vscode.workspace.workspaceFolders[0].uri.fsPath } : undefined; let args: string[]; if (this.trigger === RunTrigger.onSave) { args = PHPValidationProvider.FileArgs.slice(0); diff --git a/extensions/php/build/update-grammar.js b/extensions/php/build/update-grammar.js index 9de0d054233..18b4b33a854 100644 --- a/extensions/php/build/update-grammar.js +++ b/extensions/php/build/update-grammar.js @@ -29,8 +29,8 @@ function includeDerivativeHtml(grammar) { }); } -// Workaround for https://github.com/Microsoft/vscode/issues/40279 -// and https://github.com/Microsoft/vscode-textmate/issues/59 +// Workaround for https://github.com/microsoft/vscode/issues/40279 +// and https://github.com/microsoft/vscode-textmate/issues/59 function fixBadRegex(grammar) { function fail(msg) { throw new Error(`fixBadRegex callback couldn't patch ${msg}. It may be obsolete`); diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json index bd31a070acd..3c5b9b9ffd3 100644 --- a/extensions/php/cgmanifest.json +++ b/extensions/php/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "language-php", "repositoryUrl": "https://github.com/atom/language-php", - "commitHash": "b95dc79f30084c25547397ab41388af154e69895" + "commitHash": "6c0da475f86b45ea990638525f68be7658986546" } }, "license": "MIT", - "version": "0.44.3" + "version": "0.44.5" } ], "version": 1 diff --git a/extensions/php/package.json b/extensions/php/package.json index 2d0e4204ec1..0bd740a10ad 100644 --- a/extensions/php/package.json +++ b/extensions/php/package.json @@ -54,7 +54,7 @@ "snippets": [ { "language": "php", - "path": "./snippets/php.snippets.json" + "path": "./snippets/php.code-snippets" } ] }, diff --git a/extensions/php/snippets/php.snippets.json b/extensions/php/snippets/php.code-snippets similarity index 99% rename from extensions/php/snippets/php.snippets.json rename to extensions/php/snippets/php.code-snippets index a0705e3aed4..b2e9078d91f 100644 --- a/extensions/php/snippets/php.snippets.json +++ b/extensions/php/snippets/php.code-snippets @@ -260,4 +260,4 @@ ], "description": "Try catch block" } -} \ No newline at end of file +} diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index fef8a2da2a6..54fbd4b7869 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-php/commit/b95dc79f30084c25547397ab41388af154e69895", + "version": "https://github.com/atom/language-php/commit/6c0da475f86b45ea990638525f68be7658986546", "scopeName": "source.php", "patterns": [ { @@ -146,7 +146,7 @@ "name": "keyword.other.use.php" } }, - "end": "(?<=})|(?=;)", + "end": "(?<=})|(?=;)|(?=\\?>)", "name": "meta.use.php", "patterns": [ { @@ -230,7 +230,7 @@ ] }, { - "begin": "(?i)(?:^|(?<=}))\\s*(?:(abstract|final)\\s+)?(class)\\s+([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*)", + "begin": "(?ix)\n(?:\n (?:^|(?<=}))\\s*(?:(abstract|final)\\s+)?(class)\\s+([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*)\n |\\b(new)\\s+(class)\\b # anonymous class\n)", "beginCaptures": { "1": { "name": "storage.modifier.${1:/downcase}.php" @@ -240,6 +240,12 @@ }, "3": { "name": "entity.name.type.class.php" + }, + "4": { + "name": "keyword.other.new.php" + }, + "5": { + "name": "storage.type.class.php" } }, "end": "}|(?=\\?>)", @@ -525,7 +531,61 @@ ] }, { - "begin": "(?x)\n((?:(?:final|abstract|public|private|protected|static)\\s+)*)\n(function)\\s+\n(?i:\n (__(?:call|construct|debugInfo|destruct|get|set|isset|unset|toString|\n clone|set_state|sleep|wakeup|autoload|invoke|callStatic))\n |(?:(&)?\\s*([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*))\n)\n\\s*(\\()", + "begin": "(?i)\\b(fn)\\s*(?=&?\\s*\\()", + "beginCaptures": { + "1": { + "name": "storage.type.function.php" + } + }, + "end": "=>", + "endCaptures": { + "0": { + "name": "punctuation.definition.arrow.php" + } + }, + "name": "meta.function.closure.php", + "patterns": [ + { + "begin": "(&)?\\s*(\\()", + "beginCaptures": { + "1": { + "name": "storage.modifier.reference.php" + }, + "2": { + "name": "punctuation.definition.parameters.begin.bracket.round.php" + } + }, + "contentName": "meta.function.parameters.php", + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.end.bracket.round.php" + } + }, + "patterns": [ + { + "include": "#function-parameters" + } + ] + }, + { + "match": "(:)\\s*(\\?)?\\s*((?:\\\\?[a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)+)\\s*", + "captures": { + "1": { + "name": "keyword.operator.return-value.php" + }, + "2": { + "name": "keyword.operator.nullable-type.php" + }, + "3": { + "name": "storage.type.php" + } + } + } + ] + }, + { + "begin": "(?x)\n((?:(?:final|abstract|public|private|protected|static)\\s+)*)\n(function)\\s+\n(?i:\n (__(?:call|construct|debugInfo|destruct|get|set|isset|unset|toString|\n clone|set_state|sleep|wakeup|autoload|invoke|callStatic|serialize|unserialize))\n |(?:(&)?\\s*([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*))\n)\n\\s*(\\()", "beginCaptures": { "1": { "patterns": [ @@ -574,6 +634,44 @@ } ] }, + { + "match": "(?xi)\n((?:(?:public|private|protected|static)(?:\\s+|(?=\\?)))+) # At least one modifier\n(?:(\\?)?\\s* # Optional nullable\n (\\\\?(?:[a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*\\\\)*) # Optional namespace\n ([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*))? # Typehinted class name\n\\s+((\\$)[a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*) # Variable name", + "captures": { + "1": { + "patterns": [ + { + "match": "public|private|protected|static", + "name": "storage.modifier.php" + } + ] + }, + "2": { + "name": "keyword.operator.nullable-type.php" + }, + "3": { + "name": "support.other.namespace.php", + "patterns": [ + { + "match": "(?i)[a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*", + "name": "storage.type.php" + }, + { + "match": "\\\\", + "name": "punctuation.separator.inheritance.php" + } + ] + }, + "4": { + "name": "storage.type.php" + }, + "5": { + "name": "variable.other.php" + }, + "6": { + "name": "punctuation.definition.variable.php" + } + } + }, { "include": "#invoke-call" }, @@ -667,6 +765,10 @@ "match": "(?i)\\bclone\\b", "name": "keyword.other.clone.php" }, + { + "match": "\\.\\.\\.", + "name": "keyword.operator.spread.php" + }, { "match": "\\.=?", "name": "keyword.operator.string.php" @@ -698,7 +800,7 @@ "name": "keyword.operator.comparison.php" }, { - "match": "=|\\+=|\\-=|\\*=|/=|%=|&=|\\|=|\\^=|<<=|>>=", + "match": "=|\\+=|\\-=|\\*\\*?=|/=|%=|&=|\\|=|\\^=|<<=|>>=|\\?\\?=", "name": "keyword.operator.assignment.php" }, { @@ -710,7 +812,7 @@ "name": "keyword.operator.increment-decrement.php" }, { - "match": "\\-|\\+|\\*|/|%", + "match": "\\-|\\+|\\*\\*?|/|%", "name": "keyword.operator.arithmetic.php" }, { @@ -731,7 +833,7 @@ "name": "keyword.operator.type.php" } }, - "end": "(?=[^\\\\$a-z0-9_\\x{7f}-\\x{7fffffff}])", + "end": "(?i)(?=[^\\\\$a-z0-9_\\x{7f}-\\x{7fffffff}])", "patterns": [ { "include": "#class-name" @@ -1227,6 +1329,17 @@ } ] }, + "named-arguments": { + "match": "(?i)(?<=^|\\(|,)\\s*([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*)\\s*(:)(?!:)", + "captures": { + "1": { + "name": "entity.name.variable.parameter.php" + }, + "2": { + "name": "punctuation.separator.colon.php" + } + } + }, "function-call": { "patterns": [ { @@ -1255,6 +1368,9 @@ }, "name": "meta.function-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -1293,6 +1409,9 @@ }, "name": "meta.function-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -1959,7 +2078,7 @@ ] }, "instantiation": { - "begin": "(?i)(new)\\s+", + "begin": "(?i)(new)\\s+(?!class\\b)", "beginCaptures": { "1": { "name": "keyword.other.new.php" @@ -2066,19 +2185,19 @@ "numbers": { "patterns": [ { - "match": "0[xX][0-9a-fA-F]+", + "match": "0[xX][0-9a-fA-F]+(?:_[0-9a-fA-F]+)*", "name": "constant.numeric.hex.php" }, { - "match": "0[bB][01]+", + "match": "0[bB][01]+(?:_[01]+)*", "name": "constant.numeric.binary.php" }, { - "match": "0[0-7]+", + "match": "0(?:_?[0-7]+)+", "name": "constant.numeric.octal.php" }, { - "match": "(?x)\n(?:\n [0-9]*(\\.)[0-9]+(?:[eE][+-]?[0-9]+)?|\n [0-9]+(\\.)[0-9]*(?:[eE][+-]?[0-9]+)?|\n [0-9]+[eE][+-]?[0-9]+\n)", + "match": "(?x)\n(?:\n (?:[0-9]+(?:_[0-9]+)*)?(\\.)[0-9]+(?:_[0-9]+)*(?:[eE][+-]?[0-9]+(?:_[0-9]+)*)?| # .3\n [0-9]+(?:_[0-9]+)*(\\.)(?:[0-9]+(?:_[0-9]+)*)?(?:[eE][+-]?[0-9]+(?:_[0-9]+)*)?| # 3.\n [0-9]+(?:_[0-9]+)*[eE][+-]?[0-9]+(?:_[0-9]+)* # 2e-3\n)", "name": "constant.numeric.decimal.php", "captures": { "1": { @@ -2090,7 +2209,7 @@ } }, { - "match": "0|[1-9][0-9]*", + "match": "0|[1-9](?:_?[0-9]+)*", "name": "constant.numeric.decimal.php" } ] @@ -2140,6 +2259,9 @@ }, "name": "meta.method-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -2512,6 +2634,9 @@ }, "name": "meta.method-call.static.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -3345,6 +3470,18 @@ } }, "patterns": [ + { + "match": "(?i)^\\s*([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*)\\s*(?=:(?!:))", + "captures": { + "1": { + "patterns": [ + { + "include": "$self" + } + ] + } + } + }, { "include": "$self" } diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index 27983076fa7..eb36ede3a4e 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -279,7 +279,7 @@ "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -378,7 +378,7 @@ "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" diff --git a/extensions/postinstall.js b/extensions/postinstall.js index e31009ac6b8..da4fa3e9d04 100644 --- a/extensions/postinstall.js +++ b/extensions/postinstall.js @@ -2,29 +2,58 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// @ts-check 'use strict'; const fs = require('fs'); const path = require('path'); -const toDelete = new Set(['tsc.js', 'tsserverlibrary.js', 'typescriptServices.js']); +const rimraf = require('rimraf'); -const root = path.join(__dirname, 'node_modules', 'typescript', 'lib'); -for (let name of fs.readdirSync(root)) { - if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') { - continue; - } - if (name === 'typescript.js' || name === 'typescript.d.ts') { - // used by html and extension editing - continue; - } +const root = path.join(__dirname, 'node_modules', 'typescript'); - if (toDelete.has(name) || name.match(/\.d\.ts$/)) { - try { - fs.unlinkSync(path.join(root, name)); - console.log(`removed '${path.join(root, name)}'`); - } catch (e) { - console.warn(e); +function processRoot() { + const toKeep = new Set([ + 'lib', + 'package.json', + ]); + for (const name of fs.readdirSync(root)) { + if (!toKeep.has(name)) { + const filePath = path.join(root, name); + console.log(`Removed ${filePath}`); + rimraf.sync(filePath); } } -} \ No newline at end of file +} + +function processLib() { + const toDelete = new Set([ + 'tsc.js', + 'tsserverlibrary.js', + 'typescriptServices.js', + ]); + + const libRoot = path.join(root, 'lib'); + + for (const name of fs.readdirSync(libRoot)) { + if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') { + continue; + } + if (name === 'typescript.js' || name === 'typescript.d.ts') { + // used by html and extension editing + continue; + } + + if (toDelete.has(name) || name.match(/\.d\.ts$/)) { + try { + fs.unlinkSync(path.join(libRoot, name)); + console.log(`removed '${path.join(libRoot, name)}'`); + } catch (e) { + console.warn(e); + } + } + } +} + +processRoot(); +processLib(); diff --git a/extensions/powershell/language-configuration.json b/extensions/powershell/language-configuration.json index f30c0f5f051..719b5f81b52 100644 --- a/extensions/powershell/language-configuration.json +++ b/extensions/powershell/language-configuration.json @@ -12,6 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], + { "open": "@'", "close": "\n'@", "notIn": ["string", "comment"]}, + { "open": "@\"", "close": "\n\"@", "notIn": ["string", "comment"]}, { "open": "\"", "close": "\"", "notIn": ["string"]}, { "open": "'", "close": "'", "notIn": ["string", "comment"]}, ["<#", "#>"] @@ -29,4 +31,4 @@ "end": "^\\s*#[eE]nd[rR]egion\\b" } } -} \ No newline at end of file +} diff --git a/extensions/powershell/package.json b/extensions/powershell/package.json index 232acb1a7d2..fb45b704b85 100644 --- a/extensions/powershell/package.json +++ b/extensions/powershell/package.json @@ -21,7 +21,7 @@ }], "snippets": [{ "language": "powershell", - "path": "./snippets/powershell.json" + "path": "./snippets/powershell.code-snippets" }] }, "scripts": { diff --git a/extensions/powershell/snippets/powershell.json b/extensions/powershell/snippets/powershell.code-snippets similarity index 100% rename from extensions/powershell/snippets/powershell.json rename to extensions/powershell/snippets/powershell.code-snippets diff --git a/extensions/pug/package.json b/extensions/pug/package.json index 19049be77af..7565f0a0119 100644 --- a/extensions/pug/package.json +++ b/extensions/pug/package.json @@ -12,7 +12,7 @@ "contributes": { "languages": [{ "id": "jade", - "extensions": [ ".jade", ".pug" ], + "extensions": [ ".pug", ".jade" ], "aliases": [ "Pug", "Jade", "jade" ], "configuration": "./language-configuration.json" }], @@ -22,4 +22,4 @@ "path": "./syntaxes/pug.tmLanguage.json" }] } -} \ No newline at end of file +} diff --git a/extensions/python/.vscodeignore b/extensions/python/.vscodeignore index 4d5a14fc91e..b5c95d0fb64 100644 --- a/extensions/python/.vscodeignore +++ b/extensions/python/.vscodeignore @@ -1,6 +1,8 @@ test/** src/** +out/** tsconfig.json extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json -.vscode \ No newline at end of file +.vscode diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json index 3ee82895a82..37a21b2de54 100644 --- a/extensions/python/cgmanifest.json +++ b/extensions/python/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "MagicStack/MagicPython", "repositoryUrl": "https://github.com/MagicStack/MagicPython", - "commitHash": "c0f8d514bbe6e9d3899f2b002bcd6971aef5e34b" + "commitHash": "c9b3409deb69acec31bbf7913830e93a046b30cc" } }, "license": "MIT", diff --git a/extensions/npm/src/features/markedTextUtil.ts b/extensions/python/extension-browser.webpack.config.js similarity index 58% rename from extensions/npm/src/features/markedTextUtil.ts rename to extensions/python/extension-browser.webpack.config.js index 856fad050e5..9ffa8d167e9 100644 --- a/extensions/npm/src/features/markedTextUtil.ts +++ b/extensions/python/extension-browser.webpack.config.js @@ -3,8 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkedString } from 'vscode'; +//@ts-check -export function textToMarkedString(text: string): MarkedString { - return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash -} \ No newline at end of file +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/pythonMain.ts' + }, + output: { + filename: 'pythonMain.js' + } +}); diff --git a/extensions/python/package.json b/extensions/python/package.json index 1a7a65b2dee..f39c3d62b23 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -8,13 +8,14 @@ "engines": { "vscode": "*" }, "activationEvents": ["onLanguage:python"], "main": "./out/pythonMain", - "extensionKind": [ "ui", "workspace" ], + "browser": "./dist/browser/pythonMain", + "extensionKind": [ "ui", "workspace", "web" ], "contributes": { "languages": [{ "id": "python", "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".pyi", ".ipy"], "aliases": [ "Python", "py" ], - "filenames": [ "Snakefile" ], + "filenames": [ "Snakefile", "SConstruct", "SConscript" ], "firstLine": "^#!\\s*/?.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" }], diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index f59618f75ff..0df9076dfc9 100644 --- a/extensions/python/syntaxes/MagicPython.tmLanguage.json +++ b/extensions/python/syntaxes/MagicPython.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/MagicStack/MagicPython/commit/c0f8d514bbe6e9d3899f2b002bcd6971aef5e34b", + "version": "https://github.com/MagicStack/MagicPython/commit/b2b4f4ae7b4e6284e80bda8080106b93bd588f9e", "name": "MagicPython", "scopeName": "source.python", "patterns": [ @@ -31,6 +31,9 @@ { "include": "#function-declaration" }, + { + "include": "#generator" + }, { "include": "#statement-keyword" }, @@ -291,6 +294,9 @@ { "include": "#lambda" }, + { + "include": "#generator" + }, { "include": "#illegal-operator" }, @@ -306,6 +312,9 @@ { "include": "#list" }, + { + "include": "#odd-function-call" + }, { "include": "#round-braces" }, @@ -388,6 +397,9 @@ }, { "include": "#member-access-base" + }, + { + "include": "#member-access-attribute" } ] }, @@ -413,6 +425,11 @@ } ] }, + "member-access-attribute": { + "comment": "Highlight attribute access in otherwise non-specialized cases.", + "name": "meta.attribute.python", + "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n" + }, "special-names": { "name": "constant.other.caps.python", "match": "(?x)\n \\b\n # we want to see \"enough\", meaning 2 or more upper-case\n # letters in the beginning of the constant\n #\n # for more details refer to:\n # https://github.com/MagicStack/MagicPython/issues/42\n (\n _* [[:upper:]] [_\\d]* [[:upper:]]\n )\n [[:upper:]\\d]* (_\\w*)?\n \\b\n" @@ -459,6 +476,21 @@ } ] }, + "odd-function-call": { + "comment": "A bit obscured function call where there may have been an\narbitrary number of other operations to get the function.\nE.g. \"arr[idx](args)\"\n", + "begin": "(?x)\n (?<= \\] | \\) ) \\s*\n (?=\\()\n", + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.arguments.end.python" + } + }, + "patterns": [ + { + "include": "#function-arguments" + } + ] + }, "round-braces": { "begin": "\\(", "end": "\\)", @@ -602,9 +634,6 @@ }, "2": { "name": "invalid.illegal.dec.python" - }, - "3": { - "name": "invalid.illegal.dec.python" } } }, @@ -1195,6 +1224,26 @@ } ] }, + "generator": { + "comment": "Match \"for ... in\" construct used in generators and for loops to\ncorrectly identify the \"in\" as a control flow keyword.\n", + "begin": "\\bfor\\b", + "beginCaptures": { + "0": { + "name": "keyword.control.flow.python" + } + }, + "end": "\\bin\\b", + "endCaptures": { + "0": { + "name": "keyword.control.flow.python" + } + }, + "patterns": [ + { + "include": "#expression" + } + ] + }, "function-declaration": { "name": "meta.function.python", "begin": "(?x)\n \\s*\n (?:\\b(async) \\s+)? \\b(def)\\s+\n (?=\n [[:alpha:]_][[:word:]]* \\s* \\(\n )\n", @@ -1407,6 +1456,7 @@ "include": "#special-names" }, { + "name": "meta.indexed-name.python", "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n" } ] @@ -1524,6 +1574,7 @@ }, "function-call": { "name": "meta.function-call.python", + "comment": "Regular function call of the type \"name(args)\"", "begin": "(?x)\n \\b(?=\n ([[:alpha:]_]\\w*) \\s* (\\()\n )\n", "end": "(\\))", "endCaptures": { diff --git a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json index c7e67436a08..fc11fa5affe 100644 --- a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json +++ b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/MagicStack/MagicPython/commit/361a4964a559481330764a447e7bab88d4f1b01b", + "version": "https://github.com/MagicStack/MagicPython/commit/c9b3409deb69acec31bbf7913830e93a046b30cc", "name": "MagicRegExp", "scopeName": "source.regexp.python", "patterns": [ diff --git a/extensions/python/test/colorize-results/test-freeze-56377_py.json b/extensions/python/test/colorize-results/test-freeze-56377_py.json index 63889e26fee..3eb0f85a2a1 100644 --- a/extensions/python/test/colorize-results/test-freeze-56377_py.json +++ b/extensions/python/test/colorize-results/test-freeze-56377_py.json @@ -254,13 +254,13 @@ }, { "c": "in", - "t": "source.python keyword.operator.logical.python", + "t": "source.python keyword.control.flow.python", "r": { - "dark_plus": "keyword.operator.logical.python: #569CD6", - "light_plus": "keyword.operator.logical.python: #0000FF", - "dark_vs": "keyword.operator.logical.python: #569CD6", - "light_vs": "keyword.operator.logical.python: #0000FF", - "hc_black": "keyword.operator.logical.python: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -298,7 +298,7 @@ }, { "c": "request", - "t": "source.python meta.member.access.python", + "t": "source.python meta.member.access.python meta.attribute.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -565,7 +565,7 @@ "t": "source.python string.quoted.single.python constant.character.escape.python", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json index c61d2db7611..c0a14453646 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -430,7 +430,7 @@ }, { "c": "size", - "t": "source.python meta.member.access.python", + "t": "source.python meta.member.access.python meta.attribute.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -914,13 +914,13 @@ }, { "c": "in", - "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.logical.python", + "t": "source.python meta.function.python meta.function.parameters.python keyword.control.flow.python", "r": { - "dark_plus": "keyword.operator.logical.python: #569CD6", - "light_plus": "keyword.operator.logical.python: #0000FF", - "dark_vs": "keyword.operator.logical.python: #569CD6", - "light_vs": "keyword.operator.logical.python: #0000FF", - "hc_black": "keyword.operator.logical.python: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -2113,13 +2113,13 @@ }, { "c": "in", - "t": "source.python keyword.operator.logical.python", + "t": "source.python keyword.control.flow.python", "r": { - "dark_plus": "keyword.operator.logical.python: #569CD6", - "light_plus": "keyword.operator.logical.python: #0000FF", - "dark_vs": "keyword.operator.logical.python: #569CD6", - "light_vs": "keyword.operator.logical.python: #0000FF", - "hc_black": "keyword.operator.logical.python: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -4544,7 +4544,7 @@ }, { "c": "fn", - "t": "source.python meta.member.access.python", + "t": "source.python meta.member.access.python meta.attribute.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4621,7 +4621,7 @@ }, { "c": "memo", - "t": "source.python meta.member.access.python", + "t": "source.python meta.member.access.python meta.attribute.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4918,7 +4918,7 @@ }, { "c": "memo", - "t": "source.python meta.member.access.python", + "t": "source.python meta.member.access.python meta.attribute.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4973,7 +4973,7 @@ }, { "c": "memo", - "t": "source.python meta.member.access.python meta.item-access.python", + "t": "source.python meta.member.access.python meta.item-access.python meta.indexed-name.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5182,7 +5182,7 @@ }, { "c": "memo", - "t": "source.python meta.member.access.python meta.item-access.python", + "t": "source.python meta.member.access.python meta.item-access.python meta.indexed-name.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6797,4 +6797,4 @@ "hc_black": "string: #CE9178" } } -] +] \ No newline at end of file diff --git a/extensions/r/cgmanifest.json b/extensions/r/cgmanifest.json index 0781a150722..62f2751e6c9 100644 --- a/extensions/r/cgmanifest.json +++ b/extensions/r/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "Ikuyadeu/vscode-R", "repositoryUrl": "https://github.com/Ikuyadeu/vscode-R", - "commitHash": "1cd3d42a6b2e54276ef2d71fe33bb3fefb1d6cff" + "commitHash": "e03ba9cb9b19412f48c73ea73deb6746d50bbf23" } }, "license": "MIT", - "version": "0.5.5" + "version": "1.3.0" } ], "version": 1 diff --git a/extensions/r/syntaxes/r.tmLanguage.json b/extensions/r/syntaxes/r.tmLanguage.json index db37b8421fe..ad947f62849 100644 --- a/extensions/r/syntaxes/r.tmLanguage.json +++ b/extensions/r/syntaxes/r.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Ikuyadeu/vscode-R/commit/1cd3d42a6b2e54276ef2d71fe33bb3fefb1d6cff", + "version": "https://github.com/Ikuyadeu/vscode-R/commit/e03ba9cb9b19412f48c73ea73deb6746d50bbf23", "name": "R", "scopeName": "source.r", "patterns": [ @@ -168,20 +168,12 @@ "match": "(\\-|\\+|\\*|\\/|%\\/%|%%|%\\*%|%o%|%x%|\\^)", "name": "keyword.operator.arithmetic.r" }, - { - "match": "<=|>=", - "name": "keyword.operator.comparison.r" - }, - { - "match": "==", - "name": "keyword.operator.comarison.r" - }, { "match": "(:=|<-|<<-|->|->>)", "name": "keyword.operator.assignment.r" }, { - "match": "(!=|<>|<|>|%in%)", + "match": "(==|<=|>=|!=|<>|<|>|%in%)", "name": "keyword.operator.comparison.r" }, { @@ -417,7 +409,7 @@ "end": "(\\))", "endCaptures": { "1": { - "name": "punctuation.definition.parameters.r" + "name": "punctuation.section.parens.end.r" } }, "name": "meta.function-call.r", diff --git a/extensions/r/test/colorize-results/test_r.json b/extensions/r/test/colorize-results/test_r.json index 2d3e504f8de..3ecd0091ce5 100644 --- a/extensions/r/test/colorize-results/test_r.json +++ b/extensions/r/test/colorize-results/test_r.json @@ -639,7 +639,7 @@ }, { "c": ")", - "t": "source.r meta.function-call.r punctuation.definition.parameters.r", + "t": "source.r meta.function-call.r punctuation.section.parens.end.r", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -716,7 +716,7 @@ }, { "c": ")", - "t": "source.r meta.function-call.r punctuation.definition.parameters.r", + "t": "source.r meta.function-call.r punctuation.section.parens.end.r", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1044,4 +1044,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/ruby/test/colorize-results/test_rb.json b/extensions/ruby/test/colorize-results/test_rb.json index 66468920c99..c5122336ae1 100644 --- a/extensions/ruby/test/colorize-results/test_rb.json +++ b/extensions/ruby/test/colorize-results/test_rb.json @@ -432,8 +432,8 @@ "c": "Models", "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -476,8 +476,8 @@ "c": "MsRestAzure", "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -1191,8 +1191,8 @@ "c": "ArgumentError", "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -1345,8 +1345,8 @@ "c": "ArgumentError", "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -1499,8 +1499,8 @@ "c": "ServiceClientCredentials", "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -2269,8 +2269,8 @@ "c": "MAVERICKS_PKG_PATH", "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", "hc_black": "variable: #9CDCFE" @@ -2501,7 +2501,7 @@ "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -2523,7 +2523,7 @@ "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -2545,7 +2545,7 @@ "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -2578,7 +2578,7 @@ "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -2837,4 +2837,4 @@ "hc_black": "keyword.control: #C586C0" } } -] +] \ No newline at end of file diff --git a/extensions/rust/cgmanifest.json b/extensions/rust/cgmanifest.json index 5181efba1c4..ec6bab93af3 100644 --- a/extensions/rust/cgmanifest.json +++ b/extensions/rust/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "language-rust", - "repositoryUrl": "https://github.com/zargony/atom-language-rust", - "commitHash": "7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f" + "name": "rust-syntax", + "repositoryUrl": "https://github.com/dustypomerleau/rust-syntax", + "commitHash": "f3eb2221c8c334f1aae1fbc7af9a6c0b753bb29b" } }, "license": "MIT", - "description": "The files syntaxes/rust.tmLanguage.json was derived from the Atom package https://atom.io/packages/language-rust.", - "version": "0.4.12" + "description": "A TextMate-style grammar for Rust.", + "version": "0.2.10" } ], "version": 1 diff --git a/extensions/rust/package.json b/extensions/rust/package.json index 66078388175..431e9fa203c 100644 --- a/extensions/rust/package.json +++ b/extensions/rust/package.json @@ -5,21 +5,32 @@ "version": "1.0.0", "publisher": "vscode", "license": "MIT", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js zargony/atom-language-rust grammars/rust.cson ./syntaxes/rust.tmLanguage.json" + "update-grammar": "node ../../build/npm/update-grammar.js dustypomerleau/rust-syntax syntaxes/rust.tmLanguage.json ./syntaxes/rust.tmLanguage.json" }, "contributes": { - "languages": [{ - "id": "rust", - "extensions": [".rs"], - "aliases": ["Rust", "rust"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "rust", - "path": "./syntaxes/rust.tmLanguage.json", - "scopeName":"source.rust" - }] + "languages": [ + { + "id": "rust", + "extensions": [ + ".rs" + ], + "aliases": [ + "Rust", + "rust" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "rust", + "path": "./syntaxes/rust.tmLanguage.json", + "scopeName": "source.rust" + } + ] } -} \ No newline at end of file +} diff --git a/extensions/rust/syntaxes/rust.tmLanguage.json b/extensions/rust/syntaxes/rust.tmLanguage.json index 784bd8c9aca..f952d4d8399 100644 --- a/extensions/rust/syntaxes/rust.tmLanguage.json +++ b/extensions/rust/syntaxes/rust.tmLanguage.json @@ -1,690 +1,1069 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/zargony/atom-language-rust/blob/master/grammars/rust.cson", + "This file has been converted from https://github.com/dustypomerleau/rust-syntax/blob/master/syntaxes/rust.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/zargony/atom-language-rust/commit/7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f", + "version": "https://github.com/dustypomerleau/rust-syntax/commit/f3eb2221c8c334f1aae1fbc7af9a6c0b753bb29b", "name": "Rust", "scopeName": "source.rust", "patterns": [ { - "comment": "Implementation", - "begin": "\\b(impl)\\b", - "end": "\\{", + "comment": "boxed slice literal", + "begin": "(<)(\\[)", "beginCaptures": { "1": { - "name": "storage.type.rust" + "name": "punctuation.brackets.angle.rust" + }, + "2": { + "name": "punctuation.brackets.square.rust" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.brackets.angle.rust" } }, "patterns": [ { - "include": "#block_comment" + "include": "#block-comments" }, { - "include": "#line_comment" + "include": "#comments" }, { - "include": "#sigils" + "include": "#gtypes" }, { - "include": "#mut" + "include": "#lvariables" }, { - "include": "#dyn" + "include": "#lifetimes" }, { - "include": "#ref_lifetime" + "include": "#punctuation" }, { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#where" - }, - { - "name": "storage.type.rust", - "match": "\\bfor\\b" - }, - { - "include": "#type" + "include": "#types" } ] }, { - "include": "#block_doc_comment" - }, - { - "include": "#block_comment" - }, - { - "include": "#line_doc_comment" - }, - { - "include": "#line_comment" - }, - { - "comment": "Attribute", - "name": "meta.attribute.rust", - "begin": "#\\!?\\[", - "end": "\\]", - "patterns": [ - { - "include": "#string_literal" - }, - { - "include": "#block_doc_comment" - }, - { - "include": "#block_comment" - }, - { - "include": "#line_doc_comment" - }, - { - "include": "#line_comment" - } - ] - }, - { - "comment": "Single-quote string literal (character)", - "name": "string.quoted.single.rust", - "match": "b?'([^'\\\\]|\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.))'" - }, - { - "include": "#string_literal" - }, - { - "include": "#raw_string_literal" - }, - { - "comment": "Floating point literal (fraction)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*\\.[0-9][0-9_]*([eE][+-]?[0-9_]+)?(f32|f64)?\\b" - }, - { - "comment": "Floating point literal (exponent)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?[eE][+-]?[0-9_]+(f32|f64)?\\b" - }, - { - "comment": "Floating point literal (typed)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?([eE][+-]?[0-9_]+)?(f32|f64)\\b" - }, - { - "comment": "Integer literal (decimal)", - "name": "constant.numeric.integer.decimal.rust", - "match": "\\b[0-9][0-9_]*([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (hexadecimal)", - "name": "constant.numeric.integer.hexadecimal.rust", - "match": "\\b0x[a-fA-F0-9_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (octal)", - "name": "constant.numeric.integer.octal.rust", - "match": "\\b0o[0-7_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (binary)", - "name": "constant.numeric.integer.binary.rust", - "match": "\\b0b[01_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Static storage modifier", - "name": "storage.modifier.static.rust", - "match": "\\bstatic\\b" - }, - { - "comment": "Boolean constant", - "name": "constant.language.boolean.rust", - "match": "\\b(true|false)\\b" - }, - { - "comment": "Control keyword", - "name": "keyword.control.rust", - "match": "\\b(async|await|break|continue|else|if|in|for|loop|match|return|try|while)\\b" - }, - { - "comment": "Keyword", - "name": "keyword.other.rust", - "match": "\\b(crate|extern|mod|let|ref|use|super|move)\\b" - }, - { - "comment": "Reserved keyword", - "name": "invalid.deprecated.rust", - "match": "\\b(abstract|alignof|become|do|final|macro|offsetof|override|priv|proc|pure|sizeof|typeof|virtual|yield)\\b" - }, - { - "include": "#unsafe" - }, - { - "include": "#sigils" - }, - { - "include": "#self" - }, - { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#box" - }, - { - "include": "#lifetime" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#const" - }, - { - "include": "#pub" - }, - { - "comment": "Miscellaneous operator", - "name": "keyword.operator.misc.rust", - "match": "(=>|::|\\bas\\b)" - }, - { - "comment": "Comparison operator", - "name": "keyword.operator.comparison.rust", - "match": "(&&|\\|\\||==|!=)" - }, - { - "comment": "Assignment operator", - "name": "keyword.operator.assignment.rust", - "match": "(\\+=|-=|/=|\\*=|%=|\\^=|&=|\\|=|<<=|>>=|=)" - }, - { - "comment": "Arithmetic operator", - "name": "keyword.operator.arithmetic.rust", - "match": "(!|\\+|-|/|\\*|%|\\^|&|\\||<<|>>)" - }, - { - "comment": "Comparison operator (second group because of regex precedence)", - "name": "keyword.operator.comparison.rust", - "match": "(<=|>=|<|>)" - }, - { - "include": "#core_types" - }, - { - "include": "#core_vars" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "comment": "Built-in macro", - "name": "support.function.builtin.rust", - "match": "\\b(macro_rules|compile_error|format_args|env|option_env|concat_idents|concat|line|column|file|stringify|include|include_str|include_bytes|module_path|cfg)!" - }, - { - "comment": "Core macro", - "name": "support.function.core.rust", - "match": "\\b(panic|assert|assert_eq|assert_ne|debug_assert|debug_assert_eq|debug_assert_ne|try|write|writeln|unreachable|unimplemented)!" - }, - { - "comment": "Standard library macro", - "name": "support.function.std.rust", - "match": "\\b(format|print|println|eprint|eprintln|select|vec)!" - }, - { - "comment": "Logging macro", - "name": "support.function.log.rust", - "match": "\\b(log|error|warn|info|debug|trace|log_enabled)!" - }, - { - "comment": "Invokation of a macro", - "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]", + "comment": "macro type metavariables", + "name": "meta.macro.metavariable.type.rust", + "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", "captures": { "1": { + "name": "keyword.operator.macro.dollar.rust" + }, + "3": { + "name": "keyword.other.crate.rust" + }, + "4": { + "name": "entity.name.type.metavariable.rust" + }, + "6": { + "name": "keyword.operator.key-value.rust" + }, + "7": { + "name": "variable.other.metavariable.specifier.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + } + ] + }, + { + "comment": "macro metavariables", + "name": "meta.macro.metavariable.rust", + "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", + "captures": { + "1": { + "name": "keyword.operator.macro.dollar.rust" + }, + "2": { + "name": "variable.other.metavariable.name.rust" + }, + "4": { + "name": "keyword.operator.key-value.rust" + }, + "5": { + "name": "variable.other.metavariable.specifier.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + } + ] + }, + { + "comment": "macro rules", + "name": "meta.macro.rules.rust", + "match": "\\b(macro_rules!)\\s+(([a-z0-9_]+)|([A-Z][a-z0-9_]*))\\s+(\\{)", + "captures": { + "1": { + "name": "entity.name.function.macro.rules.rust" + }, + "3": { "name": "entity.name.function.macro.rust" + }, + "4": { + "name": "entity.name.type.macro.rust" + }, + "5": { + "name": "punctuation.brackets.curly.rust" } } }, { - "comment": "Function call", - "match": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*\\(", + "comment": "attributes", + "name": "meta.attribute.rust", + "begin": "(#)(\\!?)(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.attribute.rust" + }, + "2": { + "name": "keyword.operator.attribute.inner.rust" + }, + "3": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#gtypes" + }, + { + "include": "#types" + } + ] + }, + { + "comment": "modules", + "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", "captures": { "1": { - "name": "entity.name.function.rust" + "name": "keyword.control.rust" + }, + "2": { + "name": "entity.name.module.rust" } } }, { - "comment": "Function call with type parameters", - "begin": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*(::)(?=\\s*<.*>\\s*\\()", - "end": "\\(", - "captures": { - "1": { - "name": "entity.name.function.rust" - }, - "2": { - "name": "keyword.operator.misc.rust" - } - }, - "patterns": [ - { - "include": "#type_params" - } - ] - }, - { - "comment": "Function definition", - "begin": "\\b(fn)\\s+([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)", - "end": "[\\{;]", + "comment": "external crate imports", + "name": "meta.import.rust", + "begin": "\\b(extern)\\s+(crate)", "beginCaptures": { "1": { - "name": "keyword.other.fn.rust" + "name": "keyword.control.rust" }, "2": { - "name": "entity.name.function.rust" + "name": "keyword.other.crate.rust" } }, - "patterns": [ - { - "include": "#block_comment" - }, - { - "include": "#line_comment" - }, - { - "include": "#sigils" - }, - { - "include": "#self" - }, - { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#const" - }, - { - "include": "#where" - }, - { - "include": "#unsafe" - }, - { - "comment": "Function arguments", - "match": "\bfn\b", - "name": "keyword.other.fn.rust" - } - ] - }, - { - "comment": "Type declaration", - "begin": "\\b(enum|struct|trait|union)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", - "end": "[\\{\\(;]", - "beginCaptures": { - "1": { - "name": "storage.type.rust" - }, - "2": { - "name": "entity.name.type.rust" - } - }, - "patterns": [ - { - "include": "#block_comment" - }, - { - "include": "#line_comment" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#core_types" - }, - { - "include": "#pub" - }, - { - "include": "#where" - } - ] - }, - { - "comment": "Type alias", - "begin": "\\b(type)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", "end": ";", - "beginCaptures": { - "1": { - "name": "storage.type.rust" - }, - "2": { - "name": "entity.name.type.rust" + "endCaptures": { + "0": { + "name": "punctuation.semi.rust" } }, "patterns": [ { - "include": "#block_comment" + "include": "#block-comments" }, { - "include": "#line_comment" + "include": "#comments" }, { - "include": "#sigils" + "include": "#keywords" }, { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#lifetime" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" + "include": "#punctuation" } ] + }, + { + "comment": "use statements", + "name": "meta.use.rust", + "begin": "\\b(use)\\s", + "beginCaptures": { + "1": { + "name": "keyword.control.rust" + } + }, + "end": ";", + "endCaptures": { + "0": { + "name": "punctuation.semi.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#types" + }, + { + "include": "#lvariables" + } + ] + }, + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#types" + }, + { + "include": "#keywords" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#variables" } ], "repository": { - "block_doc_comment": { - "comment": "Block documentation comment", - "name": "comment.block.documentation.rust", - "begin": "/\\*[\\*!](?![\\*/])", - "end": "\\*/", + "comments": { "patterns": [ { - "include": "#block_doc_comment" + "comment": "documentation comments", + "name": "comment.line.documentation.rust", + "match": "^\\s*///.*" }, { - "include": "#block_comment" + "comment": "line comments", + "name": "comment.line.double-slash.rust", + "match": "\\s*//.*" } ] }, - "block_comment": { - "comment": "Block comment", - "name": "comment.block.rust", - "begin": "/\\*", - "end": "\\*/", + "block-comments": { "patterns": [ { - "include": "#block_doc_comment" + "comment": "block comments", + "name": "comment.block.rust", + "begin": "/\\*(?!\\*)", + "end": "\\*/", + "patterns": [ + { + "include": "#block-comments" + } + ] }, { - "include": "#block_comment" + "comment": "block documentation comments", + "name": "comment.block.documentation.rust", + "begin": "/\\*\\*", + "end": "\\*/", + "patterns": [ + { + "include": "#block-comments" + } + ] } ] }, - "line_doc_comment": { - "comment": "Single-line documentation comment", - "name": "comment.line.documentation.rust", - "begin": "//[!/](?=[^/])", - "end": "$" + "constants": { + "patterns": [ + { + "comment": "ALL CAPS constants", + "name": "constant.other.caps.rust", + "match": "\\b[A-Z]{2}[A-Z0-9_]*\\b" + }, + { + "comment": "constant declarations", + "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "keyword.control.rust" + }, + "2": { + "name": "constant.other.caps.rust" + } + } + }, + { + "comment": "decimal integers and floats", + "name": "constant.numeric.decimal.rust", + "match": "\\b\\d[\\d_]*(\\.?)[\\d_]*(?:(E)([+-])([\\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "punctuation.separator.dot.decimal.rust" + }, + "2": { + "name": "keyword.operator.exponent.rust" + }, + "3": { + "name": "keyword.operator.exponent.sign.rust" + }, + "4": { + "name": "constant.numeric.decimal.exponent.mantissa.rust" + }, + "5": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "hexadecimal integers", + "name": "constant.numeric.hex.rust", + "match": "\\b0x[\\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "octal integers", + "name": "constant.numeric.oct.rust", + "match": "\\b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "binary integers", + "name": "constant.numeric.bin.rust", + "match": "\\b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "booleans", + "name": "constant.language.bool.rust", + "match": "\\btrue|false\\b" + } + ] }, - "line_comment": { - "comment": "Single-line comment", - "name": "comment.line.double-slash.rust", - "begin": "//", - "end": "$" - }, - "escaped_character": { + "escapes": { + "comment": "escapes: ASCII, byte, Unicode, quote, regex", "name": "constant.character.escape.rust", - "match": "\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)" - }, - "string_literal": { - "comment": "Double-quote string literal", - "name": "string.quoted.double.rust", - "begin": "b?\"", - "end": "\"", - "patterns": [ - { - "include": "#escaped_character" - } - ] - }, - "raw_string_literal": { - "comment": "Raw double-quote string literal", - "name": "string.quoted.double.raw.rust", - "begin": "b?r(#*)\"", - "end": "\"\\1" - }, - "sigils": { - "comment": "Sigil", - "name": "keyword.operator.sigil.rust", - "match": "[&*](?=[a-zA-Z0-9_\\(\\[\\|\\\"]+)" - }, - "self": { - "comment": "Self variable", - "name": "variable.language.rust", - "match": "\\bself\\b" - }, - "mut": { - "comment": "Mutable storage modifier", - "name": "storage.modifier.mut.rust", - "match": "\\bmut\\b" - }, - "dyn": { - "comment": "Dynamic modifier", - "name": "storage.modifier.dyn.rust", - "match": "\\bdyn\\b" - }, - "impl": { - "comment": "Existential type modifier", - "name": "storage.modifier.impl.rust", - "match": "\\bimpl\\b" - }, - "box": { - "comment": "Box storage modifier", - "name": "storage.modifier.box.rust", - "match": "\\bbox\\b" - }, - "const": { - "comment": "Const storage modifier", - "name": "storage.modifier.const.rust", - "match": "\\bconst\\b" - }, - "pub": { - "comment": "Visibility modifier", - "name": "storage.modifier.visibility.rust", - "match": "\\bpub\\b" - }, - "unsafe": { - "comment": "Unsafe code keyword", - "name": "keyword.other.unsafe.rust", - "match": "\\bunsafe\\b" - }, - "where": { - "comment": "Generic where clause", - "name": "keyword.other.where.rust", - "match": "\\bwhere\\b" - }, - "lifetime": { - "comment": "Named lifetime", - "name": "storage.modifier.lifetime.rust", - "match": "'([a-zA-Z_][a-zA-Z0-9_]*)\\b", + "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))", "captures": { "1": { - "name": "entity.name.lifetime.rust" - } - } - }, - "ref_lifetime": { - "comment": "Reference with named lifetime", - "match": "&('([a-zA-Z_][a-zA-Z0-9_]*))\\b", - "captures": { - "1": { - "name": "storage.modifier.lifetime.rust" + "name": "constant.character.escape.backslash.rust" }, "2": { - "name": "entity.name.lifetime.rust" + "name": "constant.character.escape.bit.rust" + }, + "3": { + "name": "constant.character.escape.unicode.rust" + }, + "4": { + "name": "constant.character.escape.unicode.punctuation.rust" + }, + "5": { + "name": "constant.character.escape.unicode.punctuation.rust" } } }, - "core_types": { - "comment": "Built-in/core type", - "name": "storage.type.core.rust", - "match": "\\b(bool|char|usize|isize|u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f32|f64|str|Self|Option|Result)\\b" - }, - "core_vars": { - "comment": "Core type variant", - "name": "support.constant.core.rust", - "match": "\\b(Some|None|Ok|Err)\\b" - }, - "core_marker": { - "comment": "Core trait (marker)", - "name": "support.type.marker.rust", - "match": "\\b(Copy|Send|Sized|Sync)\\b" - }, - "core_traits": { - "comment": "Core trait", - "name": "support.type.core.rust", - "match": "\\b(Drop|Fn|FnMut|FnOnce|Clone|PartialEq|PartialOrd|Eq|Ord|AsRef|AsMut|Into|From|Default|Iterator|Extend|IntoIterator|DoubleEndedIterator|ExactSizeIterator)\\b" - }, - "std_types": { - "comment": "Standard library type", - "name": "storage.class.std.rust", - "match": "\\b(Box|String|Vec|Path|PathBuf)\\b" - }, - "std_traits": { - "comment": "Standard library trait", - "name": "support.type.std.rust", - "match": "\\b(ToOwned|ToString)\\b" - }, - "type": { - "comment": "A type", - "name": "entity.name.type.rust", - "match": "\\b([A-Za-z][_A-Za-z0-9]*|_[_A-Za-z0-9]+)\\b" - }, - "type_params": { - "comment": "Type parameters", - "name": "meta.type_params.rust", - "begin": "<(?![=<])", - "end": "(?<![-])>", + "functions": { "patterns": [ { - "include": "#block_comment" + "comment": "pub as a function", + "match": "\\b(pub)(\\()", + "captures": { + "1": { + "name": "keyword.other.rust" + }, + "2": { + "name": "punctuation.brackets.round.rust" + } + } }, { - "include": "#line_comment" + "comment": "function definition", + "name": "meta.function.definition.rust", + "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", + "beginCaptures": { + "1": { + "name": "keyword.control.fn.rust" + }, + "2": { + "name": "entity.name.function.rust" + }, + "4": { + "name": "punctuation.brackets.round.rust" + }, + "5": { + "name": "punctuation.brackets.angle.rust" + } + }, + "end": "\\{", + "endCaptures": { + "0": { + "name": "punctuation.brackets.curly.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] }, { - "include": "#sigils" + "comment": "function/method calls, chaining", + "name": "meta.function.call.rust", + "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.rust" + }, + "2": { + "name": "punctuation.brackets.round.rust" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.brackets.round.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#namespaces" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] + } + ] + }, + "keywords": { + "patterns": [ + { + "comment": "control flow keywords", + "name": "keyword.control.rust", + "match": "\\b(async|await|break|continue|do|else|for|if|loop|match|move|return|try|where|while|yield)\\b" }, { - "include": "#mut" + "comment": "storage keywords", + "name": "storage.type.rust", + "match": "\\b(const|enum|extern|let|macro|mod|struct|trait|type)\\b" }, { - "include": "#dyn" + "comment": "storage modifiers", + "name": "storage.modifier.rust", + "match": "\\b(abstract|static)\\b" }, { - "include": "#impl" + "comment": "other keywords", + "name": "keyword.other.rust", + "match": "\\b(as|become|box|dyn|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual)\\b" }, { - "include": "#lifetime" + "comment": "fn", + "name": "keyword.other.fn.rust", + "match": "\\bfn\\b" }, { - "include": "#core_types" + "comment": "crate", + "name": "keyword.other.crate.rust", + "match": "\\bcrate\\b" }, { - "include": "#core_marker" + "comment": "mut", + "name": "storage.modifier.mut.rust", + "match": "\\bmut\\b" }, { - "include": "#core_traits" + "comment": "math operators", + "name": "keyword.operator.math.rust", + "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))" }, { - "include": "#std_types" + "comment": "logical operators", + "name": "keyword.operator.logical.rust", + "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)" }, { - "include": "#std_traits" + "comment": "logical AND, borrow references", + "name": "keyword.operator.borrow.and.rust", + "match": "&(?![&=])" }, { - "include": "#type_params" + "comment": "assignment operators", + "name": "keyword.operator.assignment.rust", + "match": "(-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)" + }, + { + "comment": "single equal", + "name": "keyword.operator.assignment.equal.rust", + "match": "(?<![<>])=(?!=|>)" + }, + { + "comment": "comparison operators", + "name": "keyword.operator.comparison.rust", + "match": "(=(=)?(?!>)|!=|<=|(?<!=)>=)" + }, + { + "comment": "less than, greater than (special case)", + "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))", + "captures": { + "1": { + "name": "punctuation.brackets.round.rust" + }, + "2": { + "name": "punctuation.brackets.square.rust" + }, + "3": { + "name": "punctuation.brackets.curly.rust" + }, + "4": { + "name": "keyword.operator.comparison.rust" + }, + "5": { + "name": "punctuation.brackets.round.rust" + }, + "6": { + "name": "punctuation.brackets.square.rust" + }, + "7": { + "name": "punctuation.brackets.curly.rust" + } + } + }, + { + "comment": "namespace operator", + "name": "keyword.operator.namespace.rust", + "match": "::" + }, + { + "comment": "dereference asterisk", + "match": "(\\*)(?=\\w+)", + "captures": { + "1": { + "name": "keyword.operator.dereference.rust" + } + } + }, + { + "comment": "subpattern binding", + "name": "keyword.operator.subpattern.rust", + "match": "@" + }, + { + "comment": "dot access", + "name": "keyword.operator.access.dot.rust", + "match": "\\.(?!\\.)" + }, + { + "comment": "ranges, range patterns", + "name": "keyword.operator.range.rust", + "match": "\\.{2}(=|\\.)?" + }, + { + "comment": "colon", + "name": "keyword.operator.key-value.rust", + "match": ":(?!:)" + }, + { + "comment": "dashrocket, skinny arrow", + "name": "keyword.operator.arrow.skinny.rust", + "match": "->" + }, + { + "comment": "hashrocket, fat arrow", + "name": "keyword.operator.arrow.fat.rust", + "match": "=>" + }, + { + "comment": "dollar macros", + "name": "keyword.operator.macro.dollar.rust", + "match": "\\$" + }, + { + "comment": "question mark operator, questionably sized, macro kleene matcher", + "name": "keyword.operator.question.rust", + "match": "\\?" + } + ] + }, + "interpolations": { + "comment": "curly brace interpolations", + "name": "meta.interpolation.rust", + "match": "({)[^\"{}]*(})", + "captures": { + "1": { + "name": "punctuation.definition.interpolation.rust" + }, + "2": { + "name": "punctuation.definition.interpolation.rust" + } + } + }, + "lifetimes": { + "patterns": [ + { + "comment": "named lifetime parameters", + "match": "(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", + "captures": { + "1": { + "name": "punctuation.definition.lifetime.rust" + }, + "2": { + "name": "entity.name.type.lifetime.rust" + } + } + }, + { + "comment": "borrowing references to named lifetimes", + "match": "(\\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", + "captures": { + "1": { + "name": "keyword.operator.borrow.rust" + }, + "2": { + "name": "punctuation.definition.lifetime.rust" + }, + "3": { + "name": "entity.name.type.lifetime.rust" + } + } + } + ] + }, + "macros": { + "patterns": [ + { + "comment": "macros", + "name": "meta.macro.rust", + "match": "(([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!))", + "captures": { + "2": { + "name": "entity.name.function.macro.rust" + }, + "3": { + "name": "entity.name.type.macro.rust" + } + } + } + ] + }, + "namespaces": { + "patterns": [ + { + "comment": "namespace (non-type, non-function path segment)", + "match": "(?<![A-Za-z0-9_])([a-z0-9_]+)((?<!super|self)::)", + "captures": { + "1": { + "name": "entity.name.namespace.rust" + }, + "2": { + "name": "keyword.operator.namespace.rust" + } + } + } + ] + }, + "types": { + "patterns": [ + { + "comment": "numeric types", + "match": "(?<![A-Za-z])(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "parameterized types", + "begin": "\\b([A-Z][A-Za-z0-9]*)(<)", + "beginCaptures": { + "1": { + "name": "entity.name.type.rust" + }, + "2": { + "name": "punctuation.brackets.angle.rust" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.brackets.angle.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#lifetimes" + }, + { + "include": "#punctuation" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] + }, + { + "comment": "primitive types", + "name": "entity.name.type.primitive.rust", + "match": "\\b(bool|char|str)\\b" + }, + { + "comment": "trait declarations", + "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.trait.rust" + } + } + }, + { + "comment": "struct declarations", + "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.struct.rust" + } + } + }, + { + "comment": "enum declarations", + "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.enum.rust" + } + } + }, + { + "comment": "type declarations", + "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.declaration.rust" + } + } + }, + { + "comment": "types", + "name": "entity.name.type.rust", + "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)" + } + ] + }, + "gtypes": { + "patterns": [ + { + "comment": "option types", + "name": "entity.name.type.option.rust", + "match": "\\b(Some|None)\\b" + }, + { + "comment": "result types", + "name": "entity.name.type.result.rust", + "match": "\\b(Ok|Err)\\b" + } + ] + }, + "punctuation": { + "patterns": [ + { + "comment": "comma", + "name": "punctuation.comma.rust", + "match": "," + }, + { + "comment": "curly braces", + "name": "punctuation.brackets.curly.rust", + "match": "[{}]" + }, + { + "comment": "parentheses, round brackets", + "name": "punctuation.brackets.round.rust", + "match": "[()]" + }, + { + "comment": "semicolon", + "name": "punctuation.semi.rust", + "match": ";" + }, + { + "comment": "square brackets", + "name": "punctuation.brackets.square.rust", + "match": "[\\[\\]]" + }, + { + "comment": "angle brackets", + "name": "punctuation.brackets.angle.rust", + "match": "(?<!=)[<>]" + } + ] + }, + "strings": { + "patterns": [ + { + "comment": "double-quoted strings and byte strings", + "name": "string.quoted.double.rust", + "begin": "(b?)(\")", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.string.rust" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.rust" + } + }, + "patterns": [ + { + "include": "#escapes" + }, + { + "include": "#interpolations" + } + ] + }, + { + "comment": "double-quoted raw strings and raw byte strings", + "name": "string.quoted.double.rust", + "begin": "(b?r)(#*)(\")", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.string.raw.rust" + }, + "3": { + "name": "punctuation.definition.string.rust" + } + }, + "end": "(\")(\\2)", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.rust" + }, + "2": { + "name": "punctuation.definition.string.raw.rust" + } + } + }, + { + "comment": "characters and bytes", + "name": "string.quoted.single.char.rust", + "begin": "(b)?(')", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.char.rust" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.char.rust" + } + }, + "patterns": [ + { + "include": "#escapes" + } + ] + } + ] + }, + "lvariables": { + "patterns": [ + { + "comment": "self", + "name": "variable.language.self.rust", + "match": "\\b[Ss]elf\\b" + }, + { + "comment": "super", + "name": "variable.language.super.rust", + "match": "\\bsuper\\b" + } + ] + }, + "variables": { + "patterns": [ + { + "comment": "variables", + "name": "variable.other.rust", + "match": "\\b(?<!\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b" } ] } diff --git a/extensions/rust/test/colorize-results/test-6611_rs.json b/extensions/rust/test/colorize-results/test-6611_rs.json index 6667e4fd311..bc87118fc5c 100644 --- a/extensions/rust/test/colorize-results/test-6611_rs.json +++ b/extensions/rust/test/colorize-results/test-6611_rs.json @@ -1,13 +1,13 @@ [ { "c": "impl", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.other.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -33,8 +33,52 @@ } }, { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -56,13 +100,13 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -88,7 +132,18 @@ } }, { - "c": ": ", + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -110,7 +165,18 @@ } }, { - "c": "{ }", + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -121,14 +187,25 @@ } }, { - "c": "impl", - "t": "source.rust storage.type.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "impl", + "t": "source.rust keyword.other.rust", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -154,8 +231,52 @@ } }, { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,13 +298,13 @@ }, { "c": "for", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -221,13 +342,13 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -253,7 +374,18 @@ } }, { - "c": ": ", + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -275,7 +407,18 @@ } }, { - "c": "{ }", + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -286,14 +429,25 @@ } }, { - "c": "impl", - "t": "source.rust storage.type.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "impl", + "t": "source.rust keyword.other.rust", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -319,8 +473,52 @@ } }, { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -342,13 +540,13 @@ }, { "c": "for", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -375,7 +573,7 @@ }, { "c": "{", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,18 +595,18 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +617,7 @@ }, { "c": "foo", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -429,8 +627,8 @@ } }, { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -440,8 +638,19 @@ } }, { - "c": " -> C", - "t": "source.rust", + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust meta.function.definition.rust punctuation.comma.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -450,9 +659,75 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "->", + "t": "source.rust meta.function.definition.rust keyword.operator.arrow.skinny.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,18 +738,18 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust meta.function.definition.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", - "t": "source.rust", + "c": " ", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -484,7 +759,73 @@ } }, { - "c": " { }", + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust meta.function.definition.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -496,7 +837,18 @@ }, { "c": "}", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,13 +859,222 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "foo", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "<", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust meta.function.definition.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "->", + "t": "source.rust meta.function.definition.rust keyword.operator.arrow.skinny.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "where", + "t": "source.rust meta.function.definition.rust keyword.control.rust", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust meta.function.definition.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { @@ -528,74 +1089,8 @@ } }, { - "c": "foo", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " -> C", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "where", - "t": "source.rust keyword.other.where.rust", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, - { - "c": " A: B", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "{ }", - "t": "source.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -628,6 +1123,28 @@ }, { "c": "Foo", + "t": "source.rust entity.name.type.struct.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", "t": "source.rust entity.name.type.rust", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -638,8 +1155,30 @@ } }, { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,17 +1200,17 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -682,7 +1221,29 @@ } }, { - "c": "{ }", + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -692,6 +1253,50 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "trait", "t": "source.rust storage.type.rust", @@ -716,6 +1321,28 @@ }, { "c": "Foo", + "t": "source.rust entity.name.type.trait.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", "t": "source.rust entity.name.type.rust", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -726,8 +1353,8 @@ } }, { - "c": "<A,B>", - "t": "source.rust meta.type_params.rust", + "c": ",", + "t": "source.rust punctuation.comma.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -737,7 +1364,29 @@ } }, { - "c": " : C", + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -747,6 +1396,39 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": " ", "t": "source.rust", @@ -760,17 +1442,17 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -781,7 +1463,29 @@ } }, { - "c": "{ }", + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -790,5 +1494,49 @@ "light_vs": "default: #000000", "hc_black": "default: #FFFFFF" } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } } ] \ No newline at end of file diff --git a/extensions/rust/test/colorize-results/test_rs.json b/extensions/rust/test/colorize-results/test_rs.json index 977fe6bd28c..82615d435e2 100644 --- a/extensions/rust/test/colorize-results/test_rs.json +++ b/extensions/rust/test/colorize-results/test_rs.json @@ -1,18 +1,18 @@ [ { "c": "use", - "t": "source.rust keyword.other.rust", + "t": "source.rust meta.use.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " std", - "t": "source.rust", + "c": " ", + "t": "source.rust meta.use.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -21,9 +21,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "std", + "t": "source.rust meta.use.rust entity.name.namespace.rust", + "r": { + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.namespace: #4EC9B0" + } + }, { "c": "::", - "t": "source.rust keyword.operator.misc.rust", + "t": "source.rust meta.use.rust keyword.operator.namespace.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -33,8 +44,19 @@ } }, { - "c": "io;", - "t": "source.rust", + "c": "io", + "t": "source.rust meta.use.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust meta.use.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -45,18 +67,18 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -67,7 +89,7 @@ }, { "c": "main", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -77,8 +99,30 @@ } }, { - "c": "() {", - "t": "source.rust", + "c": "()", + "t": "source.rust meta.function.definition.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -100,18 +144,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -121,7 +165,18 @@ } }, { - "c": "\"Guess the number!\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Guess the number!", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -132,8 +187,30 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,18 +232,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -176,7 +253,18 @@ } }, { - "c": "\"Please input your guess.\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Please input your guess.", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -187,8 +275,30 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,13 +320,13 @@ }, { "c": "let", - "t": "source.rust keyword.other.rust", + "t": "source.rust storage.type.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" } }, { @@ -242,7 +352,29 @@ } }, { - "c": " guess ", + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "guess", + "t": "source.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -254,7 +386,7 @@ }, { "c": "=", - "t": "source.rust keyword.operator.assignment.rust", + "t": "source.rust keyword.operator.assignment.equal.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -276,18 +408,18 @@ }, { "c": "String", - "t": "source.rust storage.class.std.rust", + "t": "source.rust entity.name.type.rust", "r": { - "dark_plus": "storage: #569CD6", - "light_plus": "storage: #0000FF", - "dark_vs": "storage: #569CD6", - "light_vs": "storage: #0000FF", - "hc_black": "storage: #569CD6" + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" } }, { "c": "::", - "t": "source.rust keyword.operator.misc.rust", + "t": "source.rust keyword.operator.namespace.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -298,139 +430,7 @@ }, { "c": "new", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "();", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " io", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "::", - "t": "source.rust keyword.operator.misc.rust", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "stdin", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "().", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "read_line", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "&", - "t": "source.rust keyword.operator.sigil.rust", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "mut", - "t": "source.rust storage.modifier.mut.rust", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " guess)", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " .", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "ok", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.call.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -441,6 +441,28 @@ }, { "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -451,8 +473,41 @@ } }, { - "c": " .", - "t": "source.rust", + "c": "io", + "t": "source.rust entity.name.namespace.rust", + "r": { + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.namespace: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.rust keyword.operator.namespace.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "stdin", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -462,8 +517,19 @@ } }, { - "c": "expect", - "t": "source.rust entity.name.function.rust", + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "read_line", + "t": "source.rust meta.function.call.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -474,6 +540,72 @@ }, { "c": "(", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "&", + "t": "source.rust meta.function.call.rust keyword.operator.borrow.and.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "mut", + "t": "source.rust meta.function.call.rust storage.modifier.mut.rust", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " ", + "t": "source.rust meta.function.call.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "guess", + "t": "source.rust meta.function.call.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -484,8 +616,85 @@ } }, { - "c": "\"Failed to read line\"", - "t": "source.rust string.quoted.double.rust", + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "ok", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "expect", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.rust meta.function.call.rust string.quoted.double.rust punctuation.definition.string.rust", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -495,8 +704,41 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "Failed to read line", + "t": "source.rust meta.function.call.rust string.quoted.double.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.rust meta.function.call.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,18 +760,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -539,7 +781,18 @@ } }, { - "c": "\"You guessed: {}\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "You guessed: ", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -550,7 +803,40 @@ } }, { - "c": ", guess);", + "c": "{}", + "t": "source.rust string.quoted.double.rust meta.interpolation.rust punctuation.definition.interpolation.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -560,9 +846,42 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "guess", + "t": "source.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "}", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/scss/test/colorize-results/test_scss.json b/extensions/scss/test/colorize-results/test_scss.json index a95d72d9e16..66f3e1f376d 100644 --- a/extensions/scss/test/colorize-results/test_scss.json +++ b/extensions/scss/test/colorize-results/test_scss.json @@ -16206,11 +16206,11 @@ "c": "rel", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.attribute-selector.scss entity.other.attribute-name.attribute.scss", "r": { - "dark_plus": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_plus": "entity.other.attribute-name.attribute.scss: #800000", - "dark_vs": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_vs": "entity.other.attribute-name.attribute.scss: #800000", - "hc_black": "entity.other.attribute-name.attribute.scss: #D7BA7D" + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" } }, { @@ -20606,11 +20606,11 @@ "c": "data-icon", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.attribute-selector.scss entity.other.attribute-name.attribute.scss", "r": { - "dark_plus": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_plus": "entity.other.attribute-name.attribute.scss: #800000", - "dark_vs": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_vs": "entity.other.attribute-name.attribute.scss: #800000", - "hc_black": "entity.other.attribute-name.attribute.scss: #D7BA7D" + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" } }, { @@ -20761,7 +20761,7 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss constant.character.escape.scss", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -20882,7 +20882,7 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss constant.character.escape.scss", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -20959,7 +20959,7 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss string.quoted.double.scss constant.character.escape.scss", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -21020,4 +21020,4 @@ "hc_black": "comment: #7CA668" } } -] +] \ No newline at end of file diff --git a/extensions/search-result/.vscodeignore b/extensions/search-result/.vscodeignore new file mode 100644 index 00000000000..da3d2763686 --- /dev/null +++ b/extensions/search-result/.vscodeignore @@ -0,0 +1,6 @@ +src/** +out/** +tsconfig.json +extension.webpack.config.js +extension-browser.webpack.config.js +yarn.lock diff --git a/extensions/search-result/README.md b/extensions/search-result/README.md index c3e1b53525c..fe886e4bde1 100644 --- a/extensions/search-result/README.md +++ b/extensions/search-result/README.md @@ -2,4 +2,4 @@ **Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. -This extension provides Syntax Highlighting, Symbol Infomation, Result Highlighting, and Go to Definition capabilities for the Search Results Editor. +This extension provides Syntax Highlighting, Symbol Information, Result Highlighting, and Go to Definition capabilities for the Search Results Editor. diff --git a/src/vs/base/parts/tree/browser/treeUtils.ts b/extensions/search-result/extension-browser.webpack.config.js similarity index 52% rename from src/vs/base/parts/tree/browser/treeUtils.ts rename to extensions/search-result/extension-browser.webpack.config.js index 6d536a7fa5a..10c0a19e356 100644 --- a/src/vs/base/parts/tree/browser/treeUtils.ts +++ b/extensions/search-result/extension-browser.webpack.config.js @@ -3,16 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as _ from 'vs/base/parts/tree/browser/tree'; +//@ts-check -export function isEqualOrParent(tree: _.ITree, element: any, candidateParent: any): boolean { - const nav = tree.getNavigator(element); +'use strict'; - do { - if (element === candidateParent) { - return true; - } - } while (element = nav.parent()); +const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); - return false; -} +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts' + }, + output: { + filename: 'extension.js', + path: path.join(__dirname, 'dist') + } +}); diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 40d59e2bcd7..7b43da243cf 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -12,6 +12,7 @@ "Programming Languages" ], "main": "./out/extension.js", + "browser": "./dist/extension.js", "activationEvents": [ "*" ], @@ -25,46 +26,6 @@ "editor.lineNumbers": "off" } }, - "commands": [ - { - "command": "searchResult.rerunSearch", - "title": "%searchResult.rerunSearch.title%", - "category": "Search Result", - "icon": { - "light": "./src/media/refresh-light.svg", - "dark": "./src/media/refresh-dark.svg" - } - }, - { - "command": "searchResult.rerunSearchWithContext", - "title": "%searchResult.rerunSearchWithContext.title%", - "category": "Search Result", - "icon": { - "light": "./src/media/refresh-light.svg", - "dark": "./src/media/refresh-dark.svg" - } - } - ], - "menus": { - "commandPalette": [ - { - "command": "searchResult.rerunSearch", - "when": "false" - }, - { - "command": "searchResult.rerunSearchWithContext", - "when": "false" - } - ], - "editor/title": [ - { - "command": "searchResult.rerunSearch", - "when": "editorLangId == search-result", - "alt": "searchResult.rerunSearchWithContext", - "group": "navigation" - } - ] - }, "languages": [ { "id": "search-result", @@ -83,8 +44,5 @@ "path": "./syntaxes/searchResult.tmLanguage.json" } ] - }, - "devDependencies": { - "vscode": "^1.1.36" } } diff --git a/extensions/search-result/package.nls.json b/extensions/search-result/package.nls.json index ce90d23c09c..324fd97bcd2 100644 --- a/extensions/search-result/package.nls.json +++ b/extensions/search-result/package.nls.json @@ -1,6 +1,4 @@ { "displayName": "Search Result", - "description": "Provides syntax highlighting and language features for tabbed search results.", - "searchResult.rerunSearch.title": "Search Again", - "searchResult.rerunSearchWithContext.title": "Search Again (With Context)" + "description": "Provides syntax highlighting and language features for tabbed search results." } diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 0bd5e12188e..0c0c9b85f5f 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -34,8 +34,6 @@ export function activate(context: vscode.ExtensionContext) { } context.subscriptions.push( - vscode.commands.registerCommand('searchResult.rerunSearch', () => vscode.commands.executeCommand('search.action.rerunEditorSearch')), - vscode.commands.registerCommand('searchResult.rerunSearchWithContext', () => vscode.commands.executeCommand('search.action.rerunEditorSearchWithContext')), vscode.languages.registerDocumentSymbolProvider(SEARCH_RESULT_SELECTOR, { provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.DocumentSymbol[] { @@ -79,9 +77,7 @@ export function activate(context: vscode.ExtensionContext) { const lineResult = parseSearchResults(document, token)[position.line]; if (!lineResult) { return []; } if (lineResult.type === 'file') { - // TODO: The multi-match peek UX isnt very smooth. - // return lineResult.allLocations.length > 1 ? lineResult.allLocations : [lineResult.location]; - return []; + return lineResult.allLocations; } const translateRangeSidewaysBy = (r: vscode.Range, n: number) => @@ -125,11 +121,20 @@ export function activate(context: vscode.ExtensionContext) { function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | undefined { - if (pathUtils.isAbsolute(path)) { return vscode.Uri.file(path); } - if (path.indexOf('~/') === 0) { - return vscode.Uri.file(pathUtils.join(process.env.HOME!, path.slice(2))); + if (pathUtils.isAbsolute(path)) { + return vscode.Uri + .file(path) + .with({ scheme: process.env.HOME ? 'file' : 'vscode-userdata' }); } + if (path.indexOf('~/') === 0) { + return vscode.Uri + .file(pathUtils.join(process.env.HOME ?? '', path.slice(2))) + .with({ scheme: process.env.HOME ? 'file' : 'vscode-userdata' }); + } + + const uriFromFolderWithPath = (folder: vscode.WorkspaceFolder, path: string): vscode.Uri => + vscode.Uri.joinPath(folder.uri, path); if (vscode.workspace.workspaceFolders) { const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path); @@ -137,17 +142,18 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u const [, workspaceName, workspacePath] = multiRootFormattedPath; const folder = vscode.workspace.workspaceFolders.filter(wf => wf.name === workspaceName)[0]; if (folder) { - return vscode.Uri.file(pathUtils.join(folder.uri.fsPath, workspacePath)); + return uriFromFolderWithPath(folder, workspacePath); } } - else if (vscode.workspace.workspaceFolders.length === 1) { - return vscode.Uri.file(pathUtils.join(vscode.workspace.workspaceFolders[0].uri.fsPath, path)); + return uriFromFolderWithPath(vscode.workspace.workspaceFolders[0], path); } else if (resultsUri.scheme !== 'untitled') { // We're in a multi-root workspace, but the path is not multi-root formatted // Possibly a saved search from a single root session. Try checking if the search result document's URI is in a current workspace folder. const prefixMatch = vscode.workspace.workspaceFolders.filter(wf => resultsUri.toString().startsWith(wf.uri.toString()))[0]; - if (prefixMatch) { return vscode.Uri.file(pathUtils.join(prefixMatch.uri.fsPath, path)); } + if (prefixMatch) { + return uriFromFolderWithPath(prefixMatch, path); + } } } diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js index 51d9e0ea7c3..fb74d3696ef 100644 --- a/extensions/search-result/syntaxes/generateTMLanguage.js +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -1,26 +1,22 @@ // @ts-check -// todo@jackson -/* eslint code-no-unexternalized-strings: 0 */ const mappings = [ ['bat', 'source.batchfile'], ['c', 'source.c'], - ['cc', 'source.cpp'], ['clj', 'source.clojure'], ['coffee', 'source.coffee'], - ['cpp', 'source.cpp'], + ['cpp', 'source.cpp', '\\.(?:cpp|c\\+\\+|cc|cxx|hxx|h\\+\\+|hh)'], ['cs', 'source.cs'], ['cshtml', 'text.html.cshtml'], ['css', 'source.css'], ['dart', 'source.dart'], ['diff', 'source.diff'], - ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile)'], + ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile|containerfile|Containerfile)'], ['fs', 'source.fsharp'], ['go', 'source.go'], ['groovy', 'source.groovy'], ['h', 'source.objc'], - ['handlebars', 'text.html.handlebars'], - ['hbs', 'text.html.handlebars'], + ['handlebars', 'text.html.handlebars', '\\.(?:handlebars|hbs)'], ['hlsl', 'source.hlsl'], ['hpp', 'source.objcpp'], ['html', 'text.html.basic'], @@ -37,9 +33,8 @@ const mappings = [ ['md', 'text.html.markdown'], ['mm', 'source.objcpp'], ['p6', 'source.perl.6'], - ['perl', 'source.perl'], + ['perl', 'source.perl', '\\.(?:perl|pl|pm)'], ['php', 'source.php'], - ['pl', 'source.perl'], ['ps1', 'source.powershell'], ['pug', 'text.pug'], ['py', 'source.python'], @@ -55,7 +50,7 @@ const mappings = [ ['tsx', 'source.tsx'], ['vb', 'source.asp.vb.net'], ['xml', 'text.xml'], - ['yaml', 'source.yaml'], + ['yaml', 'source.yaml', '\\.(?:ya?ml)'], ]; const scopes = { @@ -104,43 +99,43 @@ mappings.forEach(([ext, scope, regexp]) => repository[ext] = { name: scopes.resultBlock.meta, begin: `^(?!\\s)(.*?)([^\\\\\\/\\n]*${regexp || `\\.${ext}`})(:)$`, - end: "^(?!\\s)", + end: '^(?!\\s)', beginCaptures: { - "0": { name: scopes.resultBlock.path.meta }, - "1": { name: scopes.resultBlock.path.dirname }, - "2": { name: scopes.resultBlock.path.basename }, - "3": { name: scopes.resultBlock.path.colon }, + '0': { name: scopes.resultBlock.path.meta }, + '1': { name: scopes.resultBlock.path.dirname }, + '2': { name: scopes.resultBlock.path.basename }, + '3': { name: scopes.resultBlock.path.colon }, }, patterns: [ { name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaMultiLine].join(' '), - begin: "^ ((\\d+) )", - while: "^ ((\\d+)(:))|((\\d+) )", + begin: '^ (?:\\s*)((\\d+) )', + while: '^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))', beginCaptures: { - "0": { name: scopes.resultBlock.result.prefix.meta }, - "1": { name: scopes.resultBlock.result.prefix.metaContext }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, + '0': { name: scopes.resultBlock.result.prefix.meta }, + '1': { name: scopes.resultBlock.result.prefix.metaContext }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, }, whileCaptures: { - "0": { name: scopes.resultBlock.result.prefix.meta }, - "1": { name: scopes.resultBlock.result.prefix.metaMatch }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, - "3": { name: scopes.resultBlock.result.prefix.colon }, + '0': { name: scopes.resultBlock.result.prefix.meta }, + '1': { name: scopes.resultBlock.result.prefix.metaMatch }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, + '3': { name: scopes.resultBlock.result.prefix.colon }, - "4": { name: scopes.resultBlock.result.prefix.metaContext }, - "5": { name: scopes.resultBlock.result.prefix.lineNumber }, + '4': { name: scopes.resultBlock.result.prefix.metaContext }, + '5': { name: scopes.resultBlock.result.prefix.lineNumber }, }, patterns: [{ include: scope }] }, { - begin: "^ ((\\d+)(:))", - while: "(?=not)possible", + begin: '^ (?:\\s*)((\\d+)(:))', + while: '(?=not)possible', name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaSingleLine].join(' '), beginCaptures: { - "0": { name: scopes.resultBlock.result.prefix.meta }, - "1": { name: scopes.resultBlock.result.prefix.metaMatch }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, - "3": { name: scopes.resultBlock.result.prefix.colon }, + '0': { name: scopes.resultBlock.result.prefix.meta }, + '1': { name: scopes.resultBlock.result.prefix.metaMatch }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, + '3': { name: scopes.resultBlock.result.prefix.colon }, }, patterns: [{ include: scope }] } @@ -149,10 +144,10 @@ mappings.forEach(([ext, scope, regexp]) => const header = [ { - begin: "^(# Query): ", - end: "\n", + begin: '^(# Query): ', + end: '\n', name: scopes.header.meta, - beginCaptures: { "1": { name: scopes.header.key }, }, + beginCaptures: { '1': { name: scopes.header.key }, }, patterns: [ { match: '(\\\\n)|(\\\\\\\\)', @@ -169,10 +164,10 @@ const header = [ ] }, { - begin: "^(# Flags): ", - end: "\n", + begin: '^(# Flags): ', + end: '\n', name: scopes.header.meta, - beginCaptures: { "1": { name: scopes.header.key }, }, + beginCaptures: { '1': { name: scopes.header.key }, }, patterns: [ { match: '(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)', @@ -182,10 +177,10 @@ const header = [ ] }, { - begin: "^(# ContextLines): ", - end: "\n", + begin: '^(# ContextLines): ', + end: '\n', name: scopes.header.meta, - beginCaptures: { "1": { name: scopes.header.key }, }, + beginCaptures: { '1': { name: scopes.header.key }, }, patterns: [ { match: '\\d', @@ -195,42 +190,42 @@ const header = [ ] }, { - match: "^(# (?:Including|Excluding)): (.*)$", + match: '^(# (?:Including|Excluding)): (.*)$', name: scopes.header.meta, captures: { - "1": { name: scopes.header.key }, - "2": { name: scopes.header.value } + '1': { name: scopes.header.key }, + '2': { name: scopes.header.value } } }, ]; const plainText = [ { - match: "^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$", + match: '^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$', name: [scopes.resultBlock.meta, scopes.resultBlock.path.meta].join(' '), captures: { - "1": { name: scopes.resultBlock.path.dirname }, - "2": { name: scopes.resultBlock.path.basename }, - "3": { name: scopes.resultBlock.path.colon } + '1': { name: scopes.resultBlock.path.dirname }, + '2': { name: scopes.resultBlock.path.basename }, + '3': { name: scopes.resultBlock.path.colon } } }, { - match: "^ ((\\d+)(:))|((\\d+)( ))(.*)", + match: '^ (?:\\s*)(?:((\\d+)(:))|((\\d+)( ))(.*))', name: [scopes.resultBlock.meta, scopes.resultBlock.result.meta].join(' '), captures: { - "1": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, - "3": { name: scopes.resultBlock.result.prefix.colon }, + '1': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, + '3': { name: scopes.resultBlock.result.prefix.colon }, - "4": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, - "5": { name: scopes.resultBlock.result.prefix.lineNumber }, + '4': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, + '5': { name: scopes.resultBlock.result.prefix.lineNumber }, } } ]; const tmLanguage = { - "information_for_contributors": "This file is generated from ./generateTMLanguage.js.", - name: "Search Results", + 'information_for_contributors': 'This file is generated from ./generateTMLanguage.js.', + name: 'Search Results', scopeName: scopes.root, patterns: [ ...header, diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json index e16ca1cb95d..e2687fe8a72 100644 --- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -84,9 +84,6 @@ { "include": "#c" }, - { - "include": "#cc" - }, { "include": "#clj" }, @@ -129,9 +126,6 @@ { "include": "#handlebars" }, - { - "include": "#hbs" - }, { "include": "#hlsl" }, @@ -186,9 +180,6 @@ { "include": "#php" }, - { - "include": "#pl" - }, { "include": "#ps1" }, @@ -253,7 +244,7 @@ } }, { - "match": "^ ((\\d+)(:))|((\\d+)( ))(.*)", + "match": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+)( ))(.*))", "name": "meta.resultBlock.search meta.resultLine.search", "captures": { "1": { @@ -296,8 +287,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -336,7 +327,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -382,8 +373,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -422,7 +413,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -447,92 +438,6 @@ } ] }, - "cc": { - "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cc)(:)$", - "end": "^(?!\\s)", - "beginCaptures": { - "0": { - "name": "string meta.path.search" - }, - "1": { - "name": "meta.path.dirname.search" - }, - "2": { - "name": "meta.path.basename.search" - }, - "3": { - "name": "punctuation.separator" - } - }, - "patterns": [ - { - "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", - "beginCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.contextLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - } - }, - "whileCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.matchLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - }, - "3": { - "name": "punctuation.separator" - }, - "4": { - "name": "meta.resultLinePrefix.contextLinePrefix.search" - }, - "5": { - "name": "meta.resultLinePrefix.lineNumber.search" - } - }, - "patterns": [ - { - "include": "source.cpp" - } - ] - }, - { - "begin": "^ ((\\d+)(:))", - "while": "(?=not)possible", - "name": "meta.resultLine.search meta.resultLine.singleLine.search", - "beginCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.matchLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - }, - "3": { - "name": "punctuation.separator" - } - }, - "patterns": [ - { - "include": "source.cpp" - } - ] - } - ] - }, "clj": { "name": "meta.resultBlock.search", "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.clj)(:)$", @@ -554,8 +459,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -594,7 +499,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -640,8 +545,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -680,7 +585,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -707,7 +612,7 @@ }, "cpp": { "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cpp)(:)$", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:cpp|c\\+\\+|cc|cxx|hxx|h\\+\\+|hh))(:)$", "end": "^(?!\\s)", "beginCaptures": { "0": { @@ -726,8 +631,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -766,7 +671,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -812,8 +717,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -852,7 +757,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -898,8 +803,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -938,7 +843,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -984,8 +889,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1024,7 +929,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1070,8 +975,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1110,7 +1015,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1156,8 +1061,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1196,7 +1101,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1223,7 +1128,7 @@ }, "dockerfile": { "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*(?:dockerfile|Dockerfile))(:)$", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*(?:dockerfile|Dockerfile|containerfile|Containerfile))(:)$", "end": "^(?!\\s)", "beginCaptures": { "0": { @@ -1242,8 +1147,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1282,7 +1187,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1328,8 +1233,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1368,7 +1273,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1414,8 +1319,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1454,7 +1359,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1500,8 +1405,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1540,7 +1445,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1586,8 +1491,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1626,7 +1531,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1653,7 +1558,7 @@ }, "handlebars": { "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.handlebars)(:)$", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:handlebars|hbs))(:)$", "end": "^(?!\\s)", "beginCaptures": { "0": { @@ -1672,8 +1577,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1712,93 +1617,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", - "while": "(?=not)possible", - "name": "meta.resultLine.search meta.resultLine.singleLine.search", - "beginCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.matchLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - }, - "3": { - "name": "punctuation.separator" - } - }, - "patterns": [ - { - "include": "text.html.handlebars" - } - ] - } - ] - }, - "hbs": { - "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.hbs)(:)$", - "end": "^(?!\\s)", - "beginCaptures": { - "0": { - "name": "string meta.path.search" - }, - "1": { - "name": "meta.path.dirname.search" - }, - "2": { - "name": "meta.path.basename.search" - }, - "3": { - "name": "punctuation.separator" - } - }, - "patterns": [ - { - "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", - "beginCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.contextLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - } - }, - "whileCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.matchLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - }, - "3": { - "name": "punctuation.separator" - }, - "4": { - "name": "meta.resultLinePrefix.contextLinePrefix.search" - }, - "5": { - "name": "meta.resultLinePrefix.lineNumber.search" - } - }, - "patterns": [ - { - "include": "text.html.handlebars" - } - ] - }, - { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1844,8 +1663,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1884,7 +1703,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -1930,8 +1749,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -1970,7 +1789,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2016,8 +1835,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2056,7 +1875,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2102,8 +1921,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2142,7 +1961,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2188,8 +2007,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2228,7 +2047,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2274,8 +2093,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2314,7 +2133,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2360,8 +2179,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2400,7 +2219,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2446,8 +2265,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2486,7 +2305,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2532,8 +2351,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2572,7 +2391,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2618,8 +2437,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2658,7 +2477,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2704,8 +2523,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2744,7 +2563,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2790,8 +2609,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2830,7 +2649,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2876,8 +2695,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -2916,7 +2735,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -2962,8 +2781,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3002,7 +2821,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3048,8 +2867,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3088,7 +2907,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3134,8 +2953,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3174,7 +2993,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3201,7 +3020,7 @@ }, "perl": { "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.perl)(:)$", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:perl|pl|pm))(:)$", "end": "^(?!\\s)", "beginCaptures": { "0": { @@ -3220,8 +3039,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3260,7 +3079,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3306,8 +3125,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3346,7 +3165,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3371,92 +3190,6 @@ } ] }, - "pl": { - "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.pl)(:)$", - "end": "^(?!\\s)", - "beginCaptures": { - "0": { - "name": "string meta.path.search" - }, - "1": { - "name": "meta.path.dirname.search" - }, - "2": { - "name": "meta.path.basename.search" - }, - "3": { - "name": "punctuation.separator" - } - }, - "patterns": [ - { - "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", - "beginCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.contextLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - } - }, - "whileCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.matchLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - }, - "3": { - "name": "punctuation.separator" - }, - "4": { - "name": "meta.resultLinePrefix.contextLinePrefix.search" - }, - "5": { - "name": "meta.resultLinePrefix.lineNumber.search" - } - }, - "patterns": [ - { - "include": "source.perl" - } - ] - }, - { - "begin": "^ ((\\d+)(:))", - "while": "(?=not)possible", - "name": "meta.resultLine.search meta.resultLine.singleLine.search", - "beginCaptures": { - "0": { - "name": "constant.numeric.integer meta.resultLinePrefix.search" - }, - "1": { - "name": "meta.resultLinePrefix.matchLinePrefix.search" - }, - "2": { - "name": "meta.resultLinePrefix.lineNumber.search" - }, - "3": { - "name": "punctuation.separator" - } - }, - "patterns": [ - { - "include": "source.perl" - } - ] - } - ] - }, "ps1": { "name": "meta.resultBlock.search", "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ps1)(:)$", @@ -3478,8 +3211,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3518,7 +3251,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3564,8 +3297,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3604,7 +3337,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3650,8 +3383,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3690,7 +3423,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3736,8 +3469,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3776,7 +3509,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3822,8 +3555,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3862,7 +3595,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3908,8 +3641,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -3948,7 +3681,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -3994,8 +3727,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4034,7 +3767,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4080,8 +3813,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4120,7 +3853,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4166,8 +3899,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4206,7 +3939,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4252,8 +3985,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4292,7 +4025,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4338,8 +4071,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4378,7 +4111,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4424,8 +4157,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4464,7 +4197,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4510,8 +4243,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4550,7 +4283,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4596,8 +4329,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4636,7 +4369,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4682,8 +4415,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4722,7 +4455,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { @@ -4749,7 +4482,7 @@ }, "yaml": { "name": "meta.resultBlock.search", - "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.yaml)(:)$", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:ya?ml))(:)$", "end": "^(?!\\s)", "beginCaptures": { "0": { @@ -4768,8 +4501,8 @@ "patterns": [ { "name": "meta.resultLine.search meta.resultLine.multiLine.search", - "begin": "^ ((\\d+) )", - "while": "^ ((\\d+)(:))|((\\d+) )", + "begin": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+) ))", "beginCaptures": { "0": { "name": "constant.numeric.integer meta.resultLinePrefix.search" @@ -4808,7 +4541,7 @@ ] }, { - "begin": "^ ((\\d+)(:))", + "begin": "^ (?:\\s*)((\\d+)(:))", "while": "(?=not)possible", "name": "meta.resultLine.search meta.resultLine.singleLine.search", "beginCaptures": { diff --git a/extensions/search-result/yarn.lock b/extensions/search-result/yarn.lock index 4ff83ab98bb..fb57ccd13af 100644 --- a/extensions/search-result/yarn.lock +++ b/extensions/search-result/yarn.lock @@ -2,601 +2,3 @@ # yarn lockfile v1 -agent-base@4, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - -ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -debug@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - -escape-string-regexp@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== - dependencies: - 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" - -glob@^7.1.2: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - 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" - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= - -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-proxy-agent@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -mime-db@1.40.0: - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== - dependencies: - browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" - diff "3.5.0" - escape-string-regexp "1.0.5" - glob "7.1.2" - growl "1.10.5" - he "1.1.1" - minimatch "3.0.4" - mkdirp "0.5.1" - supports-color "5.4.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -psl@^1.1.24: - version "1.4.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" - integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - -request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - 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" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -source-map-support@^0.5.0: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - 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" - -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== - dependencies: - has-flag "^3.0.0" - -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -url-parse@^1.4.4: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vscode-test@^0.4.1: - version "0.4.3" - resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" - integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== - dependencies: - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - -vscode@^1.1.36: - version "1.1.36" - resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" - integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== - dependencies: - glob "^7.1.2" - mocha "^5.2.0" - request "^2.88.0" - semver "^5.4.1" - source-map-support "^0.5.0" - url-parse "^1.4.4" - vscode-test "^0.4.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/extensions/shaderlab/test/colorize-results/test_shader.json b/extensions/shaderlab/test/colorize-results/test_shader.json index 80eff76e153..fac0a925a41 100644 --- a/extensions/shaderlab/test/colorize-results/test_shader.json +++ b/extensions/shaderlab/test/colorize-results/test_shader.json @@ -452,7 +452,7 @@ }, { "c": "1", - "t": "source.shaderlab meta.cgblock constant.numeric.hlsl", + "t": "source.shaderlab meta.cgblock constant.numeric.decimal.hlsl", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -571,4 +571,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/shared.tsconfig.json b/extensions/shared.tsconfig.json index bb42dd479cf..a84d7a1cc03 100644 --- a/extensions/shared.tsconfig.json +++ b/extensions/shared.tsconfig.json @@ -1,6 +1,9 @@ { "compilerOptions": { "target": "es2018", + "lib": [ + "es2018" + ], "module": "commonjs", "strict": true, "alwaysStrict": true, @@ -9,4 +12,4 @@ "noUnusedLocals": true, "noUnusedParameters": true } -} \ No newline at end of file +} diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 2bcb6c47474..ab6a40c6b80 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -13,8 +13,9 @@ const fs = require('fs'); const merge = require('merge-options'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { NLSBundlePlugin } = require('vscode-nls-dev/lib/webpack-bundler'); +const { DefinePlugin } = require('webpack'); -module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { +function withNodeDefaults(/**@type WebpackConfig*/extConfig) { // Need to find the top-most `package.json` file const folderName = path.relative(__dirname, extConfig.context).split(/[\\\/]/)[0]; const pkgPath = path.join(__dirname, folderName, 'package.json'); @@ -68,7 +69,7 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { // yes, really source maps devtool: 'source-map', plugins: [ - // @ts-ignore + // @ts-expect-error new CopyWebpackPlugin([ { from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] } ]), @@ -77,4 +78,67 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { }; return merge(defaultConfig, extConfig); -}; +} + + +function withBrowserDefaults(/**@type WebpackConfig*/extConfig) { + /** @type WebpackConfig */ + let defaultConfig = { + mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: 'webworker', // extensions run in a webworker context + resolve: { + mainFields: ['module', 'main'], + extensions: ['.ts', '.js'], // support ts-files and js-files + alias: { + 'vscode-nls': path.resolve(__dirname, '../build/polyfills/vscode-nls.js'), + 'vscode-extension-telemetry': path.resolve(__dirname, '../build/polyfills/vscode-extension-telemetry.js') + } + }, + module: { + rules: [{ + test: /\.ts$/, + exclude: /node_modules/, + use: [{ + // configure TypeScript loader: + // * enable sources maps for end-to-end source maps + loader: 'ts-loader', + options: { + compilerOptions: { + 'sourceMap': true, + } + } + }] + }] + }, + externals: { + 'vscode': 'commonjs vscode', // ignored because it doesn't exist + }, + performance: { + hints: false + }, + output: { + // all output goes into `dist`. + // packaging depends on that and this must always be like it + filename: '[name].js', + path: path.join(extConfig.context, 'dist', 'browser'), + libraryTarget: 'commonjs', + }, + // yes, really source maps + devtool: 'source-map', + plugins: [ + // @ts-expect-error + new CopyWebpackPlugin([ + { from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] } + ]), + new DefinePlugin({ WEBWORKER: JSON.stringify(true) }) + ] + }; + + return merge(defaultConfig, extConfig); +} + + +module.exports = withNodeDefaults; +module.exports.node = withNodeDefaults; +module.exports.browser = withBrowserDefaults; + diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index 211401a070c..50c3ffdb799 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -12,10 +12,46 @@ "contributes": { "languages": [{ "id": "shellscript", - "aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh", "ksh"], - "extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme", ".ksh"], - "filenames": ["APKBUILD", "PKGBUILD"], - "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh|ash|qsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", + "aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh", "ksh", "csh"], + "extensions": [ + ".sh", + ".bash", + ".bashrc", + ".bash_aliases", + ".bash_profile", + ".bash_login", + ".ebuild", + ".install", + ".profile", + ".bash_logout", + ".zsh", + ".zshrc", + ".zprofile", + ".zlogin", + ".zlogout", + ".zshenv", + ".zsh-theme", + ".ksh", + ".csh", + ".cshrc", + ".tcshrc", + ".yashrc", + ".yash_profile" + ], + "filenames": [ + "APKBUILD", + "PKGBUILD", + ".envrc", + ".hushlogin", + "zshrc", + "zshenv", + "zlogin", + "zprofile", + "zlogout", + "bashrc_Apple_Terminal", + "zshrc_Apple_Terminal" + ], + "firstLine": "^#!.*\\b(bash|zsh|sh|ksh|dtksh|pdksh|mksh|ash|dash|yash|sh|csh|jcsh|tcsh|itcsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", "configuration": "./language-configuration.json", "mimetypes": ["text/x-shellscript"] }], diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index 7abcea36e5d..110a300aff8 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "Microsoft/vscode-mssql", - "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "a79741f76fd33bd137a8c28172c9750b978309b6" + "name": "microsoft/vscode-mssql", + "repositoryUrl": "https://github.com/microsoft/vscode-mssql", + "commitHash": "61ae0eb21ac53883a23e09913a5ae77a59126ff9" } }, "license": "MIT", - "version": "1.6.0" + "version": "1.9.0" } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/sql/package.json b/extensions/sql/package.json index 8c283e97246..5063c9d73e8 100644 --- a/extensions/sql/package.json +++ b/extensions/sql/package.json @@ -7,7 +7,7 @@ "license": "MIT", "engines": { "vscode": "*" }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json" + "update-grammar": "node ../../build/npm/update-grammar.js microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json" }, "contributes": { "languages": [{ @@ -22,4 +22,4 @@ "path": "./syntaxes/sql.tmLanguage.json" }] } -} \ No newline at end of file +} diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 1dd6903783f..446d200815e 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/vscode-mssql/blob/master/syntaxes/SQL.plist", + "This file has been converted from https://github.com/microsoft/vscode-mssql/blob/master/syntaxes/SQL.plist", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/vscode-mssql/commit/a79741f76fd33bd137a8c28172c9750b978309b6", + "version": "https://github.com/microsoft/vscode-mssql/commit/61ae0eb21ac53883a23e09913a5ae77a59126ff9", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -516,4 +516,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/swift/package.json b/extensions/swift/package.json index f88ac07ea5e..a8f75268efc 100644 --- a/extensions/swift/package.json +++ b/extensions/swift/package.json @@ -23,7 +23,7 @@ }], "snippets": [{ "language": "swift", - "path": "./snippets/swift.json" + "path": "./snippets/swift.code-snippets" }] } } diff --git a/extensions/swift/snippets/swift.json b/extensions/swift/snippets/swift.code-snippets similarity index 100% rename from extensions/swift/snippets/swift.json rename to extensions/swift/snippets/swift.code-snippets diff --git a/extensions/swift/syntaxes/swift.tmLanguage.json b/extensions/swift/syntaxes/swift.tmLanguage.json index 2d67b7a13e1..91a374d4716 100644 --- a/extensions/swift/syntaxes/swift.tmLanguage.json +++ b/extensions/swift/syntaxes/swift.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/swift.tmbundle/commit/3f582e9acc1613745b06a56be55ba2a032c458eb", + "version": "https://github.com/textmate/swift.tmbundle/commit/97d29d2073853c328e42239c5d38c96e2e2ade9c", "name": "Swift", "scopeName": "source.swift", "comment": "See swift.tmbundle/grammar-test.swift for test cases.", @@ -2620,7 +2620,7 @@ "name": "variable.language.swift" }, { - "match": "\\B(?:#file|#line|#column|#function|#dsohandle)\\b|\\b(?:__FILE__|__LINE__|__COLUMN__|__FUNCTION__|__DSO_HANDLE__)\\b", + "match": "\\B(?:#file|#filePath|#fileID|#line|#column|#function|#dsohandle)\\b|\\b(?:__FILE__|__LINE__|__COLUMN__|__FUNCTION__|__DSO_HANDLE__)\\b", "name": "support.variable.swift" }, { diff --git a/extensions/theme-abyss/package.json b/extensions/theme-abyss/package.json index bdeb6a2a643..9543824f331 100644 --- a/extensions/theme-abyss/package.json +++ b/extensions/theme-abyss/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Abyss", + "id": "Abyss", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/abyss-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-abyss/package.nls.json b/extensions/theme-abyss/package.nls.json index 48fbcd8583a..a25492d706d 100644 --- a/extensions/theme-abyss/package.nls.json +++ b/extensions/theme-abyss/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Abyss Theme", - "description": "Abyss theme for Visual Studio Code" -} \ No newline at end of file + "description": "Abyss theme for Visual Studio Code", + "themeLabel": "Abyss" +} diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 1df39d31c90..8e97bbb6dc1 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -172,14 +172,14 @@ "scope": "invalid", "settings": { "fontStyle": "", - "foreground": "#F8F8F0" + "foreground": "#A22D44" } }, { "name": "Invalid deprecated", "scope": "invalid.deprecated", "settings": { - "foreground": "#F8F8F0" + "foreground": "#A22D44" } }, { @@ -233,6 +233,20 @@ "foreground": "#22aa44" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -242,11 +256,14 @@ } }, { - "name": "Markup Setext Header", - "scope": "markup.heading.setext", + "name": "Markup Headings", + "scope": [ + "markup.heading", + "markup.heading.setext" + ], "settings": { - "fontStyle": "", - "foreground": "#ddbb88" + "fontStyle": "bold", + "foreground": "#6688cc" } } ], @@ -373,13 +390,13 @@ "tab.inactiveBackground": "#10192c", // "tab.activeForeground": "", // "tab.inactiveForeground": "", + "tab.lastPinnedBorder": "#2b3c5d", // Workbench: Activity Bar "activityBar.background": "#051336", // "activityBar.foreground": "", // "activityBarBadge.background": "", // "activityBarBadge.foreground": "", - // "activityBar.dropBackground": "", // Workbench: Panel // "panel.background": "", @@ -434,5 +451,6 @@ "terminal.ansiBrightMagenta": "#d778ff", "terminal.ansiBrightCyan": "#78ffff", "terminal.ansiBrightWhite": "#ffffff" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json index 56d5fa3f4b1..75e1583428e 100644 --- a/extensions/theme-defaults/package.json +++ b/extensions/theme-defaults/package.json @@ -11,31 +11,31 @@ "themes": [ { "id": "Default Dark+", - "label": "Dark+ (default dark)", + "label": "%darkPlusColorThemeLabel%", "uiTheme": "vs-dark", "path": "./themes/dark_plus.json" }, { "id": "Default Light+", - "label": "Light+ (default light)", + "label": "%lightPlusColorThemeLabel%", "uiTheme": "vs", "path": "./themes/light_plus.json" }, { "id": "Visual Studio Dark", - "label": "Dark (Visual Studio)", + "label": "%darkColorThemeLabel%", "uiTheme": "vs-dark", "path": "./themes/dark_vs.json" }, { "id": "Visual Studio Light", - "label": "Light (Visual Studio)", + "label": "%lightColorThemeLabel%", "uiTheme": "vs", "path": "./themes/light_vs.json" }, { "id": "Default High Contrast", - "label": "High Contrast", + "label": "%hcColorThemeLabel%", "uiTheme": "hc-black", "path": "./themes/hc_black.json" } @@ -43,9 +43,9 @@ "iconThemes": [ { "id": "vs-minimal", - "label": "Minimal (Visual Studio Code)", + "label": "%minimalIconThemeLabel%", "path": "./fileicons/vs_minimal-icon-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-defaults/package.nls.json b/extensions/theme-defaults/package.nls.json index f03211eed2b..4cf3da530e0 100644 --- a/extensions/theme-defaults/package.nls.json +++ b/extensions/theme-defaults/package.nls.json @@ -1,4 +1,10 @@ { "displayName": "Default Themes", - "description": "The default Visual Studio light and dark themes" -} \ No newline at end of file + "description": "The default Visual Studio light and dark themes", + "darkPlusColorThemeLabel": "Dark+ (default dark)", + "lightPlusColorThemeLabel": "Light+ (default light)", + "darkColorThemeLabel": "Dark (Visual Studio)", + "lightColorThemeLabel": "Light (Visual Studio)", + "hcColorThemeLabel": "High Contrast", + "minimalIconThemeLabel": "Minimal (Visual Studio Code)" +} diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index 00c2ac8c36b..2c722c470dd 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -12,11 +12,13 @@ "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#BBBBBB", "input.placeholderForeground": "#A6A6A6", - "settings.textInputBackground": "#292929", - "settings.numberInputBackground": "#292929", "menu.background": "#252526", "menu.foreground": "#CCCCCC", "statusBarItem.remoteForeground": "#FFF", - "statusBarItem.remoteBackground": "#16825D" - } -} \ No newline at end of file + "statusBarItem.remoteBackground": "#16825D", + "sideBarSectionHeader.background": "#0000", + "sideBarSectionHeader.border": "#ccc3", + "tab.lastPinnedBorder": "#ccc3" + }, + "semanticHighlighting": true +} diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 03e62612d62..4fd89793214 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -97,6 +97,16 @@ "foreground": "#9CDCFE" } }, + { + "name": "Constants and enums", + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], + "settings": { + "foreground": "#4FC1FF", + } + }, { "name": "Object keys, TS grammar specific", "scope": [ @@ -180,5 +190,11 @@ "foreground": "#C8C8C8" } } - ] + ], + "semanticTokenColors": { + "newOperator":"#C586C0", + "stringLiteral":"#ce9178", + "customLiteral": "#DCDCAA", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 6e5f4c25f80..3a5008aef7a 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -45,7 +45,6 @@ { "scope": [ "constant.numeric", - "entity.name.operator.custom-literal.number", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" @@ -87,7 +86,6 @@ "entity.other.attribute-name.pseudo-class.css", "entity.other.attribute-name.pseudo-element.css", "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.attribute.scss", "entity.other.attribute-name.scss" ], "settings": { @@ -226,7 +224,6 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", "meta.embedded.assembly" ], "settings": { @@ -364,5 +361,11 @@ "foreground": "#569cd6" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#d4d4d4", + "stringLiteral": "#ce9178", + "customLiteral": "#D4D4D4", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index 73309c34b44..436dfa52912 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -122,5 +122,11 @@ "foreground": "#CBEDCB", } } - ] + ], + "semanticTokenColors": { + "newOperator": "#FFFFFF", + "stringLiteral": "#ce9178", + "customLiteral": "#DCDCAA", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 9d11138a99b..d0382cec294 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -105,10 +105,7 @@ "entity.other.attribute-name.parent-selector.css", "entity.other.attribute-name.pseudo-class.css", "entity.other.attribute-name.pseudo-element.css", - "source.css.less entity.other.attribute-name.id", - - "entity.other.attribute-name.attribute.scss", "entity.other.attribute-name.scss" ], "settings": { @@ -136,6 +133,7 @@ { "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#6796e6" } }, @@ -337,5 +335,6 @@ "foreground": "#569cd6" } } - ] -} \ No newline at end of file + ], + "semanticHighlighting": true +} diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index e28c9b8ed0b..0f82502db7d 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -13,9 +13,17 @@ "sideBarTitle.foreground": "#6F6F6F", "list.hoverBackground": "#E8E8E8", "input.placeholderForeground": "#767676", + "searchEditor.textInputBorder": "#CECECE", "settings.textInputBorder": "#CECECE", "settings.numberInputBorder": "#CECECE", "statusBarItem.remoteForeground": "#FFF", - "statusBarItem.remoteBackground": "#16825D" - } -} \ No newline at end of file + "statusBarItem.remoteBackground": "#16825D", + "sideBarSectionHeader.background": "#0000", + "sideBarSectionHeader.border": "#61616130", + "tab.lastPinnedBorder": "#61616130", + "notebook.focusedCellBackground": "#c8ddf150", + "notebook.cellBorderColor": "#dae3e9", + "notebook.outputContainerBackgroundColor": "#c8ddf150" + }, + "semanticHighlighting": true +} diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index faa2b836c2d..b743b1b998a 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -97,6 +97,16 @@ "foreground": "#001080" } }, + { + "name": "Constants and enums", + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], + "settings": { + "foreground": "#0070C1", + } + }, { "name": "Object keys, TS grammar specific", "scope": [ @@ -159,7 +169,7 @@ "keyword.control.anchor.regexp" ], "settings": { - "foreground": "#ff0000" + "foreground": "#EE0000" } }, { @@ -171,7 +181,7 @@ { "scope": "constant.character.escape", "settings": { - "foreground": "#ff0000" + "foreground": "#EE0000" } }, { @@ -180,5 +190,11 @@ "foreground": "#000000" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#AF00DB", + "stringLiteral": "#a31515", + "customLiteral": "#795E26", + "numberLiteral": "#098658", + } } diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index cbf19b22988..23881ae8dc7 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -45,7 +45,6 @@ { "scope": [ "constant.numeric", - "entity.name.operator.custom-literal.number", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" @@ -88,7 +87,6 @@ "entity.other.attribute-name.pseudo-class.css", "entity.other.attribute-name.pseudo-element.css", "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.attribute.scss", "entity.other.attribute-name.scss" ], "settings": { @@ -218,7 +216,6 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", "meta.embedded.assembly" ], "settings": { @@ -388,5 +385,11 @@ "foreground": "#0000ff" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#0000ff", + "stringLiteral": "#a31515", + "customLiteral": "#000000", + "numberLiteral": "#098658", + } } diff --git a/extensions/theme-kimbie-dark/package.json b/extensions/theme-kimbie-dark/package.json index 1031c34d2e7..7c7ff5ea76c 100644 --- a/extensions/theme-kimbie-dark/package.json +++ b/extensions/theme-kimbie-dark/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Kimbie Dark", + "id": "Kimbie Dark", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/kimbie-dark-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-kimbie-dark/package.nls.json b/extensions/theme-kimbie-dark/package.nls.json index 85c736cee8b..0d96b6f4a81 100644 --- a/extensions/theme-kimbie-dark/package.nls.json +++ b/extensions/theme-kimbie-dark/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Kimbie Dark Theme", - "description": "Kimbie dark theme for Visual Studio Code" -} \ No newline at end of file + "description": "Kimbie dark theme for Visual Studio Code", + "themeLabel": "Kimbie Dark" +} diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index 111a4a23d9b..3453c53dc2b 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -23,6 +23,7 @@ "editorGroupHeader.tabsBackground": "#131510", "editorLineNumber.activeForeground": "#adadad", "tab.inactiveBackground": "#131510", + "tab.lastPinnedBorder": "#51412c", "titleBar.activeBackground": "#423523", "statusBar.background": "#423523", "statusBar.debuggingBackground": "#423523", @@ -260,7 +261,7 @@ "entity.name.section" ], "settings": { - "fontStyle": "", + "fontStyle": "bold", "foreground": "#8ab1b0" } }, @@ -389,10 +390,11 @@ }, { "name": "Invalid", - "scope": "invalid.illegal", + "scope": "invalid", "settings": { "foreground": "#dc3958" } } - ] + ], + "semanticHighlighting": true } diff --git a/extensions/theme-monokai-dimmed/package.json b/extensions/theme-monokai-dimmed/package.json index 66c4711d9ab..43d950eb8c1 100644 --- a/extensions/theme-monokai-dimmed/package.json +++ b/extensions/theme-monokai-dimmed/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Monokai Dimmed", + "id": "Monokai Dimmed", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/dimmed-monokai-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-monokai-dimmed/package.nls.json b/extensions/theme-monokai-dimmed/package.nls.json index 3d93898e2ca..47baa226750 100644 --- a/extensions/theme-monokai-dimmed/package.nls.json +++ b/extensions/theme-monokai-dimmed/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Monokai Dimmed Theme", - "description": "Monokai dimmed theme for Visual Studio Code" -} \ No newline at end of file + "description": "Monokai dimmed theme for Visual Studio Code", + "themeLabel": "Monokai Dimmed" +} diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index f0b6126d5fd..bd8b896c011 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -25,6 +25,7 @@ "tab.inactiveBackground": "#404040", "tab.border": "#303030", "tab.inactiveForeground": "#d8d8d8", + "tab.lastPinnedBorder": "#505050", "peekView.border": "#3655b5", "panelTitle.activeForeground": "#ffffff", "statusBar.background": "#505050", @@ -40,9 +41,24 @@ "menu.background": "#272727", "menu.foreground": "#CCCCCC", "pickerGroup.foreground": "#b0b0b0", - "terminal.ansiWhite": "#ffffff", "inputOption.activeBorder": "#3655b5", - "focusBorder": "#3655b5" + "focusBorder": "#3655b5", + "terminal.ansiBlack": "#1e1e1e", + "terminal.ansiRed": "#C4265E", // the bright color with ~75% transparent on the background + "terminal.ansiGreen": "#86B42B", + "terminal.ansiYellow": "#B3B42B", + "terminal.ansiBlue": "#6A7EC8", + "terminal.ansiMagenta": "#8C6BC8", + "terminal.ansiCyan": "#56ADBC", + "terminal.ansiWhite": "#e3e3dd", + "terminal.ansiBrightBlack": "#666666", + "terminal.ansiBrightRed": "#f92672", + "terminal.ansiBrightGreen": "#A6E22E", + "terminal.ansiBrightYellow": "#e2e22e", // hue shifted #A6E22E + "terminal.ansiBrightBlue": "#819aff", // hue shifted #AE81FF + "terminal.ansiBrightMagenta": "#AE81FF", + "terminal.ansiBrightCyan": "#66D9EF", + "terminal.ansiBrightWhite": "#f8f8f2" }, "tokenColors": [ { @@ -541,6 +557,65 @@ "foreground": "#D0B344" } }, + { + "name": "Markdown Headings", + "scope": "markup.heading.markdown", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markdown Quote", + "scope": "markup.quote.markdown", + "settings": { + "fontStyle": "italic", + "foreground": "" + } + }, + { + "name": "Markdown Bold", + "scope": "markup.bold.markdown", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markdown Link Title/Description", + "scope": "string.other.link.title.markdown,string.other.link.description.markdown", + "settings": { + "foreground": "#AE81FF" + } + }, + { + "name": "Markdown Underline Link/Image", + "scope": "markup.underline.link.markdown,markup.underline.link.image.markdown", + "settings": { + "foreground": "" + } + }, + { + "name": "Markdown Emphasis", + "scope": "markup.italic.markdown", + "settings": { + "fontStyle": "italic" + } + }, + { + "name": "Markdown Punctuation Definition Link", + "scope": "markup.list.unnumbered.markdown, markup.list.numbered.markdown", + "settings": { + "foreground": "" + } + }, + { + "name": "Markdown List Punctuation", + "scope": [ + "punctuation.definition.list.begin.markdown" + ], + "settings": { + "foreground": "" + } + }, { "scope": "token.info-token", "settings": { @@ -572,5 +647,6 @@ "foreground": "#c7444a" } } - ] + ], + "semanticHighlighting": true } diff --git a/extensions/theme-monokai/package.json b/extensions/theme-monokai/package.json index 13b2db10d00..b21aded1b49 100644 --- a/extensions/theme-monokai/package.json +++ b/extensions/theme-monokai/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Monokai", + "id": "Monokai", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/monokai-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-monokai/package.nls.json b/extensions/theme-monokai/package.nls.json index 8e8d73c7568..a5a17dc5717 100644 --- a/extensions/theme-monokai/package.nls.json +++ b/extensions/theme-monokai/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Monokai Theme", - "description": "Monokai theme for Visual Studio Code" -} \ No newline at end of file + "description": "Monokai theme for Visual Studio Code", + "themeLabel": "Monokai" +} diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index c695640f299..2e520d15bdb 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -11,8 +11,6 @@ "list.activeSelectionBackground": "#75715E", "list.focusBackground": "#414339", "dropdown.listBackground": "#1e1f1c", - "settings.textInputBackground": "#32342d", - "settings.numberInputBackground": "#32342d", "list.inactiveSelectionBackground": "#414339", "list.hoverBackground": "#3e3d32", "list.dropBackground": "#414339", @@ -37,6 +35,7 @@ "tab.inactiveBackground": "#34352f", "tab.border": "#1e1f1c", "tab.inactiveForeground": "#ccccc7", // needs to be bright so it's readable when another editor group is focused + "tab.lastPinnedBorder": "#414339", "widget.shadow": "#000000", "progressBar.background": "#75715E", "badge.background": "#75715E", @@ -46,6 +45,7 @@ "panelTitle.activeBorder": "#75715E", "panelTitle.inactiveForeground": "#75715E", "panel.border": "#414339", + "settings.focusedRowBackground": "#4143395A", "titleBar.activeBackground": "#1e1f1c", "statusBar.background": "#414339", "statusBar.noFolderBackground": "#414339", @@ -53,7 +53,6 @@ "statusBarItem.remoteBackground": "#AC6218", "activityBar.background": "#272822", "activityBar.foreground": "#f8f8f2", - "activityBar.dropBackground": "#414339", "sideBar.background": "#1e1f1c", "sideBarSectionHeader.background": "#272822", "menu.background": "#1e1f1c", @@ -285,14 +284,14 @@ "scope": "invalid", "settings": { "fontStyle": "", - "foreground": "#F8F8F0" + "foreground": "#F44747" } }, { "name": "Invalid deprecated", "scope": "invalid.deprecated", "settings": { - "foreground": "#F8F8F0" + "foreground": "#F44747" } }, { @@ -476,5 +475,6 @@ "foreground": "#FD971F" } } - ] + ], + "semanticHighlighting": true } diff --git a/extensions/theme-quietlight/package.json b/extensions/theme-quietlight/package.json index 0263925eee8..f2e66e7a95d 100644 --- a/extensions/theme-quietlight/package.json +++ b/extensions/theme-quietlight/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Quiet Light", + "id": "Quiet Light", + "label": "%themeLabel%", "uiTheme": "vs", "path": "./themes/quietlight-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-quietlight/package.nls.json b/extensions/theme-quietlight/package.nls.json index 1873df058e7..30354d62952 100644 --- a/extensions/theme-quietlight/package.nls.json +++ b/extensions/theme-quietlight/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Quiet Light Theme", - "description": "Quiet light theme for Visual Studio Code" -} \ No newline at end of file + "description": "Quiet light theme for Visual Studio Code", + "themeLabel": "Quiet Light" +} diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index ae19ba7889b..6156e6542a7 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -45,6 +45,13 @@ "foreground": "#448C27" } }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "foreground": "#cd3131" + } + }, { "name": "Invalid - Illegal", "scope": "invalid.illegal", @@ -472,6 +479,7 @@ "peekViewResult.background": "#F2F8FC", "peekView.border": "#705697", "peekViewResult.matchHighlightBackground": "#93C6D6", + "tab.lastPinnedBorder": "#c9d0d9", "statusBar.background": "#705697", "statusBar.noFolderBackground": "#705697", "statusBar.debuggingBackground": "#705697", @@ -494,5 +502,6 @@ "walkThrough.embeddedEditorBackground": "#00000014", "editorIndentGuide.background": "#aaaaaa60", "editorIndentGuide.activeBackground": "#777777b0" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-red/package.json b/extensions/theme-red/package.json index ba751a33e42..a9920fdfd0e 100644 --- a/extensions/theme-red/package.json +++ b/extensions/theme-red/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Red", + "id": "Red", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/Red-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-red/package.nls.json b/extensions/theme-red/package.nls.json index 680fde603ec..d58a547e8a3 100644 --- a/extensions/theme-red/package.nls.json +++ b/extensions/theme-red/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Red Theme", - "description": "Red theme for Visual Studio Code" -} \ No newline at end of file + "description": "Red theme for Visual Studio Code", + "themeLabel": "Red" +} diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 277e7a8db3f..8eeda13456e 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -5,6 +5,7 @@ "activityBar.background": "#580000", "tab.inactiveBackground": "#300a0a", "tab.activeBackground": "#490000", + "tab.lastPinnedBorder": "#ff000044", "sideBar.background": "#330000", "statusBar.background": "#700000", "statusBar.noFolderBackground": "#700000", @@ -192,7 +193,7 @@ }, { "name": "Support.constant", - "scope": "support.constant", + "scope": [ "support.constant", "support.variable"], "settings": { "fontStyle": "", "foreground": "#eb939aff" @@ -350,6 +351,20 @@ "foreground": "#fb9a4bff" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -359,17 +374,15 @@ } }, { - "name": "Markup Headings", - "scope": "markup.heading", + "name": "Headings", + "scope": [ + "markup.heading", + "markup.heading.setext", + "punctuation.definition.heading", + "entity.name.section" + ], "settings": { - "foreground": "#fec758ff" - } - }, - { - "name": "Markup Setext Header", - "scope": "markup.heading.setext", - "settings": { - "fontStyle": "", + "fontStyle": "bold", "foreground": "#fec758ff" } }, @@ -385,5 +398,6 @@ "foreground": "#ec0d1e" } } - ] + ], + "semanticHighlighting": true } diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 3e14951e342..85228d704bf 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -10,8 +10,8 @@ let fs = require('fs'); let https = require('https'); let url = require('url'); -// list of languagesIs not shipped with VSCode. The information is used to associate an icon with a language association -let nonBuiltInLanguages = { // { fileNames, extensions } +// list of languagesId not shipped with VSCode. The information is used to associate an icon with a language association +let nonBuiltInLanguages = { // { fileNames, extensions } "r": { extensions: ['r', 'rhistory', 'rprofile', 'rt'] }, "argdown": { extensions: ['ad', 'adown', 'argdown', 'argdn'] }, "elm": { extensions: ['elm'] }, @@ -32,10 +32,16 @@ let nonBuiltInLanguages = { // { fileNames, extensions } "haml": { extensions: ['haml'] }, "stylus": { extensions: ['styl'] }, "vala": { extensions: ['vala'] }, - "todo": { fileNames: ['todo'] }, - "jsonc": { extensions: ['json'] } + "todo": { fileNames: ['todo'] } }; +// list of languagesId that inherit the icon from another language +let inheritIconFromLanguage = { + "jsonc": 'json', + "postcss": 'css', + "django-html": 'html' +} + let FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo let font, fontMappingsFile, fileAssociationFile, colorsFile; @@ -299,7 +305,7 @@ exports.update = function () { } return download(fileAssociationFile).then(function (content) { - let regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; + let regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.+]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; while ((match = regex2.exec(content)) !== null) { let pattern = match[1]; let def = '_' + match[2]; @@ -358,6 +364,16 @@ exports.update = function () { } } } + for (let lang in inheritIconFromLanguage) { + let superLang = inheritIconFromLanguage[lang]; + let def = lang2Def[superLang]; + if (def) { + lang2Def[lang] = def; + } else { + console.log('skipping icon def for ' + lang + ': no icon for ' + superLang + ' defined'); + } + + } return download(colorsFile).then(function (content) { diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 1c86b8bcb2b..20f54daa8ba 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "85a222708824c6f19bbecbec71633d2c97077dad" + "commitHash": "7ea773d195eac3f40261897b49a2499815e9346c" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index b85727d01e4..021e5270200 100644 Binary files a/extensions/theme-seti/icons/seti.woff and b/extensions/theme-seti/icons/seti.woff differ diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 825ac52ee83..bca70445720 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -166,1191 +166,1231 @@ "fontCharacter": "\\E012", "fontColor": "#8dc149" }, - "_coffee_light": { + "_code-search_light": { "fontCharacter": "\\E013", + "fontColor": "#9068b0" + }, + "_code-search": { + "fontCharacter": "\\E013", + "fontColor": "#a074c4" + }, + "_coffee_light": { + "fontCharacter": "\\E014", "fontColor": "#b7b73b" }, "_coffee": { - "fontCharacter": "\\E013", + "fontCharacter": "\\E014", "fontColor": "#cbcb41" }, "_coldfusion_light": { - "fontCharacter": "\\E015", + "fontCharacter": "\\E016", "fontColor": "#498ba7" }, "_coldfusion": { - "fontCharacter": "\\E015", + "fontCharacter": "\\E016", "fontColor": "#519aba" }, "_config_light": { - "fontCharacter": "\\E016", + "fontCharacter": "\\E017", "fontColor": "#627379" }, "_config": { - "fontCharacter": "\\E016", + "fontCharacter": "\\E017", "fontColor": "#6d8086" }, "_cpp_light": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#498ba7" }, "_cpp": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#519aba" }, "_cpp_1_light": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#9068b0" }, "_cpp_1": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#a074c4" }, "_cpp_2_light": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#b7b73b" }, "_cpp_2": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#cbcb41" }, "_crystal_light": { - "fontCharacter": "\\E018", + "fontCharacter": "\\E019", "fontColor": "#bfc2c1" }, "_crystal": { - "fontCharacter": "\\E018", + "fontCharacter": "\\E019", "fontColor": "#d4d7d6" }, "_crystal_embedded_light": { - "fontCharacter": "\\E019", + "fontCharacter": "\\E01A", "fontColor": "#bfc2c1" }, "_crystal_embedded": { - "fontCharacter": "\\E019", + "fontCharacter": "\\E01A", "fontColor": "#d4d7d6" }, "_css_light": { - "fontCharacter": "\\E01A", + "fontCharacter": "\\E01B", "fontColor": "#498ba7" }, "_css": { - "fontCharacter": "\\E01A", + "fontCharacter": "\\E01B", "fontColor": "#519aba" }, "_csv_light": { - "fontCharacter": "\\E01B", + "fontCharacter": "\\E01C", "fontColor": "#7fae42" }, "_csv": { - "fontCharacter": "\\E01B", + "fontCharacter": "\\E01C", "fontColor": "#8dc149" }, + "_cu_light": { + "fontCharacter": "\\E01D", + "fontColor": "#7fae42" + }, + "_cu": { + "fontCharacter": "\\E01D", + "fontColor": "#8dc149" + }, + "_cu_1_light": { + "fontCharacter": "\\E01D", + "fontColor": "#9068b0" + }, + "_cu_1": { + "fontCharacter": "\\E01D", + "fontColor": "#a074c4" + }, "_d_light": { - "fontCharacter": "\\E01C", + "fontCharacter": "\\E01E", "fontColor": "#b8383d" }, "_d": { - "fontCharacter": "\\E01C", + "fontCharacter": "\\E01E", "fontColor": "#cc3e44" }, "_dart_light": { - "fontCharacter": "\\E01D", + "fontCharacter": "\\E01F", "fontColor": "#498ba7" }, "_dart": { - "fontCharacter": "\\E01D", + "fontCharacter": "\\E01F", "fontColor": "#519aba" }, "_db_light": { - "fontCharacter": "\\E01E", + "fontCharacter": "\\E020", "fontColor": "#dd4b78" }, "_db": { - "fontCharacter": "\\E01E", + "fontCharacter": "\\E020", "fontColor": "#f55385" }, "_default_light": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E021", "fontColor": "#bfc2c1" }, "_default": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E021", "fontColor": "#d4d7d6" }, "_docker_light": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#498ba7" }, "_docker": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#519aba" }, "_docker_1_light": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#455155" }, "_docker_1": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#4d5a5e" }, "_docker_2_light": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#7fae42" }, "_docker_2": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#8dc149" }, "_docker_3_light": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#dd4b78" }, "_docker_3": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E023", "fontColor": "#f55385" }, "_ejs_light": { - "fontCharacter": "\\E023", + "fontCharacter": "\\E025", "fontColor": "#b7b73b" }, "_ejs": { - "fontCharacter": "\\E023", + "fontCharacter": "\\E025", "fontColor": "#cbcb41" }, "_elixir_light": { - "fontCharacter": "\\E024", + "fontCharacter": "\\E026", "fontColor": "#9068b0" }, "_elixir": { - "fontCharacter": "\\E024", + "fontCharacter": "\\E026", "fontColor": "#a074c4" }, "_elixir_script_light": { - "fontCharacter": "\\E025", + "fontCharacter": "\\E027", "fontColor": "#9068b0" }, "_elixir_script": { - "fontCharacter": "\\E025", + "fontCharacter": "\\E027", "fontColor": "#a074c4" }, "_elm_light": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E028", "fontColor": "#498ba7" }, "_elm": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E028", "fontColor": "#519aba" }, "_eslint_light": { - "fontCharacter": "\\E028", + "fontCharacter": "\\E02A", "fontColor": "#9068b0" }, "_eslint": { - "fontCharacter": "\\E028", + "fontCharacter": "\\E02A", "fontColor": "#a074c4" }, "_eslint_1_light": { - "fontCharacter": "\\E028", + "fontCharacter": "\\E02A", "fontColor": "#455155" }, "_eslint_1": { - "fontCharacter": "\\E028", + "fontCharacter": "\\E02A", "fontColor": "#4d5a5e" }, "_ethereum_light": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02B", "fontColor": "#498ba7" }, "_ethereum": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02B", "fontColor": "#519aba" }, "_f-sharp_light": { - "fontCharacter": "\\E02A", + "fontCharacter": "\\E02C", "fontColor": "#498ba7" }, "_f-sharp": { - "fontCharacter": "\\E02A", + "fontCharacter": "\\E02C", "fontColor": "#519aba" }, "_favicon_light": { - "fontCharacter": "\\E02B", + "fontCharacter": "\\E02D", "fontColor": "#b7b73b" }, "_favicon": { - "fontCharacter": "\\E02B", + "fontCharacter": "\\E02D", "fontColor": "#cbcb41" }, "_firebase_light": { - "fontCharacter": "\\E02C", + "fontCharacter": "\\E02E", "fontColor": "#cc6d2e" }, "_firebase": { - "fontCharacter": "\\E02C", + "fontCharacter": "\\E02E", "fontColor": "#e37933" }, "_firefox_light": { - "fontCharacter": "\\E02D", + "fontCharacter": "\\E02F", "fontColor": "#cc6d2e" }, "_firefox": { - "fontCharacter": "\\E02D", + "fontCharacter": "\\E02F", "fontColor": "#e37933" }, "_font_light": { - "fontCharacter": "\\E02F", + "fontCharacter": "\\E031", "fontColor": "#b8383d" }, "_font": { - "fontCharacter": "\\E02F", + "fontCharacter": "\\E031", "fontColor": "#cc3e44" }, "_git_light": { - "fontCharacter": "\\E030", + "fontCharacter": "\\E032", "fontColor": "#3b4b52" }, "_git": { - "fontCharacter": "\\E030", + "fontCharacter": "\\E032", "fontColor": "#41535b" }, "_go_light": { - "fontCharacter": "\\E034", + "fontCharacter": "\\E036", "fontColor": "#498ba7" }, "_go": { - "fontCharacter": "\\E034", + "fontCharacter": "\\E036", "fontColor": "#519aba" }, "_go2_light": { - "fontCharacter": "\\E035", + "fontCharacter": "\\E037", "fontColor": "#498ba7" }, "_go2": { - "fontCharacter": "\\E035", + "fontCharacter": "\\E037", "fontColor": "#519aba" }, "_gradle_light": { - "fontCharacter": "\\E036", - "fontColor": "#7fae42" + "fontCharacter": "\\E038", + "fontColor": "#498ba7" }, "_gradle": { - "fontCharacter": "\\E036", - "fontColor": "#8dc149" + "fontCharacter": "\\E038", + "fontColor": "#519aba" }, "_grails_light": { - "fontCharacter": "\\E037", + "fontCharacter": "\\E039", "fontColor": "#7fae42" }, "_grails": { - "fontCharacter": "\\E037", + "fontCharacter": "\\E039", "fontColor": "#8dc149" }, "_graphql_light": { - "fontCharacter": "\\E038", + "fontCharacter": "\\E03A", "fontColor": "#dd4b78" }, "_graphql": { - "fontCharacter": "\\E038", + "fontCharacter": "\\E03A", "fontColor": "#f55385" }, "_grunt_light": { - "fontCharacter": "\\E039", + "fontCharacter": "\\E03B", "fontColor": "#cc6d2e" }, "_grunt": { - "fontCharacter": "\\E039", + "fontCharacter": "\\E03B", "fontColor": "#e37933" }, "_gulp_light": { - "fontCharacter": "\\E03A", + "fontCharacter": "\\E03C", "fontColor": "#b8383d" }, "_gulp": { - "fontCharacter": "\\E03A", + "fontCharacter": "\\E03C", "fontColor": "#cc3e44" }, "_haml_light": { - "fontCharacter": "\\E03C", + "fontCharacter": "\\E03E", "fontColor": "#b8383d" }, "_haml": { - "fontCharacter": "\\E03C", + "fontCharacter": "\\E03E", "fontColor": "#cc3e44" }, "_happenings_light": { - "fontCharacter": "\\E03D", + "fontCharacter": "\\E03F", "fontColor": "#498ba7" }, "_happenings": { - "fontCharacter": "\\E03D", + "fontCharacter": "\\E03F", "fontColor": "#519aba" }, "_haskell_light": { - "fontCharacter": "\\E03E", + "fontCharacter": "\\E040", "fontColor": "#9068b0" }, "_haskell": { - "fontCharacter": "\\E03E", + "fontCharacter": "\\E040", "fontColor": "#a074c4" }, "_haxe_light": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#cc6d2e" }, "_haxe": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#e37933" }, "_haxe_1_light": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#b7b73b" }, "_haxe_1": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#cbcb41" }, "_haxe_2_light": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#498ba7" }, "_haxe_2": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#519aba" }, "_haxe_3_light": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#9068b0" }, "_haxe_3": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E041", "fontColor": "#a074c4" }, "_heroku_light": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E042", "fontColor": "#9068b0" }, "_heroku": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E042", "fontColor": "#a074c4" }, "_hex_light": { - "fontCharacter": "\\E041", + "fontCharacter": "\\E043", "fontColor": "#b8383d" }, "_hex": { - "fontCharacter": "\\E041", + "fontCharacter": "\\E043", "fontColor": "#cc3e44" }, "_html_light": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#498ba7" }, "_html": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#519aba" }, "_html_1_light": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#7fae42" }, "_html_1": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#8dc149" }, "_html_2_light": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#b7b73b" }, "_html_2": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#cbcb41" }, "_html_3_light": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#cc6d2e" }, "_html_3": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E044", "fontColor": "#e37933" }, "_html_erb_light": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E045", "fontColor": "#b8383d" }, "_html_erb": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E045", "fontColor": "#cc3e44" }, "_ignored_light": { - "fontCharacter": "\\E044", + "fontCharacter": "\\E046", "fontColor": "#3b4b52" }, "_ignored": { - "fontCharacter": "\\E044", + "fontCharacter": "\\E046", "fontColor": "#41535b" }, "_illustrator_light": { - "fontCharacter": "\\E045", + "fontCharacter": "\\E047", "fontColor": "#b7b73b" }, "_illustrator": { - "fontCharacter": "\\E045", + "fontCharacter": "\\E047", "fontColor": "#cbcb41" }, "_image_light": { - "fontCharacter": "\\E046", + "fontCharacter": "\\E048", "fontColor": "#9068b0" }, "_image": { - "fontCharacter": "\\E046", + "fontCharacter": "\\E048", "fontColor": "#a074c4" }, "_info_light": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E049", "fontColor": "#498ba7" }, "_info": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E049", "fontColor": "#519aba" }, "_ionic_light": { - "fontCharacter": "\\E048", + "fontCharacter": "\\E04A", "fontColor": "#498ba7" }, "_ionic": { - "fontCharacter": "\\E048", + "fontCharacter": "\\E04A", "fontColor": "#519aba" }, "_jade_light": { - "fontCharacter": "\\E049", + "fontCharacter": "\\E04B", "fontColor": "#b8383d" }, "_jade": { - "fontCharacter": "\\E049", + "fontCharacter": "\\E04B", "fontColor": "#cc3e44" }, "_java_light": { - "fontCharacter": "\\E04A", + "fontCharacter": "\\E04C", "fontColor": "#b8383d" }, "_java": { - "fontCharacter": "\\E04A", + "fontCharacter": "\\E04C", "fontColor": "#cc3e44" }, "_javascript_light": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04D", "fontColor": "#b7b73b" }, "_javascript": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04D", "fontColor": "#cbcb41" }, "_javascript_1_light": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04D", "fontColor": "#cc6d2e" }, "_javascript_1": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04D", "fontColor": "#e37933" }, "_javascript_2_light": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04D", "fontColor": "#498ba7" }, "_javascript_2": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04D", "fontColor": "#519aba" }, "_jenkins_light": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04E", "fontColor": "#b8383d" }, "_jenkins": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04E", "fontColor": "#cc3e44" }, "_jinja_light": { - "fontCharacter": "\\E04D", + "fontCharacter": "\\E04F", "fontColor": "#b8383d" }, "_jinja": { - "fontCharacter": "\\E04D", + "fontCharacter": "\\E04F", "fontColor": "#cc3e44" }, "_json_light": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#b7b73b" }, "_json": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#cbcb41" }, "_json_1_light": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#7fae42" }, "_json_1": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#8dc149" }, "_julia_light": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E052", "fontColor": "#9068b0" }, "_julia": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E052", "fontColor": "#a074c4" }, "_karma_light": { - "fontCharacter": "\\E051", + "fontCharacter": "\\E053", "fontColor": "#7fae42" }, "_karma": { - "fontCharacter": "\\E051", + "fontCharacter": "\\E053", "fontColor": "#8dc149" }, "_kotlin_light": { - "fontCharacter": "\\E052", + "fontCharacter": "\\E054", "fontColor": "#cc6d2e" }, "_kotlin": { - "fontCharacter": "\\E052", + "fontCharacter": "\\E054", "fontColor": "#e37933" }, "_less_light": { - "fontCharacter": "\\E053", + "fontCharacter": "\\E055", "fontColor": "#498ba7" }, "_less": { - "fontCharacter": "\\E053", + "fontCharacter": "\\E055", "fontColor": "#519aba" }, "_license_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#b7b73b" }, "_license": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#cbcb41" }, "_license_1_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#cc6d2e" }, "_license_1": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#e37933" }, "_license_2_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#b8383d" }, "_license_2": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#cc3e44" }, "_liquid_light": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E057", "fontColor": "#7fae42" }, "_liquid": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E057", "fontColor": "#8dc149" }, "_livescript_light": { - "fontCharacter": "\\E056", + "fontCharacter": "\\E058", "fontColor": "#498ba7" }, "_livescript": { - "fontCharacter": "\\E056", + "fontCharacter": "\\E058", "fontColor": "#519aba" }, "_lock_light": { - "fontCharacter": "\\E057", + "fontCharacter": "\\E059", "fontColor": "#7fae42" }, "_lock": { - "fontCharacter": "\\E057", + "fontCharacter": "\\E059", "fontColor": "#8dc149" }, "_lua_light": { - "fontCharacter": "\\E058", + "fontCharacter": "\\E05A", "fontColor": "#498ba7" }, "_lua": { - "fontCharacter": "\\E058", + "fontCharacter": "\\E05A", "fontColor": "#519aba" }, "_makefile_light": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#cc6d2e" }, "_makefile": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#e37933" }, "_makefile_1_light": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#9068b0" }, "_makefile_1": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#a074c4" }, "_makefile_2_light": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#627379" }, "_makefile_2": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#6d8086" }, "_makefile_3_light": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#498ba7" }, "_makefile_3": { - "fontCharacter": "\\E059", + "fontCharacter": "\\E05B", "fontColor": "#519aba" }, "_markdown_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05C", "fontColor": "#498ba7" }, "_markdown": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05C", "fontColor": "#519aba" }, "_maven_light": { - "fontCharacter": "\\E05B", + "fontCharacter": "\\E05D", "fontColor": "#b8383d" }, "_maven": { - "fontCharacter": "\\E05B", + "fontCharacter": "\\E05D", "fontColor": "#cc3e44" }, "_mdo_light": { - "fontCharacter": "\\E05C", + "fontCharacter": "\\E05E", "fontColor": "#b8383d" }, "_mdo": { - "fontCharacter": "\\E05C", + "fontCharacter": "\\E05E", "fontColor": "#cc3e44" }, "_mustache_light": { - "fontCharacter": "\\E05D", + "fontCharacter": "\\E05F", "fontColor": "#cc6d2e" }, "_mustache": { - "fontCharacter": "\\E05D", + "fontCharacter": "\\E05F", "fontColor": "#e37933" }, + "_nim_light": { + "fontCharacter": "\\E061", + "fontColor": "#b7b73b" + }, + "_nim": { + "fontCharacter": "\\E061", + "fontColor": "#cbcb41" + }, "_npm_light": { - "fontCharacter": "\\E05F", + "fontCharacter": "\\E062", "fontColor": "#3b4b52" }, "_npm": { - "fontCharacter": "\\E05F", + "fontCharacter": "\\E062", "fontColor": "#41535b" }, "_npm_1_light": { - "fontCharacter": "\\E05F", + "fontCharacter": "\\E062", "fontColor": "#b8383d" }, "_npm_1": { - "fontCharacter": "\\E05F", + "fontCharacter": "\\E062", "fontColor": "#cc3e44" }, "_npm_ignored_light": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E063", "fontColor": "#3b4b52" }, "_npm_ignored": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E063", "fontColor": "#41535b" }, "_nunjucks_light": { - "fontCharacter": "\\E061", + "fontCharacter": "\\E064", "fontColor": "#7fae42" }, "_nunjucks": { - "fontCharacter": "\\E061", + "fontCharacter": "\\E064", "fontColor": "#8dc149" }, "_ocaml_light": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E065", "fontColor": "#cc6d2e" }, "_ocaml": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E065", "fontColor": "#e37933" }, "_odata_light": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E066", "fontColor": "#cc6d2e" }, "_odata": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E066", "fontColor": "#e37933" }, "_pddl_light": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E067", "fontColor": "#9068b0" }, "_pddl": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E067", "fontColor": "#a074c4" }, "_pdf_light": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E068", "fontColor": "#b8383d" }, "_pdf": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E068", "fontColor": "#cc3e44" }, "_perl_light": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E069", "fontColor": "#498ba7" }, "_perl": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E069", "fontColor": "#519aba" }, "_photoshop_light": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E06A", "fontColor": "#498ba7" }, "_photoshop": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E06A", "fontColor": "#519aba" }, "_php_light": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E06B", "fontColor": "#9068b0" }, "_php": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E06B", "fontColor": "#a074c4" }, "_plan_light": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06C", "fontColor": "#7fae42" }, "_plan": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06C", "fontColor": "#8dc149" }, "_platformio_light": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06D", "fontColor": "#cc6d2e" }, "_platformio": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06D", "fontColor": "#e37933" }, "_powershell_light": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06E", "fontColor": "#498ba7" }, "_powershell": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06E", "fontColor": "#519aba" }, + "_prolog_light": { + "fontCharacter": "\\E070", + "fontColor": "#cc6d2e" + }, + "_prolog": { + "fontCharacter": "\\E070", + "fontColor": "#e37933" + }, "_pug_light": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E071", "fontColor": "#b8383d" }, "_pug": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E071", "fontColor": "#cc3e44" }, "_puppet_light": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E072", "fontColor": "#b7b73b" }, "_puppet": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E072", "fontColor": "#cbcb41" }, "_python_light": { - "fontCharacter": "\\E06F", + "fontCharacter": "\\E073", "fontColor": "#498ba7" }, "_python": { - "fontCharacter": "\\E06F", + "fontCharacter": "\\E073", "fontColor": "#519aba" }, "_react_light": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E075", "fontColor": "#498ba7" }, "_react": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E075", "fontColor": "#519aba" }, "_react_1_light": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E075", "fontColor": "#cc6d2e" }, "_react_1": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E075", "fontColor": "#e37933" }, "_react_2_light": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E075", "fontColor": "#b7b73b" }, "_react_2": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E075", "fontColor": "#cbcb41" }, "_reasonml_light": { - "fontCharacter": "\\E072", + "fontCharacter": "\\E076", "fontColor": "#b8383d" }, "_reasonml": { - "fontCharacter": "\\E072", + "fontCharacter": "\\E076", "fontColor": "#cc3e44" }, "_rollup_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E077", "fontColor": "#b8383d" }, "_rollup": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E077", "fontColor": "#cc3e44" }, "_ruby_light": { - "fontCharacter": "\\E074", + "fontCharacter": "\\E078", "fontColor": "#b8383d" }, "_ruby": { - "fontCharacter": "\\E074", + "fontCharacter": "\\E078", "fontColor": "#cc3e44" }, "_rust_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E079", "fontColor": "#627379" }, "_rust": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E079", "fontColor": "#6d8086" }, "_salesforce_light": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E07A", "fontColor": "#498ba7" }, "_salesforce": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E07A", "fontColor": "#519aba" }, "_sass_light": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E07B", "fontColor": "#dd4b78" }, "_sass": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E07B", "fontColor": "#f55385" }, "_sbt_light": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07C", "fontColor": "#498ba7" }, "_sbt": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07C", "fontColor": "#519aba" }, "_scala_light": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07D", "fontColor": "#b8383d" }, "_scala": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07D", "fontColor": "#cc3e44" }, "_shell_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E080", "fontColor": "#455155" }, "_shell": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E080", "fontColor": "#4d5a5e" }, "_slim_light": { - "fontCharacter": "\\E07D", + "fontCharacter": "\\E081", "fontColor": "#cc6d2e" }, "_slim": { - "fontCharacter": "\\E07D", + "fontCharacter": "\\E081", "fontColor": "#e37933" }, "_smarty_light": { - "fontCharacter": "\\E07E", + "fontCharacter": "\\E082", "fontColor": "#b7b73b" }, "_smarty": { - "fontCharacter": "\\E07E", + "fontCharacter": "\\E082", "fontColor": "#cbcb41" }, "_spring_light": { - "fontCharacter": "\\E07F", + "fontCharacter": "\\E083", "fontColor": "#7fae42" }, "_spring": { - "fontCharacter": "\\E07F", + "fontCharacter": "\\E083", "fontColor": "#8dc149" }, "_stylelint_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E084", "fontColor": "#bfc2c1" }, "_stylelint": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E084", "fontColor": "#d4d7d6" }, "_stylelint_1_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E084", "fontColor": "#455155" }, "_stylelint_1": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E084", "fontColor": "#4d5a5e" }, "_stylus_light": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E085", "fontColor": "#7fae42" }, "_stylus": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E085", "fontColor": "#8dc149" }, "_sublime_light": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E086", "fontColor": "#cc6d2e" }, "_sublime": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E086", "fontColor": "#e37933" }, "_svg_light": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E087", "fontColor": "#9068b0" }, "_svg": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E087", "fontColor": "#a074c4" }, "_svg_1_light": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E087", "fontColor": "#498ba7" }, "_svg_1": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E087", "fontColor": "#519aba" }, "_swift_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E088", "fontColor": "#cc6d2e" }, "_swift": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E088", "fontColor": "#e37933" }, "_terraform_light": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E089", "fontColor": "#9068b0" }, "_terraform": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E089", "fontColor": "#a074c4" }, "_tex_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#498ba7" }, "_tex": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#519aba" }, "_tex_1_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#b7b73b" }, "_tex_1": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#cbcb41" }, "_tex_2_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#cc6d2e" }, "_tex_2": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#e37933" }, "_tex_3_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#bfc2c1" }, "_tex_3": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08A", "fontColor": "#d4d7d6" }, "_todo": { - "fontCharacter": "\\E088" + "fontCharacter": "\\E08C" }, "_tsconfig_light": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E08D", "fontColor": "#498ba7" }, "_tsconfig": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E08D", "fontColor": "#519aba" }, "_twig_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08E", "fontColor": "#7fae42" }, "_twig": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08E", "fontColor": "#8dc149" }, "_typescript_light": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E08F", "fontColor": "#498ba7" }, "_typescript": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E08F", "fontColor": "#519aba" }, "_typescript_1_light": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E08F", "fontColor": "#b7b73b" }, "_typescript_1": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E08F", "fontColor": "#cbcb41" }, "_vala_light": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E090", "fontColor": "#627379" }, "_vala": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E090", "fontColor": "#6d8086" }, "_video_light": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E091", "fontColor": "#dd4b78" }, "_video": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E091", "fontColor": "#f55385" }, "_vue_light": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E092", "fontColor": "#7fae42" }, "_vue": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E092", "fontColor": "#8dc149" }, "_wasm_light": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E093", "fontColor": "#9068b0" }, "_wasm": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E093", "fontColor": "#a074c4" }, "_wat_light": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E094", "fontColor": "#9068b0" }, "_wat": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E094", "fontColor": "#a074c4" }, "_webpack_light": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E095", "fontColor": "#498ba7" }, "_webpack": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E095", "fontColor": "#519aba" }, "_wgt_light": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E096", "fontColor": "#498ba7" }, "_wgt": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E096", "fontColor": "#519aba" }, "_windows_light": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E097", "fontColor": "#498ba7" }, "_windows": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E097", "fontColor": "#519aba" }, "_word_light": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E098", "fontColor": "#498ba7" }, "_word": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E098", "fontColor": "#519aba" }, "_xls_light": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E099", "fontColor": "#7fae42" }, "_xls": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E099", "fontColor": "#8dc149" }, "_xml_light": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E09A", "fontColor": "#cc6d2e" }, "_xml": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E09A", "fontColor": "#e37933" }, "_yarn_light": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E09B", "fontColor": "#498ba7" }, "_yarn": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E09B", "fontColor": "#519aba" }, "_yml_light": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E09C", "fontColor": "#9068b0" }, "_yml": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E09C", "fontColor": "#a074c4" }, "_zip_light": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09D", "fontColor": "#b8383d" }, "_zip": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09D", "fontColor": "#cc3e44" }, "_zip_1_light": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09D", "fontColor": "#627379" }, "_zip_1": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09D", "fontColor": "#6d8086" } }, @@ -1368,9 +1408,11 @@ "hh": "_cpp_1", "hpp": "_cpp_1", "hxx": "_cpp_1", + "h++": "_cpp_1", "edn": "_clojure_1", "cfc": "_coldfusion", "cfm": "_coldfusion", + "litcoffee": "_coffee", "config": "_config", "cfg": "_config", "conf": "_config", @@ -1383,6 +1425,9 @@ "csv": "_csv", "xls": "_xls", "xlsx": "_xls", + "cu": "_cu", + "cuh": "_cu_1", + "hu": "_cu_1", "cake": "_cake", "ctp": "_cake_php", "d": "_d", @@ -1411,8 +1456,10 @@ "hxs": "_haxe_1", "hxp": "_haxe_2", "hxml": "_haxe_3", + "jade": "_jade", "class": "_java", "classpath": "_java", + "properties": "_java", "js.map": "_javascript", "spec.js": "_javascript_1", "test.js": "_javascript_1", @@ -1431,6 +1478,8 @@ "ad": "_argdown", "mustache": "_mustache", "stache": "_mustache", + "nim": "_nim", + "nims": "_nim", "njk": "_nunjucks", "nunjucks": "_nunjucks", "nunjs": "_nunjucks", @@ -1438,8 +1487,6 @@ "njs": "_nunjucks", "nj": "_nunjucks", "npm-debug.log": "_npm", - "npmignore": "_npm_1", - "npmrc": "_npm_1", "ml": "_ocaml", "mli": "_ocaml", "cmx": "_ocaml", @@ -1449,7 +1496,6 @@ "pddl": "_pddl", "plan": "_plan", "happenings": "_happenings", - "pug": "_pug", "pp": "_puppet", "epp": "_puppet", "spec.jsx": "_react_1", @@ -1459,6 +1505,7 @@ "test.tsx": "_react_2", "re": "_reasonml", "r": "_R", + "rmd": "_R", "erb": "_html_erb", "erb.html": "_html_erb", "html.erb": "_html_erb", @@ -1466,6 +1513,7 @@ "springbeans": "_spring", "slim": "_slim", "smarty.tpl": "_smarty", + "tpl": "_smarty", "sbt": "_sbt", "scala": "_scala", "sol": "_ethereum", @@ -1486,6 +1534,7 @@ "vue": "_vue", "wasm": "_wasm", "wat": "_wat", + "pro": "_prolog", "jar": "_zip", "zip": "_zip_1", "wgt": "_wgt", @@ -1503,6 +1552,8 @@ "pxm": "_image", "svg": "_svg", "svgx": "_image", + "tiff": "_image", + "webp": "_image", "sublime-project": "_sublime", "sublime-workspace": "_sublime", "component": "_salesforce", @@ -1524,6 +1575,8 @@ "obj": "_svg_1", "dae": "_svg_1", "babelrc": "_babel", + "babelrc.js": "_babel", + "babelrc.cjs": "_babel", "bowerrc": "_bower", "dockerignore": "_docker_1", "codeclimate.yml": "_code-climate", @@ -1564,12 +1617,14 @@ "version.md": "_clock", "version": "_clock", "mvnw": "_maven", - "tsconfig.json": "_tsconfig", "swagger.json": "_json_1", "swagger.yml": "_json_1", "swagger.yaml": "_json_1", "mime.types": "_config", "jenkinsfile": "_jenkins", + "babel.config.js": "_babel", + "babel.config.json": "_babel", + "babel.config.cjs": "_babel", "bower.json": "_bower", "docker-healthcheck": "_docker_2", "docker-compose.yml": "_docker_3", @@ -1582,6 +1637,7 @@ "gruntfile.babel.js": "_grunt", "gruntfile.coffee": "_grunt", "gulpfile": "_gulp", + "gulpfile.js": "_gulp", "ionic.config.json": "_ionic", "ionic.project": "_ionic", "platformio.ini": "_platformio", @@ -1617,12 +1673,13 @@ "csharp": "_c-sharp", "css": "_css", "dockerfile": "_docker", + "ignore": "_npm_1", "fsharp": "_f-sharp", "go": "_go2", "groovy": "_grails", "handlebars": "_mustache", "html": "_html_3", - "properties": "_java", + "properties": "_npm_1", "java": "_java", "javascriptreact": "_react", "javascript": "_javascript", @@ -1636,18 +1693,19 @@ "perl": "_perl", "php": "_php", "powershell": "_powershell", - "jade": "_jade", + "jade": "_pug", "python": "_python", "r": "_R", "razor": "_html", "ruby": "_ruby", "rust": "_rust", "scss": "_sass", + "search-result": "_code-search", "shellscript": "_shell", "sql": "_db", "swift": "_swift", "typescript": "_typescript", - "typescriptreact": "_react", + "typescriptreact": "_typescript", "xml": "_xml", "yaml": "_yml", "argdown": "_argdown", @@ -1668,7 +1726,9 @@ "haml": "_haml", "stylus": "_stylus", "vala": "_vala", - "todo": "_todo" + "todo": "_todo", + "postcss": "_css", + "django-html": "_html_3" }, "light": { "file": "_default_light", @@ -1685,9 +1745,11 @@ "hh": "_cpp_1_light", "hpp": "_cpp_1_light", "hxx": "_cpp_1_light", + "h++": "_cpp_1_light", "edn": "_clojure_1_light", "cfc": "_coldfusion_light", "cfm": "_coldfusion_light", + "litcoffee": "_coffee_light", "config": "_config_light", "cfg": "_config_light", "conf": "_config_light", @@ -1700,6 +1762,9 @@ "csv": "_csv_light", "xls": "_xls_light", "xlsx": "_xls_light", + "cu": "_cu_light", + "cuh": "_cu_1_light", + "hu": "_cu_1_light", "cake": "_cake_light", "ctp": "_cake_php_light", "d": "_d_light", @@ -1728,8 +1793,10 @@ "hxs": "_haxe_1_light", "hxp": "_haxe_2_light", "hxml": "_haxe_3_light", + "jade": "_jade_light", "class": "_java_light", "classpath": "_java_light", + "properties": "_java_light", "js.map": "_javascript_light", "spec.js": "_javascript_1_light", "test.js": "_javascript_1_light", @@ -1748,6 +1815,8 @@ "ad": "_argdown_light", "mustache": "_mustache_light", "stache": "_mustache_light", + "nim": "_nim_light", + "nims": "_nim_light", "njk": "_nunjucks_light", "nunjucks": "_nunjucks_light", "nunjs": "_nunjucks_light", @@ -1755,8 +1824,6 @@ "njs": "_nunjucks_light", "nj": "_nunjucks_light", "npm-debug.log": "_npm_light", - "npmignore": "_npm_1_light", - "npmrc": "_npm_1_light", "ml": "_ocaml_light", "mli": "_ocaml_light", "cmx": "_ocaml_light", @@ -1766,7 +1833,6 @@ "pddl": "_pddl_light", "plan": "_plan_light", "happenings": "_happenings_light", - "pug": "_pug_light", "pp": "_puppet_light", "epp": "_puppet_light", "spec.jsx": "_react_1_light", @@ -1776,6 +1842,7 @@ "test.tsx": "_react_2_light", "re": "_reasonml_light", "r": "_R_light", + "rmd": "_R_light", "erb": "_html_erb_light", "erb.html": "_html_erb_light", "html.erb": "_html_erb_light", @@ -1783,6 +1850,7 @@ "springbeans": "_spring_light", "slim": "_slim_light", "smarty.tpl": "_smarty_light", + "tpl": "_smarty_light", "sbt": "_sbt_light", "scala": "_scala_light", "sol": "_ethereum_light", @@ -1803,6 +1871,7 @@ "vue": "_vue_light", "wasm": "_wasm_light", "wat": "_wat_light", + "pro": "_prolog_light", "jar": "_zip_light", "zip": "_zip_1_light", "wgt": "_wgt_light", @@ -1820,6 +1889,8 @@ "pxm": "_image_light", "svg": "_svg_light", "svgx": "_image_light", + "tiff": "_image_light", + "webp": "_image_light", "sublime-project": "_sublime_light", "sublime-workspace": "_sublime_light", "component": "_salesforce_light", @@ -1841,6 +1912,8 @@ "obj": "_svg_1_light", "dae": "_svg_1_light", "babelrc": "_babel_light", + "babelrc.js": "_babel_light", + "babelrc.cjs": "_babel_light", "bowerrc": "_bower_light", "dockerignore": "_docker_1_light", "codeclimate.yml": "_code-climate_light", @@ -1880,12 +1953,13 @@ "csharp": "_c-sharp_light", "css": "_css_light", "dockerfile": "_docker_light", + "ignore": "_npm_1_light", "fsharp": "_f-sharp_light", "go": "_go2_light", "groovy": "_grails_light", "handlebars": "_mustache_light", "html": "_html_3_light", - "properties": "_java_light", + "properties": "_npm_1_light", "java": "_java_light", "javascriptreact": "_react_light", "javascript": "_javascript_light", @@ -1899,18 +1973,19 @@ "perl": "_perl_light", "php": "_php_light", "powershell": "_powershell_light", - "jade": "_jade_light", + "jade": "_pug_light", "python": "_python_light", "r": "_R_light", "razor": "_html_light", "ruby": "_ruby_light", "rust": "_rust_light", "scss": "_sass_light", + "search-result": "_code-search_light", "shellscript": "_shell_light", "sql": "_db_light", "swift": "_swift_light", "typescript": "_typescript_light", - "typescriptreact": "_react_light", + "typescriptreact": "_typescript_light", "xml": "_xml_light", "yaml": "_yml_light", "argdown": "_argdown_light", @@ -1930,7 +2005,9 @@ "elixir": "_elixir_light", "haml": "_haml_light", "stylus": "_stylus_light", - "vala": "_vala_light" + "vala": "_vala_light", + "postcss": "_css_light", + "django-html": "_html_3_light" }, "fileNames": { "mix": "_hex_light", @@ -1943,12 +2020,14 @@ "version.md": "_clock_light", "version": "_clock_light", "mvnw": "_maven_light", - "tsconfig.json": "_tsconfig_light", "swagger.json": "_json_1_light", "swagger.yml": "_json_1_light", "swagger.yaml": "_json_1_light", "mime.types": "_config_light", "jenkinsfile": "_jenkins_light", + "babel.config.js": "_babel_light", + "babel.config.json": "_babel_light", + "babel.config.cjs": "_babel_light", "bower.json": "_bower_light", "docker-healthcheck": "_docker_2_light", "docker-compose.yml": "_docker_3_light", @@ -1961,6 +2040,7 @@ "gruntfile.babel.js": "_grunt_light", "gruntfile.coffee": "_grunt_light", "gulpfile": "_gulp_light", + "gulpfile.js": "_gulp_light", "ionic.config.json": "_ionic_light", "ionic.project": "_ionic_light", "platformio.ini": "_platformio_light", @@ -1986,5 +2066,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/85a222708824c6f19bbecbec71633d2c97077dad" + "version": "https://github.com/jesseweed/seti-ui/commit/7ea773d195eac3f40261897b49a2499815e9346c" } \ No newline at end of file diff --git a/extensions/theme-seti/package.json b/extensions/theme-seti/package.json index a9721611a6f..bcdbb3209e6 100644 --- a/extensions/theme-seti/package.json +++ b/extensions/theme-seti/package.json @@ -15,7 +15,7 @@ "iconThemes": [ { "id": "vs-seti", - "label": "Seti (Visual Studio Code)", + "label": "%themeLabel%", "path": "./icons/vs-seti-icon-theme.json" } ] diff --git a/extensions/theme-seti/package.nls.json b/extensions/theme-seti/package.nls.json index 173d99ddaa8..779302d7319 100644 --- a/extensions/theme-seti/package.nls.json +++ b/extensions/theme-seti/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Seti File Icon Theme", - "description": "A file icon theme made out of the Seti UI file icons" -} \ No newline at end of file + "description": "A file icon theme made out of the Seti UI file icons", + "themeLabel": "Seti (Visual Studio Code)" +} diff --git a/extensions/theme-solarized-dark/package.json b/extensions/theme-solarized-dark/package.json index 427b50fe482..eb7dc5ff943 100644 --- a/extensions/theme-solarized-dark/package.json +++ b/extensions/theme-solarized-dark/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Solarized Dark", + "id": "Solarized Dark", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/solarized-dark-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-solarized-dark/package.nls.json b/extensions/theme-solarized-dark/package.nls.json index c226e15f3ef..46228484d4f 100644 --- a/extensions/theme-solarized-dark/package.nls.json +++ b/extensions/theme-solarized-dark/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Solarized Dark Theme", - "description": "Solarized dark theme for Visual Studio Code" -} \ No newline at end of file + "description": "Solarized dark theme for Visual Studio Code", + "themeLabel": "Solarized Dark" +} diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 682444485d5..8a9deb0cd4b 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -210,7 +210,9 @@ { "name": "Invalid", "scope": "invalid", - "settings": {} + "settings": { + "foreground": "#D30102" + } }, { "name": "diff: header", @@ -270,6 +272,20 @@ "foreground": "#D33682" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -282,6 +298,7 @@ "name": "Markup Headings", "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#268BD2" } }, @@ -419,11 +436,11 @@ "tab.inactiveForeground": "#93A1A1", "tab.inactiveBackground": "#004052", "tab.border": "#003847", + "tab.lastPinnedBorder": "#2AA19844", // Workbench: Activity Bar "activityBar.background": "#003847", // "activityBarBadge.background": "", - // "activityBar.dropBackground": "", // "activityBar.foreground": "", // "activityBarBadge.foreground": "", @@ -477,5 +494,6 @@ "terminal.ansiBrightMagenta": "#6c71c4", "terminal.ansiBrightCyan": "#93a1a1", "terminal.ansiBrightWhite": "#fdf6e3" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-solarized-light/package.json b/extensions/theme-solarized-light/package.json index 3afb2b7ed9d..421aa7a825a 100644 --- a/extensions/theme-solarized-light/package.json +++ b/extensions/theme-solarized-light/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Solarized Light", + "id": "Solarized Light", + "label": "%themeLabel%", "uiTheme": "vs", "path": "./themes/solarized-light-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-solarized-light/package.nls.json b/extensions/theme-solarized-light/package.nls.json index f7a3d0cee33..a2e9c31f30b 100644 --- a/extensions/theme-solarized-light/package.nls.json +++ b/extensions/theme-solarized-light/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Solarized Light Theme", - "description": "Solarized light theme for Visual Studio Code" -} \ No newline at end of file + "description": "Solarized light theme for Visual Studio Code", + "themeLabel": "Solarized Light" +} diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index a29c8fb32f0..427b7c3c359 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -182,7 +182,10 @@ }, { "name": "Library constant", - "scope": "support.constant", + "scope": [ + "support.constant", + "support.variable" + ], "settings": {} }, { @@ -210,7 +213,9 @@ { "name": "Invalid", "scope": "invalid", - "settings": {} + "settings": { + "foreground": "#cd3131" + } }, { "name": "diff: header", @@ -270,6 +275,20 @@ "foreground": "#D33682" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -282,6 +301,7 @@ "name": "Markup Headings", "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#268BD2" } }, @@ -418,11 +438,11 @@ // "tab.activeBackground": "", // "tab.activeForeground": "", // "tab.inactiveForeground": "", + "tab.lastPinnedBorder": "#FDF6E3", // Workbench: Activity Bar "activityBar.background": "#DDD6C1", "activityBar.foreground": "#584c27", - "activityBar.dropBackground": "#EEE8D5", "activityBarBadge.background": "#B58900", // "activityBarBadge.foreground": "", @@ -484,5 +504,6 @@ // Interactive Playground "walkThrough.embeddedEditorBackground": "#00000014" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-tomorrow-night-blue/package.json b/extensions/theme-tomorrow-night-blue/package.json index 1266ac58ca4..e03f05d8a5a 100644 --- a/extensions/theme-tomorrow-night-blue/package.json +++ b/extensions/theme-tomorrow-night-blue/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Tomorrow Night Blue", + "id": "Tomorrow Night Blue", + "label": "%themeLabel%", "uiTheme": "vs-dark", - "path": "./themes/tomorrow-night-blue-theme.json" + "path": "./themes/tomorrow-night-blue-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-tomorrow-night-blue/package.nls.json b/extensions/theme-tomorrow-night-blue/package.nls.json index 044df352eaf..a37a31b282d 100644 --- a/extensions/theme-tomorrow-night-blue/package.nls.json +++ b/extensions/theme-tomorrow-night-blue/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Tomorrow Night Blue Theme", - "description": "Tomorrow night blue theme for Visual Studio Code" -} \ No newline at end of file + "description": "Tomorrow night blue theme for Visual Studio Code", + "themeLabel": "Quiet Light" +} diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json similarity index 91% rename from extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json rename to extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index f8c47a29e7b..91c135342f1 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -27,7 +27,7 @@ "editorGroup.dropBackground": "#25375daa", "peekViewResult.background": "#001c40", "tab.inactiveBackground": "#001c40", - "tab.modifiedBorder": "#FFEEAD", + "tab.lastPinnedBorder": "#007acc80", "debugToolBar.background": "#001c40", "titleBar.activeBackground": "#001126", "statusBar.background": "#001126", @@ -66,7 +66,7 @@ { "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { - "background": "#002451", + //"background": "#002451", "foreground": "#FFFFFF" } }, @@ -143,16 +143,16 @@ "name": "Invalid", "scope": "invalid", "settings": { - "background": "#F99DA5", + //"background": "#F99DA5", "fontStyle": "", - "foreground": "#FFFFFF" + "foreground": "#a92049" } }, { "name": "Separator", "scope": "meta.separator", "settings": { - "background": "#BBDAFE", + //"background": "#BBDAFE", "foreground": "#FFFFFF" } }, @@ -160,9 +160,9 @@ "name": "Deprecated", "scope": "invalid.deprecated", "settings": { - "background": "#EBBBFF", + //"background": "#EBBBFF", "fontStyle": "", - "foreground": "#FFFFFF" + "foreground": "#cd9731" } }, { @@ -190,8 +190,7 @@ "name": "Diff header", "scope": "meta.diff.header.from-file, meta.diff.header.to-file", "settings": { - "foreground": "#FFFFFF", - "background": "#4271ae" + "foreground": "#4271ae" } }, { @@ -223,6 +222,20 @@ "foreground": "#FFC58F" } }, + { + "name": "Markup: Strong", + "scope": "markup.bold", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markup: Emphasis", + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, { "name": "Markup Inline", "scope": "markup.inline.raw", @@ -231,6 +244,13 @@ "foreground": "#FF9DA4" } }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "fontStyle": "bold" + } + }, { "scope": "token.info-token", "settings": { @@ -255,5 +275,6 @@ "foreground": "#b267e6" } } - ] + ], + "semanticHighlighting": true } diff --git a/extensions/types/lib.textEncoder.d.ts b/extensions/types/lib.textEncoder.d.ts new file mode 100644 index 00000000000..99a5b2271d6 --- /dev/null +++ b/extensions/types/lib.textEncoder.d.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Define TextEncoder + TextDecoder globals for both browser and node runtimes +// +// Proper fix: https://github.com/microsoft/TypeScript/issues/31535 + +declare var TextDecoder: typeof import('util').TextDecoder; +declare var TextEncoder: typeof import('util').TextEncoder; diff --git a/extensions/types/lib.url.d.ts b/extensions/types/lib.url.d.ts new file mode 100644 index 00000000000..5dbe0fdc505 --- /dev/null +++ b/extensions/types/lib.url.d.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Define Url global for both browser and node runtimes +// +// Copied from https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34960 + +declare const URL: typeof import('url').URL; diff --git a/extensions/typescript-basics/.vscodeignore b/extensions/typescript-basics/.vscodeignore index 06c7b11c5e6..0a0a50bc3e0 100644 --- a/extensions/typescript-basics/.vscodeignore +++ b/extensions/typescript-basics/.vscodeignore @@ -3,3 +3,4 @@ src/** test/** tsconfig.json cgmanifest.json +syntaxes/Readme.md diff --git a/extensions/typescript-basics/build/update-grammars.js b/extensions/typescript-basics/build/update-grammars.js index 1839ad63674..b58a2f57d2e 100644 --- a/extensions/typescript-basics/build/update-grammars.js +++ b/extensions/typescript-basics/build/update-grammars.js @@ -17,6 +17,26 @@ function removeDom(grammar) { return grammar; } +function removeNodeTypes(grammar) { + grammar.repository['support-objects'].patterns = grammar.repository['support-objects'].patterns.filter(pattern => { + if (pattern.name) { + if (pattern.name.startsWith('support.variable.object.node') || pattern.name.startsWith('support.class.node.')) { + return false; + } + } + if (pattern.captures) { + if (Object.values(pattern.captures).some(capture => + capture.name && (capture.name.startsWith('support.variable.object.process') + || capture.name.startsWith('support.class.console')) + )) { + return false; + } + } + return true; + }); + return grammar; +} + function patchJsdoctype(grammar) { grammar.repository['jsdoctype'].patterns = grammar.repository['jsdoctype'].patterns.filter(pattern => { if (pattern.name && pattern.name.indexOf('illegal') >= -1) { @@ -28,7 +48,7 @@ function patchJsdoctype(grammar) { } function patchGrammar(grammar) { - return removeDom(patchJsdoctype(grammar)); + return removeNodeTypes(removeDom(patchJsdoctype(grammar))); } function adaptToJavaScript(grammar, replacementScope) { @@ -57,7 +77,7 @@ function adaptToJavaScript(grammar, replacementScope) { } } -var tsGrammarRepo = 'Microsoft/TypeScript-TmLanguage'; +var tsGrammarRepo = 'microsoft/TypeScript-TmLanguage'; updateGrammar.update(tsGrammarRepo, 'TypeScript.tmLanguage', './syntaxes/TypeScript.tmLanguage.json', grammar => patchGrammar(grammar)); updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', './syntaxes/TypeScriptReact.tmLanguage.json', grammar => patchGrammar(grammar)); updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', '../javascript/syntaxes/JavaScript.tmLanguage.json', grammar => adaptToJavaScript(patchGrammar(grammar), '.js')); diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index 4136543a84f..3a42696ad71 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -5,12 +5,12 @@ "type": "git", "git": { "name": "TypeScript-TmLanguage", - "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", - "commitHash": "f065e7e88d1c20160c5ec92455aad99a1016284f" + "repositoryUrl": "https://github.com/microsoft/TypeScript-TmLanguage", + "commitHash": "fa4e0d3a918db0eab8e5c5be952f3bd649968456" } }, "license": "MIT", - "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage.", + "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/microsoft/TypeScript-TmLanguage.", "version": "1.0.0" } ], diff --git a/extensions/typescript-basics/package.json b/extensions/typescript-basics/package.json index 307958534b9..8ef6a585e59 100644 --- a/extensions/typescript-basics/package.json +++ b/extensions/typescript-basics/package.json @@ -45,7 +45,9 @@ ], "filenamePatterns": [ "tsconfig.*.json", - "tsconfig-*.json" + "jsconfig.*.json", + "tsconfig-*.json", + "jsconfig-*.json" ] } ], @@ -77,16 +79,92 @@ "meta.import string.quoted": "other", "variable.other.jsdoc": "other" } + }, + { + "scopeName": "documentation.injection.ts", + "path": "./syntaxes/jsdoc.ts.injection.tmLanguage.json", + "injectTo": [ + "source.ts", + "source.tsx" + ] + }, + { + "scopeName": "documentation.injection.js.jsx", + "path": "./syntaxes/jsdoc.js.injection.tmLanguage.json", + "injectTo": [ + "source.js", + "source.js.jsx" + ] + } + ], + "semanticTokenScopes": [ + { + "language": "typescript", + "scopes": { + "property": [ + "variable.other.property.ts" + ], + "property.readonly": [ + "variable.other.constant.property.ts" + ], + "variable": [ + "variable.other.readwrite.ts" + ], + "variable.readonly": [ + "variable.other.constant.object.ts" + ], + "function": [ + "entity.name.function.ts" + ], + "namespace": [ + "entity.name.type.module.ts" + ], + "variable.defaultLibrary": [ + "support.variable.ts" + ], + "function.defaultLibrary": [ + "support.function.ts" + ] + } + }, + { + "language": "typescriptreact", + "scopes": { + "property": [ + "variable.other.property.tsx" + ], + "property.readonly": [ + "variable.other.constant.property.tsx" + ], + "variable": [ + "variable.other.readwrite.tsx" + ], + "variable.readonly": [ + "variable.other.constant.object.tsx" + ], + "function": [ + "entity.name.function.tsx" + ], + "namespace": [ + "entity.name.type.module.tsx" + ], + "variable.defaultLibrary": [ + "support.variable.tsx" + ], + "function.defaultLibrary": [ + "support.function.tsx" + ] + } } ], "snippets": [ { "language": "typescript", - "path": "./snippets/typescript.json" + "path": "./snippets/typescript.code-snippets" }, { "language": "typescriptreact", - "path": "./snippets/typescript.json" + "path": "./snippets/typescript.code-snippets" } ] } diff --git a/extensions/typescript-basics/snippets/typescript.json b/extensions/typescript-basics/snippets/typescript.code-snippets similarity index 85% rename from extensions/typescript-basics/snippets/typescript.json rename to extensions/typescript-basics/snippets/typescript.code-snippets index 0a7195751d8..8eeb13e2e2d 100644 --- a/extensions/typescript-basics/snippets/typescript.json +++ b/extensions/typescript-basics/snippets/typescript.code-snippets @@ -123,7 +123,7 @@ "Throw Exception": { "prefix": "throw", "body": [ - "throw \"$1\";", + "throw new Error(\"$1\");", "$0" ], "description": "Throw Exception" @@ -151,7 +151,7 @@ "prefix": "forin", "body": [ "for (const ${1:key} in ${2:object}) {", - "\tif (${2:object}.hasOwnProperty(${1:key})) {", + "\tif (Object.prototype.hasOwnProperty.call(${2:object}, ${1:key})) {", "\t\tconst ${3:element} = ${2:object}[${1:key}];", "\t\t$0", "\t}", @@ -168,6 +168,15 @@ ], "description": "For-Of Loop" }, + "For-Await-Of Loop": { + "prefix": "forawaitof", + "body": [ + "for await (const ${1:iterator} of ${2:object}) {", + "\t$0", + "}" + ], + "description": "For-Await-Of Loop" + }, "Function Statement": { "prefix": "function", "body": [ @@ -269,5 +278,32 @@ "//#endregion" ], "description": "Folding Region End" + }, + "new Promise": { + "prefix": "newpromise", + "body": [ + "new Promise<$1:type>((resolve, reject) => {", + "\t$1", + "})" + ], + "description": "Create a new Promise" + }, + "Async Function Statement": { + "prefix": "async function", + "body": [ + "async function ${1:name}(${2:params}:${3:type}) {", + "\t$0", + "}" + ], + "description": "Async Function Statement" + }, + "Async Function Expression": { + "prefix": "async arrow function", + "body": [ + "async (${1:params}:${2:type}) => {", + "\t$0", + "}" + ], + "description": "Async Function Expression" } } diff --git a/extensions/typescript-basics/syntaxes/Readme.md b/extensions/typescript-basics/syntaxes/Readme.md index 3e1cf32a0e2..2f9c2b95ee2 100644 --- a/extensions/typescript-basics/syntaxes/Readme.md +++ b/extensions/typescript-basics/syntaxes/Readme.md @@ -1,4 +1,4 @@ -The file `TypeScript.tmLanguage.json` and `TypeScriptReact.tmLanguage.json` are derived from [TypeScript.tmLanguage](https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScript.tmLanguage) and [TypeScriptReact.tmLanguage](https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage). +The file `TypeScript.tmLanguage.json` and `TypeScriptReact.tmLanguage.json` are derived from [TypeScript.tmLanguage](https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScript.tmLanguage) and [TypeScriptReact.tmLanguage](https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage). To update to the latest version: - `cd extensions/typescript` and run `npm run update-grammars` diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 4edc3b8285e..f22ebbf2041 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScript.tmLanguage", + "This file has been converted from https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScript.tmLanguage", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -15,6 +15,11 @@ "include": "#statements" }, { + "include": "#shebang" + } + ], + "repository": { + "shebang": { "name": "comment.line.shebang.ts", "match": "\\A(#!).*(?=$)", "captures": { @@ -22,9 +27,7 @@ "name": "punctuation.definition.comment.ts" } } - } - ], - "repository": { + }, "statements": { "patterns": [ { @@ -423,7 +426,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.ts", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -481,7 +484,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.ts", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts variable.other.constant.ts entity.name.function.ts" @@ -865,7 +868,7 @@ } }, { - "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -1105,7 +1108,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.ts entity.name.function.ts" @@ -1428,7 +1431,7 @@ }, { "name": "meta.arrow.ts", - "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2622,7 +2625,7 @@ }, { "name": "meta.object.member.ts", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2685,7 +2688,7 @@ "name": "keyword.control.as.ts" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", + "end": "(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", "patterns": [ { "include": "#type" @@ -2833,26 +2836,75 @@ ] }, "function-call": { - "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.ts", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\())", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\())", "patterns": [ { - "include": "#support-function-call-identifiers" + "name": "meta.function-call.ts", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\())", + "patterns": [ + { + "include": "#function-call-target" + } + ] }, { - "name": "entity.name.function.ts", - "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + }, + { + "include": "#paren-expression" } ] }, { - "include": "#comment" + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "end": "(?<=\\>)(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "name": "meta.function-call.ts", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "include": "#function-call-target" + } + ] + }, + { + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + } + ] + } + ] + }, + "function-call-target": { + "patterns": [ + { + "include": "#support-function-call-identifiers" }, + { + "name": "entity.name.function.ts", + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + "function-call-optionals": { + "patterns": [ { "name": "meta.function-call.ts punctuation.accessor.optional.ts", "match": "\\?\\." @@ -2860,12 +2912,6 @@ { "name": "meta.function-call.ts keyword.operator.definiteassignment.ts", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2897,7 +2943,7 @@ "name": "keyword.operator.new.ts" } }, - "end": "(?<=\\))|(?=[;),}\\]:\\-\\+]|\\|\\||\\&\\&|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))new(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", + "end": "(?<=\\))|(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))new(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", "patterns": [ { "include": "#paren-expression" @@ -2917,7 +2963,7 @@ "name": "keyword.operator.expression.instanceof.ts" } }, - "end": "(?<=\\))|(?=[;),}\\]:?]|\\|\\||\\&\\&|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", + "end": "(?<=\\))|(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|(([\\&\\~\\^\\|]\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s+instanceof(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", "patterns": [ { "include": "#type" @@ -3011,7 +3057,7 @@ } }, { - "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -3227,7 +3273,7 @@ "name": "keyword.control.as.ts" } }, - "end": "(?=$|^|[;,:})\\]]|\\|\\||\\&\\&|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+)|(\\s+\\<))", + "end": "(?=^|[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+)|(\\s+\\<))", "patterns": [ { "include": "#type" @@ -3259,7 +3305,7 @@ "match": "<=|>=|<>|<|>" }, { - "match": "(\\!)\\s*(/)(?![/*])", + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", "captures": { "1": { "name": "keyword.operator.logical.ts" @@ -3535,23 +3581,6 @@ } } }, - { - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(console)(?:\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\n assert|clear|count|debug|dir|error|group|groupCollapsed|groupEnd|info|log\n |profile|profileEnd|table|time|timeEnd|timeStamp|trace|warn))?\\b(?!\\$)", - "captures": { - "1": { - "name": "support.class.console.ts" - }, - "2": { - "name": "punctuation.accessor.ts" - }, - "3": { - "name": "punctuation.accessor.optional.ts" - }, - "4": { - "name": "support.function.console.ts" - } - } - }, { "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(JSON)(?:\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(parse|stringify))?\\b(?!\\$)", "captures": { @@ -3620,30 +3649,6 @@ } } }, - { - "name": "support.class.node.ts", - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Buffer|EventEmitter|Server|Pipe|Socket|REPLServer|ReadStream|WriteStream|Stream\n |Inflate|Deflate|InflateRaw|DeflateRaw|GZip|GUnzip|Unzip|Zip)\\b(?!\\$)" - }, - { - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(process)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))(?:\n (arch|argv|config|connected|env|execArgv|execPath|exitCode|mainModule|pid|platform|release|stderr|stdin|stdout|title|version|versions)\n |\n (abort|chdir|cwd|disconnect|exit|[sg]ete?[gu]id|send|[sg]etgroups|initgroups|kill|memoryUsage|nextTick|umask|uptime|hrtime)\n))?\\b(?!\\$)", - "captures": { - "1": { - "name": "support.variable.object.process.ts" - }, - "2": { - "name": "punctuation.accessor.ts" - }, - "3": { - "name": "punctuation.accessor.optional.ts" - }, - "4": { - "name": "support.variable.property.process.ts" - }, - "5": { - "name": "support.function.process.ts" - } - } - }, { "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(exports)|(module)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))(exports|id|filename|loaded|parent|children))?)\\b(?!\\$)", "captures": { @@ -3663,10 +3668,6 @@ "name": "support.type.object.module.ts" } } - }, - { - "name": "support.variable.object.node.ts", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(global|GLOBAL|root|__dirname|__filename)\\b(?!\\$)" } ] }, @@ -3676,7 +3677,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -4070,6 +4071,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.ts", + "match": "\\.\\.\\." + }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?)?\\s*(:)", + "captures": { + "1": { + "name": "entity.name.label.ts" + }, + "2": { + "name": "keyword.operator.optional.ts" + }, + "3": { + "name": "punctuation.separator.label.ts" + } + } + }, { "include": "#type" }, @@ -4382,6 +4401,10 @@ "name": "keyword.operator.expression.infer.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))infer(?=\\s+[_$[:alpha:]])" }, + { + "name": "keyword.operator.expression.awaited.ts", + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))awaited(?=\\s+[_$[:alpha:]])" + }, { "name": "keyword.operator.expression.import.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))import(?=\\s*\\()" @@ -4591,12 +4614,12 @@ "patterns": [ { "name": "string.template.ts", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", "end": "(?=`)", "patterns": [ { "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "end": "(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", "patterns": [ { "include": "#support-function-call-identifiers" @@ -4614,7 +4637,7 @@ }, { "name": "string.template.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.ts" diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index 810cac1b2db..3a6fd84e934 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/Microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage", + "This file has been converted from https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -15,16 +15,19 @@ "include": "#statements" }, { - "name": "comment.line.shebang.ts", - "match": "\\A(#!).*(?=$)", - "captures": { - "1": { - "name": "punctuation.definition.comment.ts" - } - } + "include": "#shebang" } ], "repository": { + "shebang": { + "name": "comment.line.shebang.tsx", + "match": "\\A(#!).*(?=$)", + "captures": { + "1": { + "name": "punctuation.definition.comment.tsx" + } + } + }, "statements": { "patterns": [ { @@ -426,7 +429,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.tsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -484,7 +487,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.tsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx variable.other.constant.tsx entity.name.function.tsx" @@ -868,7 +871,7 @@ } }, { - "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -1108,7 +1111,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.tsx entity.name.function.tsx" @@ -1431,7 +1434,7 @@ }, { "name": "meta.arrow.tsx", - "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2625,7 +2628,7 @@ }, { "name": "meta.object.member.tsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2688,7 +2691,7 @@ "name": "keyword.control.as.tsx" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", + "end": "(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", "patterns": [ { "include": "#type" @@ -2836,26 +2839,75 @@ ] }, "function-call": { - "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.tsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\())", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\())", "patterns": [ { - "include": "#support-function-call-identifiers" + "name": "meta.function-call.tsx", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?((<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\())", + "patterns": [ + { + "include": "#function-call-target" + } + ] }, { - "name": "entity.name.function.tsx", - "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + }, + { + "include": "#paren-expression" } ] }, { - "include": "#comment" + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "end": "(?<=\\>)(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "name": "meta.function-call.tsx", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*[\\{\\[\\(]\\s*$))", + "patterns": [ + { + "include": "#function-call-target" + } + ] + }, + { + "include": "#comment" + }, + { + "include": "#function-call-optionals" + }, + { + "include": "#type-arguments" + } + ] + } + ] + }, + "function-call-target": { + "patterns": [ + { + "include": "#support-function-call-identifiers" }, + { + "name": "entity.name.function.tsx", + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + "function-call-optionals": { + "patterns": [ { "name": "meta.function-call.tsx punctuation.accessor.optional.tsx", "match": "\\?\\." @@ -2863,12 +2915,6 @@ { "name": "meta.function-call.tsx keyword.operator.definiteassignment.tsx", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2900,7 +2946,7 @@ "name": "keyword.operator.new.tsx" } }, - "end": "(?<=\\))|(?=[;),}\\]:\\-\\+]|\\|\\||\\&\\&|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))new(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", + "end": "(?<=\\))|(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))new(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", "patterns": [ { "include": "#paren-expression" @@ -2920,7 +2966,7 @@ "name": "keyword.operator.expression.instanceof.tsx" } }, - "end": "(?<=\\))|(?=[;),}\\]:?]|\\|\\||\\&\\&|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", + "end": "(?<=\\))|(?=[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|(([\\&\\~\\^\\|]\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s+instanceof(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))function((\\s+[_$[:alpha:]][_$[:alnum:]]*)|(\\s*[\\(]))))", "patterns": [ { "include": "#type" @@ -3014,7 +3060,7 @@ } }, { - "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))Function(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))) |\n(:\\s*((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -3178,7 +3224,7 @@ "name": "keyword.control.as.tsx" } }, - "end": "(?=$|^|[;,:})\\]]|\\|\\||\\&\\&|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+)|(\\s+\\<))", + "end": "(?=^|[;),}\\]:?\\-\\+\\>]|\\|\\||\\&\\&|\\!\\=\\=|$|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+)|(\\s+\\<))", "patterns": [ { "include": "#type" @@ -3210,7 +3256,7 @@ "match": "<=|>=|<>|<|>" }, { - "match": "(\\!)\\s*(/)(?![/*])", + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", "captures": { "1": { "name": "keyword.operator.logical.tsx" @@ -3486,23 +3532,6 @@ } } }, - { - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(console)(?:\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\n assert|clear|count|debug|dir|error|group|groupCollapsed|groupEnd|info|log\n |profile|profileEnd|table|time|timeEnd|timeStamp|trace|warn))?\\b(?!\\$)", - "captures": { - "1": { - "name": "support.class.console.tsx" - }, - "2": { - "name": "punctuation.accessor.tsx" - }, - "3": { - "name": "punctuation.accessor.optional.tsx" - }, - "4": { - "name": "support.function.console.tsx" - } - } - }, { "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(JSON)(?:\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(parse|stringify))?\\b(?!\\$)", "captures": { @@ -3571,30 +3600,6 @@ } } }, - { - "name": "support.class.node.tsx", - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Buffer|EventEmitter|Server|Pipe|Socket|REPLServer|ReadStream|WriteStream|Stream\n |Inflate|Deflate|InflateRaw|DeflateRaw|GZip|GUnzip|Unzip|Zip)\\b(?!\\$)" - }, - { - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(process)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))(?:\n (arch|argv|config|connected|env|execArgv|execPath|exitCode|mainModule|pid|platform|release|stderr|stdin|stdout|title|version|versions)\n |\n (abort|chdir|cwd|disconnect|exit|[sg]ete?[gu]id|send|[sg]etgroups|initgroups|kill|memoryUsage|nextTick|umask|uptime|hrtime)\n))?\\b(?!\\$)", - "captures": { - "1": { - "name": "support.variable.object.process.tsx" - }, - "2": { - "name": "punctuation.accessor.tsx" - }, - "3": { - "name": "punctuation.accessor.optional.tsx" - }, - "4": { - "name": "support.variable.property.process.tsx" - }, - "5": { - "name": "support.function.process.tsx" - } - } - }, { "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(exports)|(module)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))(exports|id|filename|loaded|parent|children))?)\\b(?!\\$)", "captures": { @@ -3614,10 +3619,6 @@ "name": "support.type.object.module.tsx" } } - }, - { - "name": "support.variable.object.node.tsx", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(global|GLOBAL|root|__dirname|__filename)\\b(?!\\$)" } ] }, @@ -3627,7 +3628,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -4021,6 +4022,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.tsx", + "match": "\\.\\.\\." + }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?)?\\s*(:)", + "captures": { + "1": { + "name": "entity.name.label.tsx" + }, + "2": { + "name": "keyword.operator.optional.tsx" + }, + "3": { + "name": "punctuation.separator.label.tsx" + } + } + }, { "include": "#type" }, @@ -4333,6 +4352,10 @@ "name": "keyword.operator.expression.infer.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))infer(?=\\s+[_$[:alpha:]])" }, + { + "name": "keyword.operator.expression.awaited.tsx", + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))awaited(?=\\s+[_$[:alpha:]])" + }, { "name": "keyword.operator.expression.import.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))import(?=\\s*\\()" @@ -4542,12 +4565,12 @@ "patterns": [ { "name": "string.template.tsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", "end": "(?=`)", "patterns": [ { "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "end": "(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", "patterns": [ { "include": "#support-function-call-identifiers" @@ -4565,7 +4588,7 @@ }, { "name": "string.template.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|awaited|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.tsx" diff --git a/extensions/typescript-basics/syntaxes/jsdoc.js.injection.tmLanguage.json b/extensions/typescript-basics/syntaxes/jsdoc.js.injection.tmLanguage.json new file mode 100644 index 00000000000..cdc3b47f828 --- /dev/null +++ b/extensions/typescript-basics/syntaxes/jsdoc.js.injection.tmLanguage.json @@ -0,0 +1,21 @@ + +{ + "injectionSelector": "L:comment.block.documentation", + "patterns": [ + { + "include": "#jsdocbody" + } + ], + "repository": { + "jsdocbody": { + "begin": "(?<=/\\*\\*)([^*]|\\*(?!/))*$", + "while": "(^|\\G)\\s*\\*(?!/)(?=([^*]|[*](?!/))*$)", + "patterns": [ + { + "include": "source.ts#docblock" + } + ] + } + }, + "scopeName": "documentation.injection.js.jsx" +} diff --git a/extensions/typescript-basics/syntaxes/jsdoc.ts.injection.tmLanguage.json b/extensions/typescript-basics/syntaxes/jsdoc.ts.injection.tmLanguage.json new file mode 100644 index 00000000000..4426e406fde --- /dev/null +++ b/extensions/typescript-basics/syntaxes/jsdoc.ts.injection.tmLanguage.json @@ -0,0 +1,21 @@ + +{ + "injectionSelector": "L:comment.block.documentation", + "patterns": [ + { + "include": "#jsdocbody" + } + ], + "repository": { + "jsdocbody": { + "begin": "(?<=/\\*\\*)([^*]|\\*(?!/))*$", + "while": "(^|\\G)\\s*\\*(?!/)(?=([^*]|[*](?!/))*$)", + "patterns": [ + { + "include": "source.ts#docblock" + } + ] + } + }, + "scopeName": "documentation.injection.ts" +} diff --git a/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json b/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json index d1146aa1288..4dfc0bbeffe 100644 --- a/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json @@ -2962,8 +2962,8 @@ "c": "t", "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -3398,4 +3398,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json b/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json index 6a27304f24a..b079500f022 100644 --- a/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json @@ -190,8 +190,8 @@ "c": "timeRange", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" diff --git a/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json b/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json index 10a8b4c625e..cc1eba5f197 100644 --- a/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json @@ -77,7 +77,18 @@ } }, { - "c": " * id: number,", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " id: number,", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -88,7 +99,18 @@ } }, { - "c": " * fn: !Function,", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " fn: !Function,", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -99,7 +121,18 @@ } }, { - "c": " * context: (!Object|undefined)", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " context: (!Object|undefined)", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -110,7 +143,18 @@ } }, { - "c": " * }", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " }", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -352,7 +396,18 @@ } }, { - "c": " * measureTask: goog.dom.animationFrame.Task_,", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " measureTask: goog.dom.animationFrame.Task_,", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -363,7 +418,18 @@ } }, { - "c": " * mutateTask: goog.dom.animationFrame.Task_,", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " mutateTask: goog.dom.animationFrame.Task_,", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -374,7 +440,18 @@ } }, { - "c": " * state: (!Object|undefined),", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " state: (!Object|undefined),", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -385,7 +462,18 @@ } }, { - "c": " * args: (!Array|undefined),", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " args: (!Array|undefined),", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -396,7 +484,18 @@ } }, { - "c": " * isScheduled: boolean", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " isScheduled: boolean", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -407,7 +506,18 @@ } }, { - "c": " * }", + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " }", "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", "r": { "dark_plus": "entity.name.type: #4EC9B0", diff --git a/extensions/typescript-basics/test/colorize-results/test-strings_ts.json b/extensions/typescript-basics/test/colorize-results/test-strings_ts.json index 5c1d4bb6c17..f9d401fabce 100644 --- a/extensions/typescript-basics/test/colorize-results/test-strings_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-strings_ts.json @@ -155,13 +155,13 @@ }, { "c": "console", - "t": "source.ts meta.function-call.ts support.class.console.ts", + "t": "source.ts meta.function-call.ts variable.other.object.ts", "r": { - "dark_plus": "support.class: #4EC9B0", - "light_plus": "support.class: #267F99", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.class: #4EC9B0" + "hc_black": "variable: #9CDCFE" } }, { @@ -177,13 +177,13 @@ }, { "c": "log", - "t": "source.ts meta.function-call.ts support.function.console.ts", + "t": "source.ts meta.function-call.ts entity.name.function.ts", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { diff --git a/extensions/typescript-language-features/.vscodeignore b/extensions/typescript-language-features/.vscodeignore index 1edbc2a7b5e..079f06f08d9 100644 --- a/extensions/typescript-language-features/.vscodeignore +++ b/extensions/typescript-language-features/.vscodeignore @@ -1,8 +1,10 @@ build/** src/** test/** +test-workspace/** out/** tsconfig.json extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json yarn.lock diff --git a/extensions/typescript-language-features/cgmanifest.json b/extensions/typescript-language-features/cgmanifest.json index 11b737dc59a..12f8e5ecacc 100644 --- a/extensions/typescript-language-features/cgmanifest.json +++ b/extensions/typescript-language-features/cgmanifest.json @@ -5,7 +5,7 @@ "type": "git", "git": { "name": "TypeScript-TmLanguage", - "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", + "repositoryUrl": "https://github.com/microsoft/TypeScript-TmLanguage", "commitHash": "3133e3d914db9a2bb8812119f9273727a305f16b" } }, @@ -28,7 +28,7 @@ "type": "other", "other": { "name": "Unicode", - "downloadUrl": "http://www.unicode.org/", + "downloadUrl": "https://home.unicode.org/", "version": "12.0.0" } }, diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..fd8e5877c3a --- /dev/null +++ b/extensions/typescript-language-features/extension-browser.webpack.config.js @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; +const CopyPlugin = require('copy-webpack-plugin'); +const { lchmod } = require('graceful-fs'); +const Terser = require('terser'); + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extension.browser.ts', + }, + plugins: [ + // @ts-ignore + new CopyPlugin({ + patterns: [ + { + from: 'node_modules/typescript-web-server/*.d.ts', + to: 'typescript-web/', + flatten: true + }, + ], + }), + // @ts-ignore + new CopyPlugin({ + patterns: [ + { + from: 'node_modules/typescript-web-server/tsserver.js', + to: 'typescript-web/tsserver.web.js', + transform: (content) => { + return Terser.minify(content.toString()).code; + + }, + transformPath: (targetPath) => { + return targetPath.replace('tsserver.js', 'tsserver.web.js'); + } + } + ], + }), + ], +}); diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index d4a7d5eb4e8..dfd3f6295aa 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -16,21 +16,26 @@ "Programming Languages" ], "dependencies": { - "jsonc-parser": "^2.1.1", + "jsonc-parser": "^2.2.1", "rimraf": "^2.6.3", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.6.3", + "typescript-vscode-sh-plugin": "^0.6.14", "vscode-extension-telemetry": "0.1.1", - "vscode-nls": "^4.0.0" + "vscode-nls": "^4.1.1" }, "devDependencies": { "@types/node": "^12.11.7", "@types/rimraf": "2.0.2", "@types/semver": "^5.5.0", + "copy-webpack-plugin": "^6.0.3", + "terser": "^4.8.0", + "typescript-web-server": "git://github.com/mjbvz/ts-server-web-build", "vscode": "^1.1.36" }, "scripts": { - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:typescript ./tsconfig.json" + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:typescript-language-features", + "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", + "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "activationEvents": [ "onLanguage:javascript", @@ -50,16 +55,8 @@ "onLanguage:jsonc" ], "main": "./out/extension", + "browser": "./dist/browser/extension", "contributes": { - "documentation": { - "refactoring": [ - { - "title": "%documentation.refactoring.title%", - "when": "typescript.isManagedFile", - "command": "_typescript.learnMoreAboutRefactorings" - } - ] - }, "jsonValidation": [ { "fileMatch": "package.json", @@ -67,7 +64,7 @@ }, { "fileMatch": "tsconfig.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig.json", @@ -75,7 +72,7 @@ }, { "fileMatch": "tsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig-*.json", @@ -83,7 +80,7 @@ }, { "fileMatch": "tsconfig-*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig.*.json", @@ -91,27 +88,27 @@ }, { "fileMatch": "typings.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/typings.json" + "url": "https://json.schemastore.org/typings" }, { "fileMatch": ".bowerrc", - "url": "https://schemastore.azurewebsites.net/schemas/json/bowerrc.json" + "url": "https://json.schemastore.org/bowerrc" }, { "fileMatch": ".babelrc", - "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" + "url": "https://json.schemastore.org/babelrc" }, { "fileMatch": ".babelrc.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" + "url": "https://json.schemastore.org/babelrc" }, { "fileMatch": "babel.config.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/babelrc.json" + "url": "https://json.schemastore.org/babelrc" }, { "fileMatch": "jsconfig.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" + "url": "https://json.schemastore.org/jsconfig" }, { "fileMatch": "jsconfig.json", @@ -119,7 +116,7 @@ }, { "fileMatch": "jsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" + "url": "https://json.schemastore.org/jsconfig" }, { "fileMatch": "jsconfig.*.json", @@ -149,6 +146,12 @@ "usesOnlineServices" ] }, + "typescript.enablePromptUseWorkspaceTsdk": { + "type": "boolean", + "default": false, + "description": "%typescript.enablePromptUseWorkspaceTsdk%", + "scope": "window" + }, "typescript.npm": { "type": [ "string", @@ -329,6 +332,12 @@ "description": "%format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces%", "scope": "resource" }, + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": { + "type": "boolean", + "default": true, + "description": "%format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces%", + "scope": "resource" + }, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": { "type": "boolean", "default": false, @@ -447,6 +456,12 @@ "description": "%format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces%", "scope": "resource" }, + "javascript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": { + "type": "boolean", + "default": true, + "description": "%format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces%", + "scope": "resource" + }, "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": { "type": "boolean", "default": false, @@ -647,16 +662,82 @@ "description": "%typescript.preferences.importModuleSpecifier%", "scope": "resource" }, + "javascript.preferences.importModuleSpecifierEnding": { + "type": "string", + "enum": [ + "auto", + "minimal", + "index", + "js" + ], + "markdownEnumDescriptions": [ + "%typescript.preferences.importModuleSpecifierEnding.auto%", + "%typescript.preferences.importModuleSpecifierEnding.minimal%", + "%typescript.preferences.importModuleSpecifierEnding.index%", + "%typescript.preferences.importModuleSpecifierEnding.js%" + ], + "default": "auto", + "description": "%typescript.preferences.importModuleSpecifierEnding%", + "scope": "resource" + }, + "typescript.preferences.importModuleSpecifierEnding": { + "type": "string", + "enum": [ + "auto", + "minimal", + "index", + "js" + ], + "markdownEnumDescriptions": [ + "%typescript.preferences.importModuleSpecifierEnding.auto%", + "%typescript.preferences.importModuleSpecifierEnding.minimal%", + "%typescript.preferences.importModuleSpecifierEnding.index%", + "%typescript.preferences.importModuleSpecifierEnding.js%" + ], + "default": "auto", + "description": "%typescript.preferences.importModuleSpecifierEnding%", + "scope": "resource" + }, + "typescript.preferences.includePackageJsonAutoImports": { + "type": "string", + "enum": [ + "auto", + "on", + "off" + ], + "enumDescriptions": [ + "%typescript.preferences.includePackageJsonAutoImports.auto%", + "%typescript.preferences.includePackageJsonAutoImports.on%", + "%typescript.preferences.includePackageJsonAutoImports.off%" + ], + "default": "auto", + "markdownDescription": "%typescript.preferences.includePackageJsonAutoImports%", + "scope": "window" + }, "javascript.preferences.renameShorthandProperties": { "type": "boolean", "default": true, - "description": "%typescript.preferences.renameShorthandProperties%", + "description": "%typescript.preferences.useAliasesForRenames%", + "deprecationMessage": "%typescript.preferences.renameShorthandProperties.deprecationMessage%", "scope": "resource" }, "typescript.preferences.renameShorthandProperties": { "type": "boolean", "default": true, - "description": "%typescript.preferences.renameShorthandProperties%", + "description": "%typescript.preferences.useAliasesForRenames%", + "deprecationMessage": "%typescript.preferences.renameShorthandProperties.deprecationMessage%", + "scope": "resource" + }, + "javascript.preferences.useAliasesForRenames": { + "type": "boolean", + "default": true, + "description": "%typescript.preferences.useAliasesForRenames%", + "scope": "resource" + }, + "typescript.preferences.useAliasesForRenames": { + "type": "boolean", + "default": true, + "description": "%typescript.preferences.useAliasesForRenames%", "scope": "resource" }, "typescript.updateImportsOnFileMove.enabled": { @@ -730,6 +811,85 @@ "default": 3072, "description": "%configuration.tsserver.maxTsServerMemory%", "scope": "window" + }, + "typescript.tsserver.experimental.enableProjectDiagnostics": { + "type": "boolean", + "default": false, + "description": "%configuration.tsserver.experimental.enableProjectDiagnostics%", + "scope": "window" + }, + "typescript.tsserver.watchOptions": { + "type": "object", + "description": "%configuration.tsserver.watchOptions%", + "scope": "window", + "properties": { + "watchFile": { + "type": "string", + "description": "%configuration.tsserver.watchOptions.watchFile%", + "enum": [ + "fixedPollingInterval", + "priorityPollingInterval", + "dynamicPriorityPolling", + "useFsEvents", + "useFsEventsOnParentDirectory" + ], + "enumDescriptions": [ + "%configuration.tsserver.watchOptions.watchFile.fixedPollingInterval%", + "%configuration.tsserver.watchOptions.watchFile.priorityPollingInterval%", + "%configuration.tsserver.watchOptions.watchFile.dynamicPriorityPolling%", + "%configuration.tsserver.watchOptions.watchFile.useFsEvents%", + "%configuration.tsserver.watchOptions.watchFile.useFsEventsOnParentDirectory%" + ], + "default": "useFsEvents" + }, + "watchDirectory": { + "type": "string", + "description": "%configuration.tsserver.watchOptions.watchDirectory%", + "enum": [ + "fixedPollingInterval", + "dynamicPriorityPolling", + "useFsEvents" + ], + "enumDescriptions": [ + "%configuration.tsserver.watchOptions.watchDirectory.fixedPollingInterval%", + "%configuration.tsserver.watchOptions.watchDirectory.dynamicPriorityPolling%", + "%configuration.tsserver.watchOptions.watchDirectory.useFsEvents%" + ], + "default": "useFsEvents" + }, + "fallbackPolling": { + "type": "string", + "description": "%configuration.tsserver.watchOptions.fallbackPolling%", + "enum": [ + "fixedPollingInterval", + "priorityPollingInterval", + "dynamicPriorityPolling" + ], + "enumDescriptions": [ + "configuration.tsserver.watchOptions.fallbackPolling.fixedPollingInterval", + "configuration.tsserver.watchOptions.fallbackPolling.priorityPollingInterval", + "configuration.tsserver.watchOptions.fallbackPolling.dynamicPriorityPolling" + ] + }, + "synchronousWatchDirectory": { + "type": "boolean", + "description": "%configuration.tsserver.watchOptions.synchronousWatchDirectory%" + } + } + }, + "typescript.workspaceSymbols.scope": { + "type": "string", + "enum": [ + "allOpenProjects", + "currentProject" + ], + "enumDescriptions": [ + "%typescript.workspaceSymbols.scope.allOpenProjects%", + "%typescript.workspaceSymbols.scope.currentProject%" + ], + "default": "allOpenProjects", + "markdownDescription": "%typescript.workspaceSymbols.scope%", + "scope": "window" } } }, @@ -882,10 +1042,10 @@ "background": { "activeOnStart": true, "beginsPattern": { - "regexp": "^\\s*(?:message TS6032:|\\[?\\D*\\d{1,2}:\\d{1,2}:\\d{1,2}\\D*(?:\\]| -)) File change detected\\. Starting incremental compilation\\.\\.\\." + "regexp": "^\\s*(?:message TS6032:|\\[?\\D*\\d{1,2}[:.]\\d{1,2}[:.]\\d{1,2}\\D*(├\\D*\\d{1,2}\\D+┤)?(?:\\]| -)) File change detected\\. Starting incremental compilation\\.\\.\\." }, "endsPattern": { - "regexp": "^\\s*(?:message TS6042:|\\[?\\D*\\d{1,2}:\\d{1,2}:\\d{1,2}\\D*(?:\\]| -)) (?:Compilation complete\\.|Found \\d+ errors?\\.) Watching for file changes\\." + "regexp": "^\\s*(?:message TS6042:|\\[?\\D*\\d{1,2}[:.]\\d{1,2}[:.]\\d{1,2}\\D*(├\\D*\\d{1,2}\\D+┤)?(?:\\]| -)) (?:Compilation complete\\.|Found \\d+ errors?\\.) Watching for file changes\\." } } } diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index adad367759b..69779f49df7 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -7,15 +7,16 @@ "configuration.suggest.includeAutomaticOptionalChainCompletions": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires TS 3.7+ and strict null checks to be enabled.", "typescript.tsdk.desc": "Specifies the folder path to the tsserver and lib*.d.ts files under a TypeScript install to use for IntelliSense, for example: `./node_modules/typescript/lib`.\n\n- When specified as a user setting, the TypeScript version from `typescript.tsdk` automatically replaces the built-in TypeScript version.\n- When specified as a workspace setting, `typescript.tsdk` allows you to switch to use that workspace version of TypeScript for IntelliSense with the `TypeScript: Select TypeScript version` command.\n\nSee the [TypeScript documentation](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions) for more detail about managing TypeScript versions.", "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.", + "typescript.enablePromptUseWorkspaceTsdk": "Enables prompting of users to use the TypeScript version configured in the workspace for Intellisense.", "typescript.tsserver.log": "Enables logging of the TS server to a file. This log can be used to diagnose TS Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project.", - "typescript.tsserver.pluginPaths": "Additional paths to discover TypeScript Language Service plugins. Requires using TypeScript 2.3.0 or newer in the workspace.", + "typescript.tsserver.pluginPaths": "Additional paths to discover TypeScript Language Service plugins.", "typescript.tsserver.pluginPaths.item": "Either an absolute or relative path. Relative path will be resolved against workspace folder(s).", "typescript.tsserver.trace": "Enables tracing of messages sent to the TS server. This trace can be used to diagnose TS Server issues. The trace may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.validate.enable": "Enable/disable TypeScript validation.", "typescript.format.enable": "Enable/disable default TypeScript formatter.", "javascript.format.enable": "Enable/disable default JavaScript formatter.", "format.insertSpaceAfterCommaDelimiter": "Defines space handling after a comma delimiter.", - "format.insertSpaceAfterConstructor": "Defines space handling after the constructor keyword. Requires using TypeScript 2.3.0 or newer in the workspace.", + "format.insertSpaceAfterConstructor": "Defines space handling after the constructor keyword.", "format.insertSpaceAfterSemicolonInForStatements": "Defines space handling after a semicolon in a for statement.", "format.insertSpaceBeforeAndAfterBinaryOperators": "Defines space handling after a binary operator.", "format.insertSpaceAfterKeywordsInControlFlowStatements": "Defines space handling after keywords in a control flow statement.", @@ -23,10 +24,11 @@ "format.insertSpaceBeforeFunctionParenthesis": "Defines space handling before function argument parentheses.", "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": "Defines space handling after opening and before closing non-empty parenthesis.", "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": "Defines space handling after opening and before closing non-empty brackets.", - "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Defines space handling after opening and before closing non-empty braces. Requires using TypeScript 2.3.0 or newer in the workspace.", + "format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": "Defines space handling after opening and before closing non-empty braces.", + "format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": "Defines space handling after opening and before closing empty braces.", "format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": "Defines space handling after opening and before closing template string braces.", "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces.", - "format.insertSpaceAfterTypeAssertion": "Defines space handling after type assertions in TypeScript. Requires using TypeScript 2.4 or newer in the workspace.", + "format.insertSpaceAfterTypeAssertion": "Defines space handling after type assertions in TypeScript.", "format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.", "format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.", "format.semicolons": "Defines handling of optional semicolons. Requires using TypeScript 3.7 or newer in the workspace.", @@ -44,8 +46,8 @@ "typescript.restartTsServer": "Restart TS server", "typescript.selectTypeScriptVersion.title": "Select TypeScript Version...", "typescript.reportStyleChecksAsWarnings": "Report style checks as warnings.", - "javascript.implicitProjectConfig.checkJs": "Enable/disable semantic checking of JavaScript files. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", - "typescript.npm": "Specifies the path to the npm executable used for Automatic Type Acquisition. Requires using TypeScript 2.3.4 or newer in the workspace.", + "javascript.implicitProjectConfig.checkJs": "Enable/disable semantic checking of JavaScript files. Existing jsconfig.json or tsconfig.json files override this setting.", + "typescript.npm": "Specifies the path to the npm executable used for Automatic Type Acquisition.", "typescript.check.npmIsInstalled": "Check if npm is installed for Automatic Type Acquisition.", "configuration.suggest.names": "Enable/disable including unique names from the file in JavaScript suggestions. Note that name suggestions are always disabled in JavaScript code that is semantically checked using `@ts-check` or `checkJs`.", "typescript.tsc.autoDetect": "Controls auto detection of tsc tasks.", @@ -58,26 +60,56 @@ "configuration.suggest.paths": "Enable/disable suggestions for paths in import statements and require calls.", "configuration.tsserver.useSeparateSyntaxServer": "Enable/disable spawning a separate TypeScript server that can more quickly respond to syntax related operations, such as calculating folding or computing document symbols. Requires using TypeScript 3.4.0 or newer in the workspace.", "configuration.tsserver.maxTsServerMemory": "Set the maximum amount of memory (in MB) to allocate to the TypeScript server process", - "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of `null` uses VS Code's locale.", - "javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", - "configuration.suggest.autoImports": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.", + "configuration.tsserver.experimental.enableProjectDiagnostics": "(Experimental) Enables project wide error reporting.", + "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Default of `null` uses VS Code's locale.", + "javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting.", + "configuration.suggest.autoImports": "Enable/disable auto import suggestions.", "taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.", - "javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", - "typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", - "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: `single` quotes, `double` quotes, or `auto` infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.", + "javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor.", + "typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor.", + "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: `single` quotes, `double` quotes, or `auto` infer quote type from existing imports.", "typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports.", "typescript.preferences.importModuleSpecifier.auto": "Automatically select import path style. Prefers using a relative import if `baseUrl` is configured and the relative path has fewer segments than the non-relative import.", "typescript.preferences.importModuleSpecifier.relative": "Relative to the file location.", "typescript.preferences.importModuleSpecifier.nonRelative": "Based on the `baseUrl` configured in your `jsconfig.json` / `tsconfig.json`.", - "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Requires using TypeScript 2.9 or newer in the workspace.", + "typescript.preferences.importModuleSpecifierEnding": "Preferred path ending for auto imports.", + "typescript.preferences.importModuleSpecifierEnding.auto": "Use project settings to select a default.", + "typescript.preferences.importModuleSpecifierEnding.minimal": "Shorten `./component/index.js` to `./component`.", + "typescript.preferences.importModuleSpecifierEnding.index": "Shorten `./component/index.js` to `./component/index`.", + "typescript.preferences.importModuleSpecifierEnding.js": "Do not shorten path endings; include the `.js` extension.", + "typescript.preferences.includePackageJsonAutoImports": "Enable/disable searching `package.json` dependencies for available auto imports.", + "typescript.preferences.includePackageJsonAutoImports.auto": "Search dependencies based on estimated performance impact.", + "typescript.preferences.includePackageJsonAutoImports.on": "Always search dependencies.", + "typescript.preferences.includePackageJsonAutoImports.off": "Never search dependencies.", + "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code.", "typescript.updateImportsOnFileMove.enabled.prompt": "Prompt on each rename.", "typescript.updateImportsOnFileMove.enabled.always": "Always update paths automatically.", "typescript.updateImportsOnFileMove.enabled.never": "Never rename paths and don't prompt.", - "typescript.autoClosingTags": "Enable/disable automatic closing of JSX tags. Requires using TypeScript 3.0 or newer in the workspace.", + "typescript.autoClosingTags": "Enable/disable automatic closing of JSX tags.", "typescript.suggest.enabled": "Enabled/disable autocomplete suggestions.", "configuration.surveys.enabled": "Enabled/disable occasional surveys that help us improve VS Code's JavaScript and TypeScript support.", "configuration.suggest.completeJSDocs": "Enable/disable suggestion to complete JSDoc comments.", - "typescript.preferences.renameShorthandProperties": "Enable/disable introducing aliases for object shorthand properties during renames. Requires using TypeScript 3.4 or newer in the workspace.", + "configuration.tsserver.watchOptions": "Configure which watching strategies should be used to keep track of files and directories. Requires using TypeScript 3.8+ in the workspace.", + "configuration.tsserver.watchOptions.watchFile": "Strategy for how individual files are watched.", + "configuration.tsserver.watchOptions.watchFile.fixedPollingInterval": "Check every file for changes several times a second at a fixed interval.", + "configuration.tsserver.watchOptions.watchFile.priorityPollingInterval": "Check every file for changes several times a second, but use heuristics to check certain types of files less frequently than others.", + "configuration.tsserver.watchOptions.watchFile.dynamicPriorityPolling": "Use a dynamic queue where less-frequently modified files will be checked less often.", + "configuration.tsserver.watchOptions.watchFile.useFsEvents": "Attempt to use the operating system/file system’s native events for file changes.", + "configuration.tsserver.watchOptions.watchFile.useFsEventsOnParentDirectory": "Attempt to use the operating system/file system’s native events to listen for changes on a file’s containing directories. This can use fewer file watchers, but might be less accurate.", + "configuration.tsserver.watchOptions.watchDirectory": "Strategy for how entire directory trees are watched under systems that lack recursive file-watching functionality.", + "configuration.tsserver.watchOptions.watchDirectory.fixedPollingInterval": "Check every directory for changes several times a second at a fixed interval.", + "configuration.tsserver.watchOptions.watchDirectory.dynamicPriorityPolling": "Use a dynamic queue where less-frequently modified directories will be checked less often.", + "configuration.tsserver.watchOptions.watchDirectory.useFsEvents": "Attempt to use the operating system/file system’s native events for directory changes.", + "configuration.tsserver.watchOptions.fallbackPolling": "When using file system events, this option specifies the polling strategy that gets used when the system runs out of native file watchers and/or doesn’t support native file watchers.", + "configuration.tsserver.watchOptions.fallbackPolling.fixedPollingInterval": "Check every file for changes several times a second at a fixed interval.", + "configuration.tsserver.watchOptions.fallbackPolling.priorityPollingInterval": "Check every file for changes several times a second, but use heuristics to check certain types of files less frequently than others.", + "configuration.tsserver.watchOptions.fallbackPolling.dynamicPriorityPolling ": "Use a dynamic queue where less-frequently modified files will be checked less often.", + "configuration.tsserver.watchOptions.synchronousWatchDirectory": "Disable deferred watching on directories. Deferred watching is useful when lots of file changes might occur at once (e.g. a change in node_modules from running npm install), but you might want to disable it with this flag for some less-common setups.", + "typescript.preferences.renameShorthandProperties.deprecationMessage": "The setting 'typescript.preferences.renameShorthandProperties' has been deprecated in favor of 'typescript.preferences.useAliasesForRenames'", + "typescript.preferences.useAliasesForRenames": "Enable/disable introducing aliases for object shorthand properties during renames. Requires using TypeScript 3.4 or newer in the workspace.", + "typescript.workspaceSymbols.scope": "Controls which files are searched by [go to symbol in workspace](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name).", + "typescript.workspaceSymbols.scope.allOpenProjects": "Search all open JavaScript or TypeScript projects for symbols. Requires using TypeScript 3.9 or newer in the workspace.", + "typescript.workspaceSymbols.scope.currentProject": "Only search for symbols in the current JavaScript or TypeScript project.", "codeActions.refactor.extract.constant.title": "Extract constant", "codeActions.refactor.extract.constant.description": "Extract expression to constant.", "codeActions.refactor.extract.function.title": "Extract function", @@ -97,6 +129,5 @@ "codeActions.refactor.rewrite.parameters.toDestructured.title": "Convert parameters to destructured object", "codeActions.refactor.rewrite.property.generateAccessors.title": "Generate accessors", "codeActions.refactor.rewrite.property.generateAccessors.description": "Generate 'get' and 'set' accessors", - "codeActions.source.organizeImports.title": "Organize imports", - "documentation.refactoring.title": "Learn more about JS/TS refactorings" + "codeActions.source.organizeImports.title": "Organize imports" } diff --git a/extensions/typescript-language-features/src/utils/commandManager.ts b/extensions/typescript-language-features/src/commands/commandManager.ts similarity index 100% rename from extensions/typescript-language-features/src/utils/commandManager.ts rename to extensions/typescript-language-features/src/commands/commandManager.ts diff --git a/extensions/typescript-language-features/src/commands/configurePlugin.ts b/extensions/typescript-language-features/src/commands/configurePlugin.ts index 8af85d8b94e..f781c4a50fa 100644 --- a/extensions/typescript-language-features/src/commands/configurePlugin.ts +++ b/extensions/typescript-language-features/src/commands/configurePlugin.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Command } from '../utils/commandManager'; import { PluginManager } from '../utils/plugins'; +import { Command } from './commandManager'; export class ConfigurePluginCommand implements Command { public readonly id = '_typescript.configurePlugin'; diff --git a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts index 37873767085..11adec35251 100644 --- a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts +++ b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts @@ -4,15 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { nulToken } from '../utils/cancellation'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; -import { isImplicitProjectConfigFile, openOrCreateConfigFile } from '../utils/tsconfig'; -import { ServerResponse } from '../typescriptService'; - -const localize = nls.loadMessageBundle(); +import { openProjectConfigForFile, ProjectType } from '../utils/tsconfig'; +import { Command } from './commandManager'; export class TypeScriptGoToProjectConfigCommand implements Command { public readonly id = 'typescript.goToProjectConfig'; @@ -24,7 +19,7 @@ export class TypeScriptGoToProjectConfigCommand implements Command { public execute() { const editor = vscode.window.activeTextEditor; if (editor) { - goToProjectConfig(this.lazyClientHost.value, true, editor.document.uri); + openProjectConfigForFile(ProjectType.TypeScript, this.lazyClientHost.value.serviceClient, editor.document.uri); } } } @@ -39,79 +34,8 @@ export class JavaScriptGoToProjectConfigCommand implements Command { public execute() { const editor = vscode.window.activeTextEditor; if (editor) { - goToProjectConfig(this.lazyClientHost.value, false, editor.document.uri); + openProjectConfigForFile(ProjectType.JavaScript, this.lazyClientHost.value.serviceClient, editor.document.uri); } } } -async function goToProjectConfig( - clientHost: TypeScriptServiceClientHost, - isTypeScriptProject: boolean, - resource: vscode.Uri -): Promise<void> { - const client = clientHost.serviceClient; - const rootPath = client.getWorkspaceRootForResource(resource); - if (!rootPath) { - vscode.window.showInformationMessage( - localize( - 'typescript.projectConfigNoWorkspace', - 'Please open a folder in VS Code to use a TypeScript or JavaScript project')); - return; - } - - const file = client.toPath(resource); - // TSServer errors when 'projectInfo' is invoked on a non js/ts file - if (!file || !await clientHost.handles(resource)) { - vscode.window.showWarningMessage( - localize( - 'typescript.projectConfigUnsupportedFile', - 'Could not determine TypeScript or JavaScript project. Unsupported file type')); - return; - } - - let res: ServerResponse.Response<protocol.ProjectInfoResponse> | undefined; - try { - res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); - } catch { - // noop - } - - if (res?.type !== 'response' || !res.body) { - vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project')); - return; - } - - const { configFileName } = res.body; - if (!isImplicitProjectConfigFile(configFileName)) { - const doc = await vscode.workspace.openTextDocument(configFileName); - vscode.window.showTextDocument(doc, vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined); - return; - } - - enum ProjectConfigAction { - None, - CreateConfig, - LearnMore, - } - - interface ProjectConfigMessageItem extends vscode.MessageItem { - id: ProjectConfigAction; - } - - const selected = await vscode.window.showInformationMessage<ProjectConfigMessageItem>( - (isTypeScriptProject - ? localize('typescript.noTypeScriptProjectConfig', 'File is not part of a TypeScript project. Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=841896') - : localize('typescript.noJavaScriptProjectConfig', 'File is not part of a JavaScript project Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=759670') - ), { - title: isTypeScriptProject - ? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json') - : localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'), - id: ProjectConfigAction.CreateConfig, - }); - - switch (selected && selected.id) { - case ProjectConfigAction.CreateConfig: - openOrCreateConfigFile(isTypeScriptProject, rootPath, client.configuration); - return; - } -} diff --git a/extensions/typescript-language-features/src/commands/index.ts b/extensions/typescript-language-features/src/commands/index.ts index 8e4cfddcccd..7ab7ae09d20 100644 --- a/extensions/typescript-language-features/src/commands/index.ts +++ b/extensions/typescript-language-features/src/commands/index.ts @@ -4,22 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { CommandManager } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; import { PluginManager } from '../utils/plugins'; +import { CommandManager } from './commandManager'; import { ConfigurePluginCommand } from './configurePlugin'; import { JavaScriptGoToProjectConfigCommand, TypeScriptGoToProjectConfigCommand } from './goToProjectConfiguration'; +import { LearnMoreAboutRefactoringsCommand } from './learnMoreAboutRefactorings'; import { OpenTsServerLogCommand } from './openTsServerLog'; import { ReloadJavaScriptProjectsCommand, ReloadTypeScriptProjectsCommand } from './reloadProject'; import { RestartTsServerCommand } from './restartTsServer'; import { SelectTypeScriptVersionCommand } from './selectTypeScriptVersion'; -import { LearnMoreAboutRefactoringsCommand } from './learnMoreAboutRefactorings'; -export function registerCommands( +export function registerBaseCommands( commandManager: CommandManager, lazyClientHost: Lazy<TypeScriptServiceClientHost>, pluginManager: PluginManager -) { +): void { commandManager.register(new ReloadTypeScriptProjectsCommand(lazyClientHost)); commandManager.register(new ReloadJavaScriptProjectsCommand(lazyClientHost)); commandManager.register(new SelectTypeScriptVersionCommand(lazyClientHost)); diff --git a/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts b/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts index 3c1673bb63c..b166397e38b 100644 --- a/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts +++ b/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts @@ -4,12 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Command } from '../utils/commandManager'; +import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { Command } from './commandManager'; export class LearnMoreAboutRefactoringsCommand implements Command { - public readonly id = '_typescript.learnMoreAboutRefactorings'; + public static readonly id = '_typescript.learnMoreAboutRefactorings'; + public readonly id = LearnMoreAboutRefactoringsCommand.id; public execute() { - vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=2114477')); + const docUrl = vscode.window.activeTextEditor && isTypeScriptDocument(vscode.window.activeTextEditor.document) + ? 'https://go.microsoft.com/fwlink/?linkid=2114477' + : 'https://go.microsoft.com/fwlink/?linkid=2116761'; + + vscode.env.openExternal(vscode.Uri.parse(docUrl)); } } diff --git a/extensions/typescript-language-features/src/commands/openTsServerLog.ts b/extensions/typescript-language-features/src/commands/openTsServerLog.ts index be47a985cd7..cd41445fef2 100644 --- a/extensions/typescript-language-features/src/commands/openTsServerLog.ts +++ b/extensions/typescript-language-features/src/commands/openTsServerLog.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class OpenTsServerLogCommand implements Command { public readonly id = 'typescript.openTsServerLog'; diff --git a/extensions/typescript-language-features/src/commands/reloadProject.ts b/extensions/typescript-language-features/src/commands/reloadProject.ts index fbba68eb94e..4da59685f67 100644 --- a/extensions/typescript-language-features/src/commands/reloadProject.ts +++ b/extensions/typescript-language-features/src/commands/reloadProject.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class ReloadTypeScriptProjectsCommand implements Command { public readonly id = 'typescript.reloadProjects'; diff --git a/extensions/typescript-language-features/src/commands/restartTsServer.ts b/extensions/typescript-language-features/src/commands/restartTsServer.ts index a357224d352..77dcae870ee 100644 --- a/extensions/typescript-language-features/src/commands/restartTsServer.ts +++ b/extensions/typescript-language-features/src/commands/restartTsServer.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class RestartTsServerCommand implements Command { public readonly id = 'typescript.restartTsServer'; diff --git a/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts b/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts index dc771df8bc2..d70f59472ff 100644 --- a/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts +++ b/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class SelectTypeScriptVersionCommand implements Command { public readonly id = 'typescript.selectTypeScriptVersion'; @@ -15,6 +15,6 @@ export class SelectTypeScriptVersionCommand implements Command { ) { } public execute() { - this.lazyClientHost.value.serviceClient.onVersionStatusClicked(); + this.lazyClientHost.value.serviceClient.showVersionPicker(); } } diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts new file mode 100644 index 00000000000..b6d15a34b58 --- /dev/null +++ b/extensions/typescript-language-features/src/extension.browser.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Api, getExtensionApi } from './api'; +import { registerBaseCommands } from './commands/index'; +import { LanguageConfigurationManager } from './languageFeatures/languageConfiguration'; +import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost'; +import { noopRequestCancellerFactory } from './tsServer/cancellation'; +import { noopLogDirectoryProvider } from './tsServer/logDirectoryProvider'; +import { ITypeScriptVersionProvider, TypeScriptVersion, TypeScriptVersionSource } from './tsServer/versionProvider'; +import { WorkerServerProcess } from './tsServer/serverProcess.browser'; +import API from './utils/api'; +import { CommandManager } from './commands/commandManager'; +import { TypeScriptServiceConfiguration } from './utils/configuration'; +import { PluginManager } from './utils/plugins'; + +class StaticVersionProvider implements ITypeScriptVersionProvider { + + constructor( + private readonly _version: TypeScriptVersion + ) { } + + updateConfiguration(_configuration: TypeScriptServiceConfiguration): void { + // noop + } + + get defaultVersion() { return this._version; } + get bundledVersion() { return this._version; } + + readonly globalVersion = undefined; + readonly localVersion = undefined; + readonly localVersions = []; +} + +export function activate( + context: vscode.ExtensionContext +): Api { + const pluginManager = new PluginManager(); + context.subscriptions.push(pluginManager); + + const commandManager = new CommandManager(); + context.subscriptions.push(commandManager); + + context.subscriptions.push(new LanguageConfigurationManager()); + + const onCompletionAccepted = new vscode.EventEmitter<vscode.CompletionItem>(); + context.subscriptions.push(onCompletionAccepted); + + const versionProvider = new StaticVersionProvider( + new TypeScriptVersion( + TypeScriptVersionSource.Bundled, + vscode.Uri.joinPath(context.extensionUri, 'dist/browser/typescript-web/tsserver.web.js').toString(), + API.fromSimpleString('4.0.3'))); + + const lazyClientHost = createLazyClientHost(context, false, { + pluginManager, + commandManager, + logDirectoryProvider: noopLogDirectoryProvider, + cancellerFactory: noopRequestCancellerFactory, + versionProvider, + processFactory: WorkerServerProcess + }, item => { + onCompletionAccepted.fire(item); + }); + + registerBaseCommands(commandManager, lazyClientHost, pluginManager); + + // context.subscriptions.push(task.register(lazyClientHost.map(x => x.serviceClient))); + + import('./languageFeatures/tsconfig').then(module => { + context.subscriptions.push(module.register()); + }); + + context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager)); + + return getExtensionApi(onCompletionAccepted.event, pluginManager); +} diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index a7493570f1b..1d6ccd2914d 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -3,24 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as rimraf from 'rimraf'; import * as vscode from 'vscode'; import { Api, getExtensionApi } from './api'; -import { registerCommands } from './commands/index'; -import { LanguageConfigurationManager } from './features/languageConfiguration'; -import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; -import { flatten } from './utils/arrays'; -import * as electron from './utils/electron'; -import * as rimraf from 'rimraf'; -import { CommandManager } from './utils/commandManager'; -import * as fileSchemes from './utils/fileSchemes'; -import { standardLanguageDescriptions } from './utils/languageDescription'; -import { lazy, Lazy } from './utils/lazy'; -import LogDirectoryProvider from './utils/logDirectoryProvider'; -import ManagedFileContextManager from './utils/managedFileContext'; +import { registerBaseCommands } from './commands/index'; +import { LanguageConfigurationManager } from './languageFeatures/languageConfiguration'; +import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost'; +import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron'; +import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron'; +import { ChildServerProcess } from './tsServer/serverProcess.electron'; +import { DiskTypeScriptVersionProvider } from './tsServer/versionProvider.electron'; +import { CommandManager } from './commands/commandManager'; +import { onCaseInsenitiveFileSystem } from './utils/fileSystem.electron'; import { PluginManager } from './utils/plugins'; -import * as ProjectStatus from './utils/projectStatus'; -import { Surveyor } from './utils/surveyor'; -import TscTaskProvider from './features/task'; +import * as temp from './utils/temp.electron'; export function activate( context: vscode.ExtensionContext @@ -34,15 +30,29 @@ export function activate( const onCompletionAccepted = new vscode.EventEmitter<vscode.CompletionItem>(); context.subscriptions.push(onCompletionAccepted); - const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, item => { + const logDirectoryProvider = new NodeLogDirectoryProvider(context); + const versionProvider = new DiskTypeScriptVersionProvider(); + + context.subscriptions.push(new LanguageConfigurationManager()); + + const lazyClientHost = createLazyClientHost(context, onCaseInsenitiveFileSystem(), { + pluginManager, + commandManager, + logDirectoryProvider, + cancellerFactory: nodeRequestCancellerFactory, + versionProvider, + processFactory: ChildServerProcess, + }, item => { onCompletionAccepted.fire(item); }); - registerCommands(commandManager, lazyClientHost, pluginManager); - context.subscriptions.push(vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(lazyClientHost.map(x => x.serviceClient)))); - context.subscriptions.push(new LanguageConfigurationManager()); + registerBaseCommands(commandManager, lazyClientHost, pluginManager); - import('./features/tsconfig').then(module => { + import('./task/taskProvider').then(module => { + context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient))); + }); + + import('./languageFeatures/tsconfig').then(module => { context.subscriptions.push(module.register()); }); @@ -51,86 +61,6 @@ export function activate( return getExtensionApi(onCompletionAccepted.event, pluginManager); } -function createLazyClientHost( - context: vscode.ExtensionContext, - pluginManager: PluginManager, - commandManager: CommandManager, - onCompletionAccepted: (item: vscode.CompletionItem) => void, -): Lazy<TypeScriptServiceClientHost> { - return lazy(() => { - const logDirectoryProvider = new LogDirectoryProvider(context); - - const clientHost = new TypeScriptServiceClientHost( - standardLanguageDescriptions, - context.workspaceState, - pluginManager, - commandManager, - logDirectoryProvider, - onCompletionAccepted); - - context.subscriptions.push(clientHost); - - context.subscriptions.push(new Surveyor(context.globalState, clientHost.serviceClient)); - - clientHost.serviceClient.onReady(() => { - context.subscriptions.push( - ProjectStatus.create( - clientHost.serviceClient, - clientHost.serviceClient.telemetryReporter)); - }); - - return clientHost; - }); -} - -function lazilyActivateClient( - lazyClientHost: Lazy<TypeScriptServiceClientHost>, - pluginManager: PluginManager, -) { - const disposables: vscode.Disposable[] = []; - - const supportedLanguage = flatten([ - ...standardLanguageDescriptions.map(x => x.modeIds), - ...pluginManager.plugins.map(x => x.languages) - ]); - - let hasActivated = false; - const maybeActivate = (textDocument: vscode.TextDocument): boolean => { - if (!hasActivated && isSupportedDocument(supportedLanguage, textDocument)) { - hasActivated = true; - // Force activation - void lazyClientHost.value; - - disposables.push(new ManagedFileContextManager(resource => { - return lazyClientHost.value.serviceClient.toPath(resource); - })); - return true; - } - return false; - }; - - const didActivate = vscode.workspace.textDocuments.some(maybeActivate); - if (!didActivate) { - const openListener = vscode.workspace.onDidOpenTextDocument(doc => { - if (maybeActivate(doc)) { - openListener.dispose(); - } - }, undefined, disposables); - } - - return vscode.Disposable.from(...disposables); -} - -function isSupportedDocument( - supportedLanguage: string[], - document: vscode.TextDocument -): boolean { - if (supportedLanguage.indexOf(document.languageId) < 0) { - return false; - } - return fileSchemes.isSupportedScheme(document.uri.scheme); -} - export function deactivate() { - rimraf.sync(electron.getInstanceDir()); + rimraf.sync(temp.getInstanceTempDir()); } diff --git a/extensions/typescript-language-features/src/features/fixAll.ts b/extensions/typescript-language-features/src/features/fixAll.ts deleted file mode 100644 index c65dbf472a6..00000000000 --- a/extensions/typescript-language-features/src/features/fixAll.ts +++ /dev/null @@ -1,140 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import API from '../utils/api'; -import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import * as typeConverters from '../utils/typeConverters'; -import { DiagnosticsManager } from './diagnostics'; -import FileConfigurationManager from './fileConfigurationManager'; - -const localize = nls.loadMessageBundle(); - -const autoFixableDiagnosticCodes = new Set<number>([ - 2420, // Incorrectly implemented interface - 2552, // Cannot find name -]); - -class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { - - private static readonly kind = vscode.CodeActionKind.SourceFixAll.append('ts'); - - public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [TypeScriptAutoFixProvider.kind] - }; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly fileConfigurationManager: FileConfigurationManager, - private readonly diagnosticsManager: DiagnosticsManager, - ) { } - - public async provideCodeActions( - document: vscode.TextDocument, - _range: vscode.Range, - context: vscode.CodeActionContext, - token: vscode.CancellationToken - ): Promise<vscode.CodeAction[] | undefined> { - if (!context.only || !vscode.CodeActionKind.SourceFixAll.intersects(context.only)) { - return undefined; - } - - const file = this.client.toOpenedFilePath(document); - if (!file) { - return undefined; - } - - const autoFixableDiagnostics = this.getAutoFixableDiagnostics(document); - if (!autoFixableDiagnostics.length) { - return undefined; - } - - const fixAllAction = await this.getFixAllCodeAction(document, file, autoFixableDiagnostics, token); - return fixAllAction ? [fixAllAction] : undefined; - } - - private getAutoFixableDiagnostics( - document: vscode.TextDocument - ): vscode.Diagnostic[] { - if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { - return []; - } - - return this.diagnosticsManager.getDiagnostics(document.uri) - .filter(x => autoFixableDiagnosticCodes.has(x.code as number)); - } - - private async getFixAllCodeAction( - document: vscode.TextDocument, - file: string, - diagnostics: ReadonlyArray<vscode.Diagnostic>, - token: vscode.CancellationToken, - ): Promise<vscode.CodeAction | undefined> { - await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); - - const autoFixResponse = await this.getAutoFixEdit(file, diagnostics, token); - if (!autoFixResponse) { - return undefined; - } - const { edit, fixedDiagnostics } = autoFixResponse; - if (!edit.size) { - return undefined; - } - - const codeAction = new vscode.CodeAction( - localize('autoFix.label', 'Auto fix'), - TypeScriptAutoFixProvider.kind); - codeAction.edit = edit; - codeAction.diagnostics = fixedDiagnostics; - - return codeAction; - } - - private async getAutoFixEdit( - file: string, - diagnostics: ReadonlyArray<vscode.Diagnostic>, - token: vscode.CancellationToken, - ): Promise<{ edit: vscode.WorkspaceEdit, fixedDiagnostics: vscode.Diagnostic[] } | undefined> { - const edit = new vscode.WorkspaceEdit(); - const fixedDiagnostics: vscode.Diagnostic[] = []; - for (const diagnostic of diagnostics) { - const args: Proto.CodeFixRequestArgs = { - ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), - errorCodes: [+(diagnostic.code!)] - }; - const response = await this.client.execute('getCodeFixes', args, token); - if (response.type !== 'response' || !response.body || response.body.length > 1) { - continue; - } - - const fix = response.body[0]; - if (['fixClassIncorrectlyImplementsInterface', 'spelling'].includes(fix.fixName)) { - typeConverters.WorkspaceEdit.withFileCodeEdits(edit, this.client, fix.changes); - fixedDiagnostics.push(diagnostic); - } - } - - if (!fixedDiagnostics.length) { - return undefined; - } - - return { edit, fixedDiagnostics }; - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, - fileConfigurationManager: FileConfigurationManager, - diagnosticsManager: DiagnosticsManager) { - return new VersionDependentRegistration(client, API.v300, () => - new ConfigurationDependentRegistration('typescript', 'experimental.autoFix.enabled', () => - vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager), - TypeScriptAutoFixProvider.metadata))); -} diff --git a/extensions/typescript-language-features/src/features/hover.ts b/extensions/typescript-language-features/src/features/hover.ts deleted file mode 100644 index 5077f9648a0..00000000000 --- a/extensions/typescript-language-features/src/features/hover.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { tagsMarkdownPreview } from '../utils/previewer'; -import * as typeConverters from '../utils/typeConverters'; - - -class TypeScriptHoverProvider implements vscode.HoverProvider { - - public constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - public async provideHover( - document: vscode.TextDocument, - position: vscode.Position, - token: vscode.CancellationToken - ): Promise<vscode.Hover | undefined> { - const filepath = this.client.toOpenedFilePath(document); - if (!filepath) { - return undefined; - } - - const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - const response = await this.client.interruptGetErr(() => this.client.execute('quickinfo', args, token)); - if (response.type !== 'response' || !response.body) { - return undefined; - } - - return new vscode.Hover( - TypeScriptHoverProvider.getContents(response.body), - typeConverters.Range.fromTextSpan(response.body)); - } - - private static getContents( - data: Proto.QuickInfoResponseBody - ) { - const parts = []; - - if (data.displayString) { - parts.push({ language: 'typescript', value: data.displayString }); - } - - const tags = tagsMarkdownPreview(data.tags); - parts.push(data.documentation + (tags ? '\n\n' + tags : '')); - return parts; - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient -): vscode.Disposable { - return vscode.languages.registerHoverProvider(selector, - new TypeScriptHoverProvider(client)); -} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/callHierarchy.ts b/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts similarity index 76% rename from extensions/typescript-language-features/src/features/callHierarchy.ts rename to extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts index b5b4501d2ad..836f372f2ff 100644 --- a/extensions/typescript-language-features/src/features/callHierarchy.ts +++ b/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts @@ -3,19 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import * as typeConverters from '../utils/typeConverters'; -import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import * as Proto from '../protocol'; import * as path from 'path'; +import * as vscode from 'vscode'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import { parseKindModifier } from '../utils/modifiers'; +import * as typeConverters from '../utils/typeConverters'; class TypeScriptCallHierarchySupport implements vscode.CallHierarchyProvider { public static readonly minVersion = API.v380; + public constructor( - private readonly client: ITypeScriptServiceClient) { } + private readonly client: ITypeScriptServiceClient + ) { } public async prepareCallHierarchy( document: vscode.TextDocument, @@ -70,14 +74,14 @@ class TypeScriptCallHierarchySupport implements vscode.CallHierarchyProvider { } function isSourceFileItem(item: Proto.CallHierarchyItem) { - return item.kind === PConst.Kind.script || item.kind === PConst.Kind.module && item.selectionSpan.start.line === 0 && item.selectionSpan.start.offset === 0; + return item.kind === PConst.Kind.script || item.kind === PConst.Kind.module && item.selectionSpan.start.line === 1 && item.selectionSpan.start.offset === 1; } function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): vscode.CallHierarchyItem { const useFileName = isSourceFileItem(item); const name = useFileName ? path.basename(item.file) : item.name; - const detail = useFileName ? vscode.workspace.asRelativePath(path.dirname(item.file)) : ''; - return new vscode.CallHierarchyItem( + const detail = useFileName ? vscode.workspace.asRelativePath(path.dirname(item.file)) : item.containerName ?? ''; + const result = new vscode.CallHierarchyItem( typeConverters.SymbolKind.fromProtocolScriptElementKind(item.kind), name, detail, @@ -85,6 +89,12 @@ function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): vscode.Ca typeConverters.Range.fromTextSpan(item.span), typeConverters.Range.fromTextSpan(item.selectionSpan) ); + + const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined; + if (kindModifiers?.has(PConst.KindModifiers.depreacted)) { + result.tags = [vscode.SymbolTag.Deprecated]; + } + return result; } function fromProtocolCallHierchyIncomingCall(item: Proto.CallHierarchyIncomingCall): vscode.CallHierarchyIncomingCall { @@ -102,10 +112,14 @@ function fromProtocolCallHierchyOutgoingCall(item: Proto.CallHierarchyOutgoingCa } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient ) { - return new VersionDependentRegistration(client, TypeScriptCallHierarchySupport.minVersion, - () => vscode.languages.registerCallHierarchyProvider(selector, - new TypeScriptCallHierarchySupport(client))); + return conditionalRegistration([ + requireMinVersion(client, TypeScriptCallHierarchySupport.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCallHierarchyProvider(selector.semantic, + new TypeScriptCallHierarchySupport(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts similarity index 92% rename from extensions/typescript-language-features/src/features/baseCodeLensProvider.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts index d5f2f0538d0..7f0e3e5f097 100644 --- a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts @@ -5,11 +5,11 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { escapeRegExp } from '../utils/regexp'; -import * as typeConverters from '../utils/typeConverters'; -import { CachedResponse } from '../tsServer/cachedResponse'; +import type * as Proto from '../../protocol'; +import { CachedResponse } from '../../tsServer/cachedResponse'; +import { ITypeScriptServiceClient } from '../../typescriptService'; +import { escapeRegExp } from '../../utils/regexp'; +import * as typeConverters from '../../utils/typeConverters'; const localize = nls.loadMessageBundle(); diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts similarity index 78% rename from extensions/typescript-language-features/src/features/implementationsCodeLens.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts index 0ab2ae8719c..f06411ec5d8 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts @@ -5,13 +5,14 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import * as PConst from '../protocol.const'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; -import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens, getSymbolRange } from './baseCodeLensProvider'; -import { CachedResponse } from '../tsServer/cachedResponse'; -import * as typeConverters from '../utils/typeConverters'; +import type * as Proto from '../../protocol'; +import * as PConst from '../../protocol.const'; +import { CachedResponse } from '../../tsServer/cachedResponse'; +import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService'; +import { conditionalRegistration, requireSomeCapability, requireConfiguration } from '../../utils/dependentRegistration'; +import { DocumentSelector } from '../../utils/documentSelector'; +import * as typeConverters from '../../utils/typeConverters'; +import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -34,7 +35,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip const locations = response.body .map(reference => - // Only take first line on implementation: https://github.com/Microsoft/vscode/issues/23924 + // Only take first line on implementation: https://github.com/microsoft/vscode/issues/23924 new vscode.Location(this.client.toResource(reference.file), reference.start.line === reference.end.line ? typeConverters.Range.fromTextSpan(reference) @@ -75,7 +76,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip return getSymbolRange(document, item); case PConst.Kind.class: - case PConst.Kind.memberFunction: + case PConst.Kind.method: case PConst.Kind.memberVariable: case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: @@ -89,13 +90,16 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { - return new ConfigurationDependentRegistration(modeId, 'implementationsCodeLens.enabled', () => { - return vscode.languages.registerCodeLensProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'implementationsCodeLens.enabled'), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeLensProvider(selector.semantic, new TypeScriptImplementationsCodeLensProvider(client, cachedResponse)); }); } diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts similarity index 75% rename from extensions/typescript-language-features/src/features/referencesCodeLens.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts index 106e13425d0..8fe118de5d2 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -5,12 +5,14 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import * as PConst from '../protocol.const'; -import { CachedResponse } from '../tsServer/cachedResponse'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; -import * as typeConverters from '../utils/typeConverters'; +import type * as Proto from '../../protocol'; +import * as PConst from '../../protocol.const'; +import { CachedResponse } from '../../tsServer/cachedResponse'; +import { ExectuionTarget } from '../../tsServer/server'; +import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService'; +import { conditionalRegistration, requireConfiguration, requireSomeCapability } from '../../utils/dependentRegistration'; +import { DocumentSelector } from '../../utils/documentSelector'; +import * as typeConverters from '../../utils/typeConverters'; import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -27,7 +29,11 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> { const codeLens = inputCodeLens as ReferencesCodeLens; const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); - const response = await this.client.execute('references', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document }); + const response = await this.client.execute('references', args, token, { + lowPriority: true, + executionTarget: ExectuionTarget.Semantic, + cancelOnResourceChange: codeLens.document, + }); if (response.type !== 'response' || !response.body) { codeLens.command = response.type === 'cancelled' ? TypeScriptBaseCodeLensProvider.cancelledCommand @@ -94,11 +100,19 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens case PConst.Kind.enum: return getSymbolRange(document, item); - case PConst.Kind.memberFunction: + case PConst.Kind.method: case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: case PConst.Kind.constructorImplementation: case PConst.Kind.memberVariable: + // Don't show if child and parent have same start + // For https://github.com/microsoft/vscode/issues/90396 + if (parent && + typeConverters.Position.fromLocation(parent.spans[0].start).isEqual(typeConverters.Position.fromLocation(item.spans[0].start)) + ) { + return null; + } + // Only show if parent is a class type object (not a literal) switch (parent?.kind) { case PConst.Kind.class: @@ -114,13 +128,16 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { - return new ConfigurationDependentRegistration(modeId, 'referencesCodeLens.enabled', () => { - return vscode.languages.registerCodeLensProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'referencesCodeLens.enabled'), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeLensProvider(selector.semantic, new TypeScriptReferencesCodeLensProvider(client, cachedResponse, modeId)); }); } diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts similarity index 65% rename from extensions/typescript-language-features/src/features/completions.ts rename to extensions/typescript-language-features/src/languageFeatures/completions.ts index e0e9e558886..73f54f20c69 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -5,15 +5,16 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; +import { Command, CommandManager } from '../commands/commandManager'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; -import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeAction } from '../utils/codeAction'; -import { Command, CommandManager } from '../utils/commandManager'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; -import { memoize } from '../utils/memoize'; +import { conditionalRegistration, requireConfiguration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import { parseKindModifier } from '../utils/modifiers'; import * as Previewer from '../utils/previewer'; import { snippetForFunctionCall } from '../utils/snippetForFunctionCall'; import { TelemetryReporter } from '../utils/telemetry'; @@ -23,8 +24,6 @@ import FileConfigurationManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); -const knownTsTriggerCharacters = new Set<string>(['.', '"', '\'', '`', '/', '@', '<']); - interface DotAccessorContext { readonly range: vscode.Range; readonly text: string; @@ -34,62 +33,66 @@ interface CompletionContext { readonly isNewIdentifierLocation: boolean; readonly isMemberCompletion: boolean; readonly isInValidCommitCharacterContext: boolean; - readonly enableCallCompletions: boolean; + readonly dotAccessorContext?: DotAccessorContext; + + readonly enableCallCompletions: boolean; + readonly useCodeSnippetsOnMethodSuggest: boolean, + + readonly wordRange: vscode.Range | undefined; + readonly line: string; + + readonly useFuzzyWordRangeLogic: boolean, } class MyCompletionItem extends vscode.CompletionItem { + public readonly useCodeSnippet: boolean; constructor( public readonly position: vscode.Position, public readonly document: vscode.TextDocument, - line: string, public readonly tsEntry: Proto.CompletionEntry, - useCodeSnippetsOnMethodSuggest: boolean, - public readonly completionContext: CompletionContext, + private readonly completionContext: CompletionContext, public readonly metadata: any | undefined, ) { super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind)); if (tsEntry.source) { // De-prioritze auto-imports - // https://github.com/Microsoft/vscode/issues/40311 + // https://github.com/microsoft/vscode/issues/40311 this.sortText = '\uffff' + tsEntry.sortText; } else { this.sortText = tsEntry.sortText; } - if (tsEntry.isRecommended) { - this.preselect = true; - } - + this.preselect = tsEntry.isRecommended; this.position = position; - this.useCodeSnippet = useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method); - - if (tsEntry.replacementSpan) { - this.range = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan); - // Make sure we only replace a single line at most - if (!this.range.isSingleLine) { - this.range = new vscode.Range(this.range.start.line, this.range.start.character, this.range.start.line, line.length); - } - } + this.useCodeSnippet = completionContext.useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method); + this.range = this.getRangeFromReplacementSpan(tsEntry, completionContext, position); + this.commitCharacters = MyCompletionItem.getCommitCharacters(completionContext, tsEntry); this.insertText = tsEntry.insertText; - // Set filterText for intelliCode and bracket accessors , but not for `this.` completions since it results in - // them being overly prioritized. #74164 - this.filterText = tsEntry.insertText && !/^this\./.test(tsEntry.insertText) ? tsEntry.insertText : undefined; + this.filterText = this.getFilterText(completionContext.line, tsEntry.insertText); if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) { this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label); if (!this.range) { - this.range = completionContext.dotAccessorContext.range; + const replacementRange = this.getFuzzyWordRange(); + if (replacementRange) { + this.range = { + inserting: completionContext.dotAccessorContext.range, + replacing: completionContext.dotAccessorContext.range.union(replacementRange), + }; + } else { + this.range = completionContext.dotAccessorContext.range; + } this.insertText = this.filterText; } } if (tsEntry.kindModifiers) { - const kindModifiers = new Set(tsEntry.kindModifiers.split(/\s+/g)); + const kindModifiers = parseKindModifier(tsEntry.kindModifiers); if (kindModifiers.has(PConst.KindModifiers.optional)) { if (!this.insertText) { this.insertText = this.label; @@ -100,6 +103,9 @@ class MyCompletionItem extends vscode.CompletionItem { } this.label += '?'; } + if (kindModifiers.has(PConst.KindModifiers.depreacted)) { + this.tags = [vscode.CompletionItemTag.Deprecated]; + } if (kindModifiers.has(PConst.KindModifiers.color)) { this.kind = vscode.CompletionItemKind.Color; @@ -118,32 +124,91 @@ class MyCompletionItem extends vscode.CompletionItem { } } } - this.resolveRange(line); + + this.resolveRange(); } - private resolveRange(line: string): void { + private getRangeFromReplacementSpan(tsEntry: Proto.CompletionEntry, completionContext: CompletionContext, position: vscode.Position) { + if (!tsEntry.replacementSpan) { + return; + } + + let replaceRange = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan); + // Make sure we only replace a single line at most + if (!replaceRange.isSingleLine) { + replaceRange = new vscode.Range(replaceRange.start.line, replaceRange.start.character, replaceRange.start.line, completionContext.line.length); + } + return { + inserting: new vscode.Range(replaceRange.start, position), + replacing: replaceRange, + }; + } + + private getFilterText(line: string, insertText: string | undefined): string | undefined { + // Handle private field completions + if (this.tsEntry.name.startsWith('#')) { + const wordRange = this.completionContext.wordRange; + const wordStart = wordRange ? line.charAt(wordRange.start.character) : undefined; + if (insertText) { + if (insertText.startsWith('this.#')) { + return wordStart === '#' ? insertText : insertText.replace(/^this\.#/, ''); + } else { + return insertText; + } + } else { + return wordStart === '#' ? undefined : this.tsEntry.name.replace(/^#/, ''); + } + } + + // For `this.` completions, generally don't set the filter text since we don't want them to be overly prioritized. #74164 + if (insertText?.startsWith('this.')) { + return undefined; + } + + // Handle the case: + // ``` + // const xyz = { 'ab c': 1 }; + // xyz.ab| + // ``` + // In which case we want to insert a bracket accessor but should use `.abc` as the filter text instead of + // the bracketed insert text. + else if (insertText?.startsWith('[')) { + return insertText.replace(/^\[['"](.+)[['"]\]$/, '.$1'); + } + + // In all other cases, fallback to using the insertText + return insertText; + } + + private resolveRange(): void { if (this.range) { return; } - - const wordRange = this.document.getWordRangeAtPosition(this.position); - if (wordRange) { - // TODO: Reverted next line due to https://github.com/Microsoft/vscode/issues/66187 - // this.range = wordRange; + const replaceRange = this.getFuzzyWordRange(); + if (replaceRange) { + this.range = { + inserting: new vscode.Range(replaceRange.start, this.position), + replacing: replaceRange + }; } + } - // Try getting longer, prefix based range for completions that span words - const text = line.slice(Math.max(0, this.position.character - this.label.length), this.position.character).toLowerCase(); - const entryName = this.label.toLowerCase(); - for (let i = entryName.length; i >= 0; --i) { - if (text.endsWith(entryName.substr(0, i)) && (!wordRange || wordRange.start.character > this.position.character - i)) { - this.range = new vscode.Range( - new vscode.Position(this.position.line, Math.max(0, this.position.character - i)), - this.position); - break; + private getFuzzyWordRange() { + if (this.completionContext.useFuzzyWordRangeLogic) { + // Try getting longer, prefix based range for completions that span words + const text = this.completionContext.line.slice(Math.max(0, this.position.character - this.label.length), this.position.character).toLowerCase(); + const entryName = this.label.toLowerCase(); + for (let i = entryName.length; i >= 0; --i) { + if (text.endsWith(entryName.substr(0, i)) && (!this.completionContext.wordRange || this.completionContext.wordRange.start.character > this.position.character - i)) { + return new vscode.Range( + new vscode.Position(this.position.line, Math.max(0, this.position.character - i)), + this.position); + } } } + + return this.completionContext.wordRange; } private static convertKind(kind: string): vscode.CompletionItemKind { @@ -151,6 +216,7 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.primitiveType: case PConst.Kind.keyword: return vscode.CompletionItemKind.Keyword; + case PConst.Kind.const: case PConst.Kind.let: case PConst.Kind.variable: @@ -158,50 +224,63 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.alias: case PConst.Kind.parameter: return vscode.CompletionItemKind.Variable; + case PConst.Kind.memberVariable: case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: return vscode.CompletionItemKind.Field; + case PConst.Kind.function: case PConst.Kind.localFunction: return vscode.CompletionItemKind.Function; - case PConst.Kind.memberFunction: + + case PConst.Kind.method: case PConst.Kind.constructSignature: case PConst.Kind.callSignature: case PConst.Kind.indexSignature: return vscode.CompletionItemKind.Method; + case PConst.Kind.enum: return vscode.CompletionItemKind.Enum; + case PConst.Kind.enumMember: return vscode.CompletionItemKind.EnumMember; + case PConst.Kind.module: case PConst.Kind.externalModuleName: return vscode.CompletionItemKind.Module; + case PConst.Kind.class: case PConst.Kind.type: return vscode.CompletionItemKind.Class; + case PConst.Kind.interface: return vscode.CompletionItemKind.Interface; + case PConst.Kind.warning: return vscode.CompletionItemKind.Text; + case PConst.Kind.script: return vscode.CompletionItemKind.File; + case PConst.Kind.directory: return vscode.CompletionItemKind.Folder; + case PConst.Kind.string: return vscode.CompletionItemKind.Constant; + + default: + return vscode.CompletionItemKind.Property; } - return vscode.CompletionItemKind.Property; } - @memoize - public get commitCharacters(): string[] | undefined { - if (this.completionContext.isNewIdentifierLocation || !this.completionContext.isInValidCommitCharacterContext) { + private static getCommitCharacters(context: CompletionContext, entry: Proto.CompletionEntry): string[] | undefined { + if (context.isNewIdentifierLocation || !context.isInValidCommitCharacterContext) { return undefined; } const commitCharacters: string[] = []; - switch (this.tsEntry.kind) { + switch (entry.kind) { case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: case PConst.Kind.constructSignature: @@ -221,11 +300,11 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.memberVariable: case PConst.Kind.class: case PConst.Kind.function: - case PConst.Kind.memberFunction: + case PConst.Kind.method: case PConst.Kind.keyword: case PConst.Kind.parameter: commitCharacters.push('.', ',', ';'); - if (this.completionContext.enableCallCompletions) { + if (context.enableCallCompletions) { commitCharacters.push('('); } break; @@ -251,10 +330,24 @@ class CompletionAcceptedCommand implements Command { public constructor( private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, + private readonly telemetryReporter: TelemetryReporter, ) { } public execute(item: vscode.CompletionItem) { this.onCompletionAccepted(item); + if (item instanceof MyCompletionItem) { + /* __GDPR__ + "completions.accept" : { + "isPackageJsonImport" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('completions.accept', { + isPackageJsonImport: item.tsEntry.isPackageJsonImport ? 'true' : undefined, + }); + } } } @@ -275,29 +368,19 @@ class ApplyCompletionCodeActionCommand implements Command { return applyCodeAction(this.client, codeActions[0], nulToken); } - interface MyQuickPickItem extends vscode.QuickPickItem { - index: number; - } - - const selection = await vscode.window.showQuickPick<MyQuickPickItem>( - codeActions.map((action, i): MyQuickPickItem => ({ + const selection = await vscode.window.showQuickPick( + codeActions.map(action => ({ label: action.description, description: '', - index: i + action, })), { placeHolder: localize('selectCodeAction', 'Select code action to apply') - } - ); + }); - if (!selection) { - return false; + if (selection) { + return applyCodeAction(this.client, selection.action, nulToken); } - - const action = codeActions[selection.index]; - if (!action) { - return false; - } - return applyCodeAction(this.client, action, nulToken); + return false; } } @@ -306,7 +389,6 @@ interface CompletionConfiguration { readonly nameSuggestions: boolean; readonly pathSuggestions: boolean; readonly autoImportSuggestions: boolean; - readonly includeAutomaticOptionalChainCompletions: boolean; } namespace CompletionConfiguration { @@ -314,7 +396,6 @@ namespace CompletionConfiguration { export const nameSuggestions = 'suggest.names'; export const pathSuggestions = 'suggest.paths'; export const autoImportSuggestions = 'suggest.autoImports'; - export const includeAutomaticOptionalChainCompletions = 'suggest.includeAutomaticOptionalChainCompletions'; export function getConfigurationForResource( modeId: string, @@ -326,12 +407,11 @@ namespace CompletionConfiguration { pathSuggestions: config.get<boolean>(CompletionConfiguration.pathSuggestions, true), autoImportSuggestions: config.get<boolean>(CompletionConfiguration.autoImportSuggestions, true), nameSuggestions: config.get<boolean>(CompletionConfiguration.nameSuggestions, true), - includeAutomaticOptionalChainCompletions: config.get<boolean>(CompletionConfiguration.includeAutomaticOptionalChainCompletions, true), }; } } -class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider { +class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider<MyCompletionItem> { public static readonly triggerCharacters = ['.', '"', '\'', '`', '/', '@', '<', '#']; @@ -346,7 +426,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ) { commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); commandManager.register(new CompositeCommand()); - commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted)); + commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted, this.telemetryReporter)); } public async provideCompletionItems( @@ -354,9 +434,9 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext - ): Promise<vscode.CompletionList | null> { + ): Promise<vscode.CompletionList<MyCompletionItem> | undefined> { if (this.typingsStatus.isAcquiringTypings) { - return Promise.reject<vscode.CompletionList>({ + return Promise.reject<vscode.CompletionList<MyCompletionItem>>({ label: localize( { key: 'acquiringTypingsLabel', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] }, 'Acquiring typings...'), @@ -368,24 +448,25 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider const file = this.client.toOpenedFilePath(document); if (!file) { - return null; + return undefined; } const line = document.lineAt(position.line); const completionConfiguration = CompletionConfiguration.getConfigurationForResource(this.modeId, document.uri); if (!this.shouldTrigger(context, line, position)) { - return null; + return undefined; } + const wordRange = document.getWordRangeAtPosition(position); + await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); - const args: Proto.CompletionsRequestArgs & { includeAutomaticOptionalChainCompletions?: boolean } = { + const args: Proto.CompletionsRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(file, position), includeExternalModuleExports: completionConfiguration.autoImportSuggestions, includeInsertTextCompletions: true, triggerCharacter: this.getTsTriggerCharacter(context), - includeAutomaticOptionalChainCompletions: completionConfiguration.includeAutomaticOptionalChainCompletions, }; let isNewIdentifierLocation = true; @@ -394,35 +475,19 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider let dotAccessorContext: DotAccessorContext | undefined; let entries: ReadonlyArray<Proto.CompletionEntry>; let metadata: any | undefined; + let response: ServerResponse.Response<Proto.CompletionInfoResponse> | undefined; + let duration: number | undefined; if (this.client.apiVersion.gte(API.v300)) { const startTime = Date.now(); - let response: ServerResponse.Response<Proto.CompletionInfoResponse> | undefined; try { response = await this.client.interruptGetErr(() => this.client.execute('completionInfo', args, token)); } finally { - const duration: number = Date.now() - startTime; - - /* __GDPR__ - "completions.execute" : { - "duration" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "type" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "count" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "updateGraphDurationMs" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('completions.execute', { - duration: duration, - type: response?.type ?? 'unknown', - count: response?.type === 'response' && response.body ? response.body.entries.length : 0, - updateGraphDurationMs: response?.type === 'response' ? response.performanceData?.updateGraphDurationMs : undefined, - }); + duration = Date.now() - startTime; } if (response.type !== 'response' || !response.body) { - return null; + this.logCompletionsTelemetry(duration, response); + return undefined; } isNewIdentifierLocation = response.body.isNewIdentifierLocation; isMemberCompletion = response.body.isMemberCompletion; @@ -440,49 +505,91 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider } else { const response = await this.client.interruptGetErr(() => this.client.execute('completions', args, token)); if (response.type !== 'response' || !response.body) { - return null; + return undefined; } entries = response.body; metadata = response.metadata; } - const isInValidCommitCharacterContext = this.isInValidCommitCharacterContext(document, position); - const items = entries - .filter(entry => !shouldExcludeCompletionEntry(entry, completionConfiguration)) - .map(entry => new MyCompletionItem(position, document, line.text, entry, completionConfiguration.useCodeSnippetsOnMethodSuggest, { - isNewIdentifierLocation, - isMemberCompletion, - dotAccessorContext, - isInValidCommitCharacterContext, - enableCallCompletions: !completionConfiguration.useCodeSnippetsOnMethodSuggest - }, metadata)); + const completionContext = { + isNewIdentifierLocation, + isMemberCompletion, + dotAccessorContext, + isInValidCommitCharacterContext: this.isInValidCommitCharacterContext(document, position), + enableCallCompletions: !completionConfiguration.useCodeSnippetsOnMethodSuggest, + wordRange, + line: line.text, + useCodeSnippetsOnMethodSuggest: completionConfiguration.useCodeSnippetsOnMethodSuggest, + useFuzzyWordRangeLogic: this.client.apiVersion.lt(API.v390), + }; + + let includesPackageJsonImport = false; + const items: MyCompletionItem[] = []; + for (let entry of entries) { + if (!shouldExcludeCompletionEntry(entry, completionConfiguration)) { + items.push(new MyCompletionItem(position, document, entry, completionContext, metadata)); + includesPackageJsonImport = !!entry.isPackageJsonImport; + } + } + if (duration !== undefined) { + this.logCompletionsTelemetry(duration, response, includesPackageJsonImport); + } return new vscode.CompletionList(items, isIncomplete); } + private logCompletionsTelemetry( + duration: number, + response: ServerResponse.Response<Proto.CompletionInfoResponse> | undefined, + includesPackageJsonImport?: boolean + ) { + /* __GDPR__ + "completions.execute" : { + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "updateGraphDurationMs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "createAutoImportProviderProgramDurationMs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "includesPackageJsonImport" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('completions.execute', { + duration: duration, + type: response?.type ?? 'unknown', + count: response?.type === 'response' && response.body ? response.body.entries.length : 0, + updateGraphDurationMs: response?.type === 'response' ? response.performanceData?.updateGraphDurationMs : undefined, + createAutoImportProviderProgramDurationMs: response?.type === 'response' ? (response.performanceData as Proto.PerformanceData & { createAutoImportProviderProgramDurationMs?: number })?.createAutoImportProviderProgramDurationMs : undefined, + includesPackageJsonImport: includesPackageJsonImport ? 'true' : undefined, + }); + } + private getTsTriggerCharacter(context: vscode.CompletionContext): Proto.CompletionsTriggerCharacter | undefined { - // Workaround for https://github.com/Microsoft/TypeScript/issues/27321 - if (context.triggerCharacter === '@' - && this.client.apiVersion.gte(API.v310) && this.client.apiVersion.lt(API.v320) - ) { - return undefined; + switch (context.triggerCharacter) { + case '@': // Workaround for https://github.com/microsoft/TypeScript/issues/27321 + return this.client.apiVersion.gte(API.v310) && this.client.apiVersion.lt(API.v320) ? undefined : '@'; + + case '#': // Workaround for https://github.com/microsoft/TypeScript/issues/36367 + return this.client.apiVersion.lt(API.v381) ? undefined : '#'; + + case '.': + case '"': + case '\'': + case '`': + case '/': + case '<': + return context.triggerCharacter; } - if (context.triggerCharacter && !knownTsTriggerCharacters.has(context.triggerCharacter)) { - return undefined; - } - - return context.triggerCharacter as Proto.CompletionsTriggerCharacter; + return undefined; } public async resolveCompletionItem( - item: vscode.CompletionItem, + item: MyCompletionItem, token: vscode.CancellationToken - ): Promise<vscode.CompletionItem | undefined> { - if (!(item instanceof MyCompletionItem)) { - return undefined; - } - + ): Promise<MyCompletionItem | undefined> { const filepath = this.client.toOpenedFilePath(item.document); if (!filepath) { return undefined; @@ -524,7 +631,11 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider const { snippet, parameterCount } = snippetForFunctionCall(item, detail.displayParts); item.insertText = snippet; if (parameterCount > 0) { - commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + //Fix for https://github.com/microsoft/vscode/issues/104059 + //Don't show parameter hints if "editor.parameterHints.enabled": false + if (vscode.workspace.getConfiguration('editor.parameterHints').get('enabled')) { + commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + } } } } @@ -599,7 +710,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider position: vscode.Position ): boolean { if (this.client.apiVersion.lt(API.v320)) { - // Workaround for https://github.com/Microsoft/TypeScript/issues/27742 + // Workaround for https://github.com/microsoft/TypeScript/issues/27742 // Only enable dot completions when previous character not a dot preceded by whitespace. // Prevents incorrectly completing while typing spread operators. if (position.character > 1) { @@ -672,7 +783,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider document: vscode.TextDocument, token: vscode.CancellationToken ): Promise<boolean> { - // Workaround for https://github.com/Microsoft/TypeScript/issues/12677 + // Workaround for https://github.com/microsoft/TypeScript/issues/12677 // Don't complete function calls inside of destructive assignments or imports try { const args: Proto.FileLocationRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); @@ -691,7 +802,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider } // Don't complete function call if there is already something that looks like a function call - // https://github.com/Microsoft/vscode/issues/18131 + // https://github.com/microsoft/vscode/issues/18131 const after = document.lineAt(position.line).text.slice(position.character); return after.match(/^[a-z_$0-9]*\s*\(/gi) === null; } @@ -710,7 +821,7 @@ function shouldExcludeCompletionEntry( } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, typingsStatus: TypingsStatus, @@ -719,8 +830,12 @@ export function register( telemetryReporter: TelemetryReporter, onCompletionAccepted: (item: vscode.CompletionItem) => void ) { - return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => - vscode.languages.registerCompletionItemProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'suggest.enabled'), + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCompletionItemProvider(selector.syntax, new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, telemetryReporter, onCompletionAccepted), - ...TypeScriptCompletionItemProvider.triggerCharacters)); + ...TypeScriptCompletionItemProvider.triggerCharacters); + }); } diff --git a/extensions/typescript-language-features/src/features/definitionProviderBase.ts b/extensions/typescript-language-features/src/languageFeatures/definitionProviderBase.ts similarity index 100% rename from extensions/typescript-language-features/src/features/definitionProviderBase.ts rename to extensions/typescript-language-features/src/languageFeatures/definitionProviderBase.ts diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/languageFeatures/definitions.ts similarity index 75% rename from extensions/typescript-language-features/src/features/definitions.ts rename to extensions/typescript-language-features/src/languageFeatures/definitions.ts index fd3a1f10e79..6d6e4fc2851 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/definitions.ts @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; import DefinitionProviderBase from './definitionProviderBase'; @@ -37,10 +39,10 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase return response.body.definitions .map((location): vscode.DefinitionLink => { const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); - if ((location as any).contextStart) { + if (location.contextStart && location.contextEnd) { return { originSelectionRange: span, - targetRange: typeConverters.Range.fromLocations((location as any).contextStart, (location as any).contextEnd), + targetRange: typeConverters.Range.fromLocations(location.contextStart, location.contextEnd), targetUri: target.uri, targetSelectionRange: target.range, }; @@ -58,9 +60,13 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerDefinitionProvider(selector, - new TypeScriptDefinitionProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerDefinitionProvider(selector.syntax, + new TypeScriptDefinitionProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts similarity index 93% rename from extensions/typescript-language-features/src/features/diagnostics.ts rename to extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 327573e8c18..25a9591cc56 100644 --- a/extensions/typescript-language-features/src/features/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -78,7 +78,7 @@ class FileDiagnostics { return this.get(DiagnosticKind.Suggestion).filter(x => { if (!enableSuggestions) { // Still show unused - return x.tags && x.tags.includes(vscode.DiagnosticTag.Unnecessary); + return x.tags && (x.tags.includes(vscode.DiagnosticTag.Unnecessary) || x.tags.includes(vscode.DiagnosticTag.Deprecated)); } return true; }); @@ -142,17 +142,21 @@ class DiagnosticSettings { } export class DiagnosticsManager extends Disposable { - private readonly _diagnostics = new ResourceMap<FileDiagnostics>(); + private readonly _diagnostics: ResourceMap<FileDiagnostics>; private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; - private readonly _pendingUpdates = new ResourceMap<any>(); + private readonly _pendingUpdates: ResourceMap<any>; private readonly _updateDelay = 50; constructor( - owner: string + owner: string, + onCaseInsenitiveFileSystem: boolean ) { super(); + this._diagnostics = new ResourceMap<FileDiagnostics>(undefined, { onCaseInsenitiveFileSystem }); + this._pendingUpdates = new ResourceMap<any>(undefined, { onCaseInsenitiveFileSystem }); + this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); } diff --git a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts b/extensions/typescript-language-features/src/languageFeatures/directiveCommentCompletions.ts similarity index 81% rename from extensions/typescript-language-features/src/features/directiveCommentCompletions.ts rename to extensions/typescript-language-features/src/languageFeatures/directiveCommentCompletions.ts index c0a222e47df..2b0f683e589 100644 --- a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/directiveCommentCompletions.ts @@ -6,6 +6,8 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { DocumentSelector } from '../utils/documentSelector'; const localize = nls.loadMessageBundle(); @@ -14,7 +16,7 @@ interface Directive { readonly description: string; } -const directives: Directive[] = [ +const tsDirectives: Directive[] = [ { value: '@ts-check', description: localize( @@ -33,6 +35,16 @@ const directives: Directive[] = [ } ]; +const tsDirectives390: Directive[] = [ + ...tsDirectives, + { + value: '@ts-expect-error', + description: localize( + 'ts-expect-error', + "Suppresses @ts-check errors on the next line of a file, expecting at least one to exist.") + } +]; + class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvider { constructor( @@ -53,6 +65,10 @@ class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvide const prefix = line.slice(0, position.character); const match = prefix.match(/^\s*\/\/+\s?(@[a-zA-Z\-]*)?$/); if (match) { + const directives = this.client.apiVersion.gte(API.v390) + ? tsDirectives390 + : tsDirectives; + return directives.map(directive => { const item = new vscode.CompletionItem(directive.value, vscode.CompletionItemKind.Snippet); item.detail = directive.description; @@ -65,10 +81,10 @@ class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvide } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerCompletionItemProvider(selector, + return vscode.languages.registerCompletionItemProvider(selector.syntax, new DirectiveCommentCompletionProvider(client), '@'); } diff --git a/extensions/typescript-language-features/src/features/documentHighlight.ts b/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts similarity index 92% rename from extensions/typescript-language-features/src/features/documentHighlight.ts rename to extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts index 970a265f218..3a7b43ed5eb 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import { flatten } from '../utils/arrays'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightProvider { @@ -48,9 +49,9 @@ function convertDocumentHighlight(highlight: Proto.DocumentHighlightsItem): Read } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerDocumentHighlightProvider(selector, + return vscode.languages.registerDocumentHighlightProvider(selector.syntax, new TypeScriptDocumentHighlightProvider(client)); -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts similarity index 67% rename from extensions/typescript-language-features/src/features/documentSymbol.ts rename to extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts index 7145bbd47c3..c3c3f5d93aa 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import * as typeConverters from '../utils/typeConverters'; import { CachedResponse } from '../tsServer/cachedResponse'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import { DocumentSelector } from '../utils/documentSelector'; +import { parseKindModifier } from '../utils/modifiers'; +import * as typeConverters from '../utils/typeConverters'; const getSymbolKind = (kind: string): vscode.SymbolKind => { switch (kind) { @@ -16,7 +18,7 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => { case PConst.Kind.class: return vscode.SymbolKind.Class; case PConst.Kind.enum: return vscode.SymbolKind.Enum; case PConst.Kind.interface: return vscode.SymbolKind.Interface; - case PConst.Kind.memberFunction: return vscode.SymbolKind.Method; + case PConst.Kind.method: return vscode.SymbolKind.Method; case PConst.Kind.memberVariable: return vscode.SymbolKind.Property; case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Property; case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Property; @@ -32,6 +34,7 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => { }; class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider { + public constructor( private readonly client: ITypeScriptServiceClient, private cachedResponse: CachedResponse<Proto.NavTreeResponse>, @@ -45,34 +48,32 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider const args: Proto.FileRequestArgs = { file }; const response = await this.cachedResponse.execute(document, () => this.client.execute('navtree', args, token)); - if (response.type !== 'response' || !response.body) { + if (response.type !== 'response' || !response.body?.childItems) { return undefined; } - let tree = response.body; - if (tree && tree.childItems) { - // The root represents the file. Ignore this when showing in the UI - const result: vscode.DocumentSymbol[] = []; - tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(document.uri, result, item)); - return result; + // The root represents the file. Ignore this when showing in the UI + const result: vscode.DocumentSymbol[] = []; + for (const item of response.body.childItems) { + TypeScriptDocumentSymbolProvider.convertNavTree(document.uri, result, item); } - - return undefined; + return result; } - private static convertNavTree(resource: vscode.Uri, bucket: vscode.DocumentSymbol[], item: Proto.NavigationTree): boolean { + private static convertNavTree( + resource: vscode.Uri, + output: vscode.DocumentSymbol[], + item: Proto.NavigationTree, + ): boolean { let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item); + if (!shouldInclude && !item.childItems?.length) { + return false; + } const children = new Set(item.childItems || []); for (const span of item.spans) { const range = typeConverters.Range.fromTextSpan(span); - const selectionRange = item.nameSpan ? typeConverters.Range.fromTextSpan(item.nameSpan) : range; - const symbolInfo = new vscode.DocumentSymbol( - item.text, - '', - getSymbolKind(item.kind), - range, - range.contains(selectionRange) ? selectionRange : range); + const symbolInfo = TypeScriptDocumentSymbolProvider.convertSymbol(item, range); for (const child of children) { if (child.spans.some(span => !!range.intersection(typeConverters.Range.fromTextSpan(span)))) { @@ -83,13 +84,38 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } if (shouldInclude) { - bucket.push(symbolInfo); + output.push(symbolInfo); } } return shouldInclude; } + private static convertSymbol(item: Proto.NavigationTree, range: vscode.Range): vscode.DocumentSymbol { + const selectionRange = item.nameSpan ? typeConverters.Range.fromTextSpan(item.nameSpan) : range; + let label = item.text; + + switch (item.kind) { + case PConst.Kind.memberGetAccessor: label = `(get) ${label}`; break; + case PConst.Kind.memberSetAccessor: label = `(set) ${label}`; break; + } + + const symbolInfo = new vscode.DocumentSymbol( + label, + '', + getSymbolKind(item.kind), + range, + range.contains(selectionRange) ? selectionRange : range); + + + const kindModifiers = parseKindModifier(item.kindModifiers); + if (kindModifiers.has(PConst.KindModifiers.depreacted)) { + symbolInfo.tags = [vscode.SymbolTag.Deprecated]; + } + + return symbolInfo; + } + private static shouldInclueEntry(item: Proto.NavigationTree | Proto.NavigationBarItem): boolean { if (item.kind === PConst.Kind.alias) { return false; @@ -98,12 +124,11 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } } - export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { - return vscode.languages.registerDocumentSymbolProvider(selector, + return vscode.languages.registerDocumentSymbolProvider(selector.syntax, new TypeScriptDocumentSymbolProvider(client, cachedResponse), { label: 'TypeScript' }); } diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts similarity index 76% rename from extensions/typescript-language-features/src/features/fileConfigurationManager.ts rename to extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index ff9245b4a5e..03ebff6dd21 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -4,22 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { isTypeScriptDocument } from '../utils/languageModeIds'; -import { ResourceMap } from '../utils/resourceMap'; import { Disposable } from '../utils/dispose'; +import * as fileSchemes from '../utils/fileSchemes'; +import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { equals } from '../utils/objects'; +import { ResourceMap } from '../utils/resourceMap'; - -function objsAreEqual<T>(a: T, b: T): boolean { - let keys = Object.keys(a); - for (const key of keys) { - if ((a as any)[key] !== (b as any)[key]) { - return false; - } +namespace Experimental { + // https://github.com/microsoft/TypeScript/pull/37871/ + export interface UserPreferences extends Proto.UserPreferences { + readonly provideRefactorNotApplicableReason?: boolean; + } + + // https://github.com/microsoft/TypeScript/issues/41208 + export interface FormatCodeSettings extends Proto.FormatCodeSettings { + readonly insertSpaceAfterOpeningAndBeforeClosingEmptyBraces?: boolean; } - return true; } interface FileConfiguration { @@ -28,19 +31,18 @@ interface FileConfiguration { } function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): boolean { - return ( - objsAreEqual(a.formatOptions, b.formatOptions) - && objsAreEqual(a.preferences, b.preferences) - ); + return equals(a, b); } export default class FileConfigurationManager extends Disposable { - private readonly formatOptions = new ResourceMap<Promise<FileConfiguration | undefined>>(); + private readonly formatOptions: ResourceMap<Promise<FileConfiguration | undefined>>; public constructor( - private readonly client: ITypeScriptServiceClient + private readonly client: ITypeScriptServiceClient, + onCaseInsenitiveFileSystem: boolean ) { super(); + this.formatOptions = new ResourceMap(undefined, { onCaseInsenitiveFileSystem }); vscode.workspace.onDidCloseTextDocument(textDocument => { // When a document gets closed delete the cached formatting options. // This is necessary since the tsserver now closed a project when its @@ -139,14 +141,12 @@ export default class FileConfigurationManager extends Disposable { private getFormatOptions( document: vscode.TextDocument, options: vscode.FormattingOptions - ): Proto.FormatCodeSettings { + ): Experimental.FormatCodeSettings { const config = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.format' : 'javascript.format', document.uri); - // `semicolons` added to `Proto.FormatCodeSettings` in TypeScript 3.7: - // remove intersection type after upgrading TypeScript. - const settings: Proto.FormatCodeSettings & { semicolons?: string } = { + return { tabSize: options.tabSize, indentSize: options.tabSize, convertTabsToSpaces: options.insertSpaces, @@ -162,6 +162,7 @@ export default class FileConfigurationManager extends Disposable { insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis'), insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets'), insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces'), + insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingEmptyBraces'), insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces'), insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces'), insertSpaceAfterTypeAssertion: config.get<boolean>('insertSpaceAfterTypeAssertion'), @@ -169,8 +170,6 @@ export default class FileConfigurationManager extends Disposable { placeOpenBraceOnNewLineForControlBlocks: config.get<boolean>('placeOpenBraceOnNewLineForControlBlocks'), semicolons: config.get<Proto.SemicolonPreference>('semicolons'), }; - - return settings; } private getPreferences(document: vscode.TextDocument): Proto.UserPreferences { @@ -179,16 +178,25 @@ export default class FileConfigurationManager extends Disposable { } const config = vscode.workspace.getConfiguration( + isTypeScriptDocument(document) ? 'typescript' : 'javascript', + document.uri); + + const preferencesConfig = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences', document.uri); - return { - quotePreference: this.getQuoteStylePreference(config), - importModuleSpecifierPreference: getImportModuleSpecifierPreference(config), - allowTextChangesInNewFiles: document.uri.scheme === 'file', - providePrefixAndSuffixTextForRename: config.get<boolean>('renameShorthandProperties', true), + const preferences: Experimental.UserPreferences = { + quotePreference: this.getQuoteStylePreference(preferencesConfig), + importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferencesConfig), + importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig), + allowTextChangesInNewFiles: document.uri.scheme === fileSchemes.file, + providePrefixAndSuffixTextForRename: preferencesConfig.get<boolean>('renameShorthandProperties', true) === false ? false : preferencesConfig.get<boolean>('useAliasesForRenames', true), allowRenameOfImportPath: true, + includeAutomaticOptionalChainCompletions: config.get<boolean>('suggest.includeAutomaticOptionalChainCompletions', true), + provideRefactorNotApplicableReason: true, }; + + return preferences; } private getQuoteStylePreference(config: vscode.WorkspaceConfiguration) { @@ -207,3 +215,12 @@ function getImportModuleSpecifierPreference(config: vscode.WorkspaceConfiguratio default: return undefined; } } + +function getImportModuleSpecifierEndingPreference(config: vscode.WorkspaceConfiguration) { + switch (config.get<string>('importModuleSpecifierEnding')) { + case 'minimal': return 'minimal'; + case 'index': return 'index'; + case 'js': return 'js'; + default: return 'auto'; + } +} diff --git a/extensions/typescript-language-features/src/languageFeatures/fixAll.ts b/extensions/typescript-language-features/src/languageFeatures/fixAll.ts new file mode 100644 index 00000000000..426a0e4636b --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/fixAll.ts @@ -0,0 +1,265 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import * as errorCodes from '../utils/errorCodes'; +import * as fixNames from '../utils/fixNames'; +import * as typeConverters from '../utils/typeConverters'; +import { DiagnosticsManager } from './diagnostics'; +import FileConfigurationManager from './fileConfigurationManager'; + +const localize = nls.loadMessageBundle(); + +interface AutoFix { + readonly codes: Set<number>; + readonly fixName: string; +} + +async function buildIndividualFixes( + fixes: readonly AutoFix[], + edit: vscode.WorkspaceEdit, + client: ITypeScriptServiceClient, + file: string, + diagnostics: readonly vscode.Diagnostic[], + token: vscode.CancellationToken, +): Promise<void> { + for (const diagnostic of diagnostics) { + for (const { codes, fixName } of fixes) { + if (token.isCancellationRequested) { + return; + } + + if (!codes.has(diagnostic.code as number)) { + continue; + } + + const args: Proto.CodeFixRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), + errorCodes: [+(diagnostic.code!)] + }; + + const response = await client.execute('getCodeFixes', args, token); + if (response.type !== 'response') { + continue; + } + + const fix = response.body?.find(fix => fix.fixName === fixName); + if (fix) { + typeConverters.WorkspaceEdit.withFileCodeEdits(edit, client, fix.changes); + break; + } + } + } +} + +async function buildCombinedFix( + fixes: readonly AutoFix[], + edit: vscode.WorkspaceEdit, + client: ITypeScriptServiceClient, + file: string, + diagnostics: readonly vscode.Diagnostic[], + token: vscode.CancellationToken, +): Promise<void> { + for (const diagnostic of diagnostics) { + for (const { codes, fixName } of fixes) { + if (token.isCancellationRequested) { + return; + } + + if (!codes.has(diagnostic.code as number)) { + continue; + } + + const args: Proto.CodeFixRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), + errorCodes: [+(diagnostic.code!)] + }; + + const response = await client.execute('getCodeFixes', args, token); + if (response.type !== 'response' || !response.body?.length) { + continue; + } + + const fix = response.body?.find(fix => fix.fixName === fixName); + if (!fix) { + continue; + } + + if (!fix.fixId) { + typeConverters.WorkspaceEdit.withFileCodeEdits(edit, client, fix.changes); + return; + } + + const combinedArgs: Proto.GetCombinedCodeFixRequestArgs = { + scope: { + type: 'file', + args: { file } + }, + fixId: fix.fixId, + }; + + const combinedResponse = await client.execute('getCombinedCodeFix', combinedArgs, token); + if (combinedResponse.type !== 'response' || !combinedResponse.body) { + return; + } + + typeConverters.WorkspaceEdit.withFileCodeEdits(edit, client, combinedResponse.body.changes); + return; + } + } +} + +// #region Source Actions + +abstract class SourceAction extends vscode.CodeAction { + abstract build( + client: ITypeScriptServiceClient, + file: string, + diagnostics: readonly vscode.Diagnostic[], + token: vscode.CancellationToken, + ): Promise<void>; +} + +class SourceFixAll extends SourceAction { + + static readonly kind = vscode.CodeActionKind.SourceFixAll.append('ts'); + + constructor() { + super(localize('autoFix.label', 'Fix All'), SourceFixAll.kind); + } + + async build(client: ITypeScriptServiceClient, file: string, diagnostics: readonly vscode.Diagnostic[], token: vscode.CancellationToken): Promise<void> { + this.edit = new vscode.WorkspaceEdit(); + + await buildIndividualFixes([ + { codes: errorCodes.incorrectlyImplementsInterface, fixName: fixNames.classIncorrectlyImplementsInterface }, + { codes: errorCodes.asyncOnlyAllowedInAsyncFunctions, fixName: fixNames.awaitInSyncFunction }, + ], this.edit, client, file, diagnostics, token); + + await buildCombinedFix([ + { codes: errorCodes.unreachableCode, fixName: fixNames.unreachableCode } + ], this.edit, client, file, diagnostics, token); + } +} + +class SourceRemoveUnused extends SourceAction { + + static readonly kind = vscode.CodeActionKind.Source.append('removeUnused').append('ts'); + + constructor() { + super(localize('autoFix.unused.label', 'Remove all unused code'), SourceRemoveUnused.kind); + } + + async build(client: ITypeScriptServiceClient, file: string, diagnostics: readonly vscode.Diagnostic[], token: vscode.CancellationToken): Promise<void> { + this.edit = new vscode.WorkspaceEdit(); + await buildCombinedFix([ + { codes: errorCodes.variableDeclaredButNeverUsed, fixName: fixNames.unusedIdentifier }, + ], this.edit, client, file, diagnostics, token); + } +} + +class SourceAddMissingImports extends SourceAction { + + static readonly kind = vscode.CodeActionKind.Source.append('addMissingImports').append('ts'); + + constructor() { + super(localize('autoFix.missingImports.label', 'Add all missing imports'), SourceAddMissingImports.kind); + } + + async build(client: ITypeScriptServiceClient, file: string, diagnostics: readonly vscode.Diagnostic[], token: vscode.CancellationToken): Promise<void> { + this.edit = new vscode.WorkspaceEdit(); + await buildCombinedFix([ + { codes: errorCodes.cannotFindName, fixName: fixNames.fixImport } + ], + this.edit, client, file, diagnostics, token); + } +} + +//#endregion + +class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { + + private static kindProviders = [ + SourceFixAll, + SourceRemoveUnused, + SourceAddMissingImports, + ]; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager, + private readonly diagnosticsManager: DiagnosticsManager, + ) { } + + public get metadata(): vscode.CodeActionProviderMetadata { + return { + providedCodeActionKinds: TypeScriptAutoFixProvider.kindProviders.map(x => x.kind), + }; + } + + public async provideCodeActions( + document: vscode.TextDocument, + _range: vscode.Range, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): Promise<vscode.CodeAction[] | undefined> { + if (!context.only || !vscode.CodeActionKind.Source.intersects(context.only)) { + return undefined; + } + + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } + + const actions = this.getFixAllActions(context.only); + if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { + return actions; + } + + const diagnostics = this.diagnosticsManager.getDiagnostics(document.uri); + if (!diagnostics.length) { + // Actions are a no-op in this case but we still want to return them + return actions; + } + + await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + + if (token.isCancellationRequested) { + return undefined; + } + + await Promise.all(actions.map(action => action.build(this.client, file, diagnostics, token))); + + return actions; + } + + private getFixAllActions(only: vscode.CodeActionKind): SourceAction[] { + return TypeScriptAutoFixProvider.kindProviders + .filter(provider => only.intersects(provider.kind)) + .map(provider => new provider()); + } +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, + diagnosticsManager: DiagnosticsManager, +) { + return conditionalRegistration([ + requireMinVersion(client, API.v300), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + const provider = new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager); + return vscode.languages.registerCodeActionsProvider(selector.semantic, provider, provider.metadata); + }); +} diff --git a/extensions/typescript-language-features/src/features/folding.ts b/extensions/typescript-language-features/src/languageFeatures/folding.ts similarity index 86% rename from extensions/typescript-language-features/src/features/folding.ts rename to extensions/typescript-language-features/src/languageFeatures/folding.ts index 079db3c66cb..5b18decb440 100644 --- a/extensions/typescript-language-features/src/features/folding.ts +++ b/extensions/typescript-language-features/src/languageFeatures/folding.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { coalesce } from '../utils/arrays'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { @@ -73,11 +74,13 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new VersionDependentRegistration(client, TypeScriptFoldingProvider.minVersion, () => { - return vscode.languages.registerFoldingRangeProvider(selector, + return conditionalRegistration([ + requireMinVersion(client, TypeScriptFoldingProvider.minVersion), + ], () => { + return vscode.languages.registerFoldingRangeProvider(selector.syntax, new TypeScriptFoldingProvider(client)); }); } diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/languageFeatures/formatting.ts similarity index 88% rename from extensions/typescript-language-features/src/features/formatting.ts rename to extensions/typescript-language-features/src/languageFeatures/formatting.ts index 3caedf86f82..cf19d59ab92 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/languageFeatures/formatting.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; @@ -65,7 +66,7 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit for (const edit of response.body) { const textEdit = typeConverters.TextEdit.fromCodeEdit(edit); const range = textEdit.range; - // Work around for https://github.com/Microsoft/TypeScript/issues/6700. + // Work around for https://github.com/microsoft/TypeScript/issues/6700. // Check if we have an edit at the beginning of the line which only removes white spaces and leaves // an empty line. Drop those edits if (range.start.character === 0 && range.start.line === range.end.line && textEdit.newText === '') { @@ -84,16 +85,18 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager ) { - return new ConfigurationDependentRegistration(modeId, 'format.enable', () => { + return conditionalRegistration([ + requireConfiguration(modeId, 'format.enable'), + ], () => { const formattingProvider = new TypeScriptFormattingProvider(client, fileConfigurationManager); return vscode.Disposable.from( - vscode.languages.registerOnTypeFormattingEditProvider(selector, formattingProvider, ';', '}', '\n'), - vscode.languages.registerDocumentRangeFormattingEditProvider(selector, formattingProvider), + vscode.languages.registerOnTypeFormattingEditProvider(selector.syntax, formattingProvider, ';', '}', '\n'), + vscode.languages.registerDocumentRangeFormattingEditProvider(selector.syntax, formattingProvider), ); }); } diff --git a/extensions/typescript-language-features/src/languageFeatures/hover.ts b/extensions/typescript-language-features/src/languageFeatures/hover.ts new file mode 100644 index 00000000000..236ef694967 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/hover.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import type * as Proto from '../protocol'; +import { localize } from '../tsServer/versionProvider'; +import { ClientCapability, ITypeScriptServiceClient, ServerType } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import { markdownDocumentation } from '../utils/previewer'; +import * as typeConverters from '../utils/typeConverters'; + + +class TypeScriptHoverProvider implements vscode.HoverProvider { + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async provideHover( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): Promise<vscode.Hover | undefined> { + const filepath = this.client.toOpenedFilePath(document); + if (!filepath) { + return undefined; + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + const response = await this.client.interruptGetErr(() => this.client.execute('quickinfo', args, token)); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + return new vscode.Hover( + this.getContents(document.uri, response.body, response._serverType), + typeConverters.Range.fromTextSpan(response.body)); + } + + private getContents( + resource: vscode.Uri, + data: Proto.QuickInfoResponseBody, + source: ServerType | undefined, + ) { + const parts: vscode.MarkedString[] = []; + + if (data.displayString) { + const displayParts: string[] = []; + + if (source === ServerType.Syntax && this.client.hasCapabilityForResource(resource, ClientCapability.Semantic)) { + displayParts.push( + localize({ + key: 'loadingPrefix', + comment: ['Prefix displayed for hover entries while the server is still loading'] + }, "(loading...)")); + } + + displayParts.push(data.displayString); + + parts.push({ language: 'typescript', value: displayParts.join(' ') }); + } + parts.push(markdownDocumentation(data.documentation, data.tags)); + return parts; + } +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient +): vscode.Disposable { + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerHoverProvider(selector.syntax, + new TypeScriptHoverProvider(client)); + }); +} diff --git a/extensions/typescript-language-features/src/features/implementations.ts b/extensions/typescript-language-features/src/languageFeatures/implementations.ts similarity index 63% rename from extensions/typescript-language-features/src/features/implementations.ts rename to extensions/typescript-language-features/src/languageFeatures/implementations.ts index c7cdeeb755f..bf3ddfee414 100644 --- a/extensions/typescript-language-features/src/features/implementations.ts +++ b/extensions/typescript-language-features/src/languageFeatures/implementations.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import DefinitionProviderBase from './definitionProviderBase'; class TypeScriptImplementationProvider extends DefinitionProviderBase implements vscode.ImplementationProvider { @@ -14,9 +16,13 @@ class TypeScriptImplementationProvider extends DefinitionProviderBase implements } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerImplementationProvider(selector, - new TypeScriptImplementationProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerImplementationProvider(selector.semantic, + new TypeScriptImplementationProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/jsDocCompletions.ts b/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts similarity index 91% rename from extensions/typescript-language-features/src/features/jsDocCompletions.ts rename to extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts index 03feee6a334..6e091af1692 100644 --- a/extensions/typescript-language-features/src/features/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; @@ -28,7 +29,7 @@ class JsDocCompletionItem extends vscode.CompletionItem { const suffix = line.slice(position.character).match(/^\s*\**\//); const start = position.translate(0, prefix ? -prefix[0].length : 0); const range = new vscode.Range(start, position.translate(0, suffix ? suffix[0].length : 0)); - this.range = <any>{ inserting: range, replacing: range }; + this.range = { inserting: range, replacing: range }; } } @@ -110,12 +111,14 @@ export function templateToSnippet(template: string): vscode.SnippetString { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new ConfigurationDependentRegistration(modeId, 'suggest.completeJSDocs', () => { - return vscode.languages.registerCompletionItemProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'suggest.completeJSDocs') + ], () => { + return vscode.languages.registerCompletionItemProvider(selector.syntax, new JsDocCompletionProvider(client), '*'); }); diff --git a/extensions/typescript-language-features/src/features/languageConfiguration.ts b/extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts similarity index 94% rename from extensions/typescript-language-features/src/features/languageConfiguration.ts rename to extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts index 59fefa1007b..fc81cb57fbe 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts @@ -5,7 +5,7 @@ /* -------------------------------------------------------------------------------------------- * Includes code from typescript-sublime-plugin project, obtained from - * https://github.com/Microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences + * https://github.com/microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; @@ -17,7 +17,7 @@ const jsTsLanguageConfiguration: vscode.LanguageConfiguration = { decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]].*$/, increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/ }, - wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, onEnterRules: [ { // e.g. /** | */ @@ -31,7 +31,7 @@ const jsTsLanguageConfiguration: vscode.LanguageConfiguration = { }, { // e.g. * ...| beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/, - oneLineAboveText: /^(\s*(\/\*\*|\*)).*/, + oneLineAboveText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/, action: { indentAction: vscode.IndentAction.None, appendText: '* ' }, }, { // e.g. */| diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/languageFeatures/organizeImports.ts similarity index 83% rename from extensions/typescript-language-features/src/features/organizeImports.ts rename to extensions/typescript-language-features/src/languageFeatures/organizeImports.ts index 2e5871d1835..218cda4103f 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/languageFeatures/organizeImports.ts @@ -5,15 +5,16 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { nulToken } from '../utils/cancellation'; +import { Command, CommandManager } from '../commands/commandManager'; +import { conditionalRegistration, requireMinVersion, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import { TelemetryReporter } from '../utils/telemetry'; import * as typeconverts from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; -import { TelemetryReporter } from '../utils/telemetry'; -import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -99,15 +100,18 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, commandManager: CommandManager, fileConfigurationManager: FileConfigurationManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, OrganizeImportsCodeActionProvider.minVersion, () => { + return conditionalRegistration([ + requireMinVersion(client, OrganizeImportsCodeActionProvider.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { const organizeImportsProvider = new OrganizeImportsCodeActionProvider(client, commandManager, fileConfigurationManager, telemetryReporter); - return vscode.languages.registerCodeActionsProvider(selector, + return vscode.languages.registerCodeActionsProvider(selector.semantic, organizeImportsProvider, organizeImportsProvider.metadata); }); diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts similarity index 71% rename from extensions/typescript-language-features/src/features/quickFix.ts rename to extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 879abf07f8b..0965a574721 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -5,13 +5,17 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { Command, CommandManager } from '../commands/commandManager'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'; -import { Command, CommandManager } from '../utils/commandManager'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import * as fixNames from '../utils/fixNames'; import { memoize } from '../utils/memoize'; +import { equals } from '../utils/objects'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import { DiagnosticsManager } from './diagnostics'; @@ -126,19 +130,45 @@ class DiagnosticsSet { } } -class CodeActionSet { - private readonly _actions = new Set<vscode.CodeAction>(); - private readonly _fixAllActions = new Map<{}, vscode.CodeAction>(); +class VsCodeCodeAction extends vscode.CodeAction { + constructor( + public readonly tsAction: Proto.CodeFixAction, + title: string, + kind: vscode.CodeActionKind, + public readonly isFixAll: boolean, + ) { + super(title, kind); + } +} - public get values(): Iterable<vscode.CodeAction> { +class CodeActionSet { + private readonly _actions = new Set<VsCodeCodeAction>(); + private readonly _fixAllActions = new Map<{}, VsCodeCodeAction>(); + + public get values(): Iterable<VsCodeCodeAction> { return this._actions; } - public addAction(action: vscode.CodeAction) { + public addAction(action: VsCodeCodeAction) { + for (const existing of this._actions) { + if (action.tsAction.fixName === existing.tsAction.fixName && equals(action.edit, existing.edit)) { + this._actions.delete(existing); + } + } + this._actions.add(action); + + if (action.tsAction.fixId) { + // If we have an existing fix all action, then make sure it follows this action + const existingFixAll = this._fixAllActions.get(action.tsAction.fixId); + if (existingFixAll) { + this._actions.delete(existingFixAll); + this._actions.add(existingFixAll); + } + } } - public addFixAllAction(fixId: {}, action: vscode.CodeAction) { + public addFixAllAction(fixId: {}, action: VsCodeCodeAction) { const existing = this._fixAllActions.get(fixId); if (existing) { // reinsert action at back of actions list @@ -219,7 +249,12 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { for (const diagnostic of fixableDiagnostics.values) { await this.getFixesForDiagnostic(document, file, diagnostic, results, token); } - return Array.from(results.values); + + const allActions = Array.from(results.values); + for (const action of allActions) { + action.isPreferred = isPreferredFix(action, allActions); + } + return allActions; } private async getFixesForDiagnostic( @@ -259,8 +294,8 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { private getSingleFixForTsCodeAction( diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction - ): vscode.CodeAction { - const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix); + ): VsCodeCodeAction { + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix, false); codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; codeAction.command = { @@ -268,7 +303,6 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { arguments: [tsAction], title: '' }; - codeAction.isPreferred = isPreferredFix(tsAction); return codeAction; } @@ -294,9 +328,10 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return results; } - const action = new vscode.CodeAction( + const action = new VsCodeCodeAction( + tsAction, tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), - vscode.CodeActionKind.QuickFix); + vscode.CodeActionKind.QuickFix, true); action.diagnostics = [diagnostic]; action.command = { command: ApplyFixAllCodeAction.ID, @@ -316,32 +351,71 @@ const fixAllErrorCodes = new Map<number, number>([ [2345, 2339], ]); - -const preferredFixes = new Set([ - 'annotateWithTypeFromJSDoc', - 'constructorForDerivedNeedSuperCall', - 'extendsInterfaceBecomesImplements', - 'fixAwaitInSyncFunction', - 'fixClassIncorrectlyImplementsInterface', - 'fixUnreachableCode', - 'forgottenThisPropertyAccess', - 'spelling', - 'unusedIdentifier', - 'addMissingAwait', +const preferredFixes = new Map<string, { readonly value: number, readonly thereCanOnlyBeOne?: boolean }>([ + [fixNames.annotateWithTypeFromJSDoc, { value: 1 }], + [fixNames.constructorForDerivedNeedSuperCall, { value: 1 }], + [fixNames.extendsInterfaceBecomesImplements, { value: 1 }], + [fixNames.awaitInSyncFunction, { value: 1 }], + [fixNames.classIncorrectlyImplementsInterface, { value: 3 }], + [fixNames.classDoesntImplementInheritedAbstractMember, { value: 3 }], + [fixNames.unreachableCode, { value: 1 }], + [fixNames.unusedIdentifier, { value: 1 }], + [fixNames.forgottenThisPropertyAccess, { value: 1 }], + [fixNames.spelling, { value: 2 }], + [fixNames.addMissingAwait, { value: 1 }], + [fixNames.fixImport, { value: 0, thereCanOnlyBeOne: true }], ]); -function isPreferredFix(tsAction: Proto.CodeFixAction): boolean { - return preferredFixes.has(tsAction.fixName); + +function isPreferredFix( + action: VsCodeCodeAction, + allActions: readonly VsCodeCodeAction[] +): boolean { + if (action.isFixAll) { + return false; + } + + const fixPriority = preferredFixes.get(action.tsAction.fixName); + if (!fixPriority) { + return false; + } + + return allActions.every(otherAction => { + if (otherAction === action) { + return true; + } + + if (otherAction.isFixAll) { + return true; + } + + const otherFixPriority = preferredFixes.get(otherAction.tsAction.fixName); + if (!otherFixPriority || otherFixPriority.value < fixPriority.value) { + return true; + } else if (otherFixPriority.value > fixPriority.value) { + return false; + } + + if (fixPriority.thereCanOnlyBeOne && action.tsAction.fixName === otherAction.tsAction.fixName) { + return false; + } + + return true; + }); } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager, commandManager: CommandManager, diagnosticsManager: DiagnosticsManager, telemetryReporter: TelemetryReporter ) { - return vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), - TypeScriptQuickFixProvider.metadata); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeActionsProvider(selector.semantic, + new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), + TypeScriptQuickFixProvider.metadata); + }); } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts similarity index 53% rename from extensions/typescript-language-features/src/features/refactor.ts rename to extensions/typescript-language-features/src/languageFeatures/refactor.ts index c866665f041..ab0186c8f01 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -5,40 +5,40 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { Command, CommandManager } from '../commands/commandManager'; +import { LearnMoreAboutRefactoringsCommand } from '../commands/learnMoreAboutRefactorings'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; -import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import * as fileSchemes from '../utils/fileSchemes'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import FormattingOptionsManager from './fileConfigurationManager'; -import * as fileSchemes from '../utils/fileSchemes'; const localize = nls.loadMessageBundle(); +namespace Experimental { + export interface RefactorActionInfo extends Proto.RefactorActionInfo { + readonly notApplicableReason?: string; + } +} -class ApplyRefactoringCommand implements Command { - public static readonly ID = '_typescript.applyRefactoring'; - public readonly id = ApplyRefactoringCommand.ID; +interface DidApplyRefactoringCommand_Args { + readonly codeAction: InlinedCodeAction +} + +class DidApplyRefactoringCommand implements Command { + public static readonly ID = '_typescript.didApplyRefactoring'; + public readonly id = DidApplyRefactoringCommand.ID; constructor( - private readonly client: ITypeScriptServiceClient, private readonly telemetryReporter: TelemetryReporter ) { } - public async execute( - document: vscode.TextDocument, - refactor: string, - action: string, - range: vscode.Range - ): Promise<boolean> { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return false; - } - + public async execute(args: DidApplyRefactoringCommand_Args): Promise<void> { /* __GDPR__ "refactor.execute" : { "action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, @@ -48,50 +48,29 @@ class ApplyRefactoringCommand implements Command { } */ this.telemetryReporter.logTelemetry('refactor.execute', { - action: action, + action: args.codeAction.action, }); - const args: Proto.GetEditsForRefactorRequestArgs = { - ...typeConverters.Range.toFileRangeRequestArgs(file, range), - refactor, - action, - }; - const response = await this.client.execute('getEditsForRefactor', args, nulToken); - if (response.type !== 'response' || !response.body) { - return false; - } - - if (!response.body.edits.length) { + if (!args.codeAction.edit?.size) { vscode.window.showErrorMessage(localize('refactoringFailed', "Could not apply refactoring")); - return false; + return; } - const workspaceEdit = await this.toWorkspaceEdit(response.body); - if (!(await vscode.workspace.applyEdit(workspaceEdit))) { - return false; - } - - const renameLocation = response.body.renameLocation; + const renameLocation = args.codeAction.renameLocation; if (renameLocation) { await vscode.commands.executeCommand('editor.action.rename', [ - document.uri, + args.codeAction.document.uri, typeConverters.Position.fromLocation(renameLocation) ]); } - return true; } +} - private async toWorkspaceEdit(body: Proto.RefactorEditInfo) { - const workspaceEdit = new vscode.WorkspaceEdit(); - for (const edit of body.edits) { - const resource = this.client.toResource(edit.fileName); - if (resource.scheme === fileSchemes.file) { - workspaceEdit.createFile(resource, { ignoreIfExists: true }); - } - } - typeConverters.WorkspaceEdit.withFileCodeEdits(workspaceEdit, this.client, body.edits); - return workspaceEdit; - } +interface SelectRefactorCommand_Args { + readonly action: vscode.CodeAction; + readonly document: vscode.TextDocument; + readonly info: Proto.ApplicableRefactorInfo; + readonly rangeOrSelection: vscode.Range | vscode.Selection; } class SelectRefactorCommand implements Command { @@ -100,26 +79,34 @@ class SelectRefactorCommand implements Command { constructor( private readonly client: ITypeScriptServiceClient, - private readonly doRefactoring: ApplyRefactoringCommand + private readonly didApplyCommand: DidApplyRefactoringCommand ) { } - public async execute( - document: vscode.TextDocument, - info: Proto.ApplicableRefactorInfo, - range: vscode.Range - ): Promise<boolean> { - const file = this.client.toOpenedFilePath(document); + public async execute(args: SelectRefactorCommand_Args): Promise<void> { + const file = this.client.toOpenedFilePath(args.document); if (!file) { - return false; + return; } - const selected = await vscode.window.showQuickPick(info.actions.map((action): vscode.QuickPickItem => ({ + + const selected = await vscode.window.showQuickPick(args.info.actions.map((action): vscode.QuickPickItem => ({ label: action.name, description: action.description, }))); if (!selected) { - return false; + return; } - return this.doRefactoring.execute(document, info.name, selected.label, range); + + const tsAction = new InlinedCodeAction(this.client, args.action.title, args.action.kind, args.document, args.info.name, selected.label, args.rangeOrSelection); + await tsAction.resolve(nulToken); + + if (tsAction.edit) { + if (!(await vscode.workspace.applyEdit(tsAction.edit))) { + vscode.window.showErrorMessage(localize('refactoringFailed', "Could not apply refactoring")); + return; + } + } + + await this.didApplyCommand.execute({ codeAction: tsAction }); } } @@ -191,7 +178,80 @@ const allKnownCodeActionKinds = [ Rewrite_Property_GenerateAccessors ]; -class TypeScriptRefactorProvider implements vscode.CodeActionProvider { +class InlinedCodeAction extends vscode.CodeAction { + constructor( + public readonly client: ITypeScriptServiceClient, + title: string, + kind: vscode.CodeActionKind | undefined, + public readonly document: vscode.TextDocument, + public readonly refactor: string, + public readonly action: string, + public readonly range: vscode.Range, + ) { + super(title, kind); + } + + // Filled in during resolve + public renameLocation?: Proto.Location; + + public async resolve(token: vscode.CancellationToken): Promise<undefined> { + const file = this.client.toOpenedFilePath(this.document); + if (!file) { + return; + } + + const args: Proto.GetEditsForRefactorRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, this.range), + refactor: this.refactor, + action: this.action, + }; + + const response = await this.client.execute('getEditsForRefactor', args, token); + if (response.type !== 'response' || !response.body) { + return; + } + + // Resolve + this.edit = InlinedCodeAction.getWorkspaceEditForRefactoring(this.client, response.body); + this.renameLocation = response.body.renameLocation; + + return; + } + + private static getWorkspaceEditForRefactoring( + client: ITypeScriptServiceClient, + body: Proto.RefactorEditInfo, + ): vscode.WorkspaceEdit { + const workspaceEdit = new vscode.WorkspaceEdit(); + for (const edit of body.edits) { + const resource = client.toResource(edit.fileName); + if (resource.scheme === fileSchemes.file) { + workspaceEdit.createFile(resource, { ignoreIfExists: true }); + } + } + typeConverters.WorkspaceEdit.withFileCodeEdits(workspaceEdit, client, body.edits); + return workspaceEdit; + } +} + +class SelectCodeAction extends vscode.CodeAction { + constructor( + info: Proto.ApplicableRefactorInfo, + document: vscode.TextDocument, + rangeOrSelection: vscode.Range | vscode.Selection + ) { + super(info.description, vscode.CodeActionKind.Refactor); + this.command = { + title: info.description, + command: SelectRefactorCommand.ID, + arguments: [<SelectRefactorCommand_Args>{ action: this, document, info, rangeOrSelection }] + }; + } +} + +type TsCodeAction = InlinedCodeAction | SelectCodeAction; + +class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeAction> { public static readonly minVersion = API.v240; constructor( @@ -200,8 +260,8 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { commandManager: CommandManager, telemetryReporter: TelemetryReporter ) { - const doRefactoringCommand = commandManager.register(new ApplyRefactoringCommand(this.client, telemetryReporter)); - commandManager.register(new SelectRefactorCommand(this.client, doRefactoringCommand)); + const didApplyRefactoringCommand = commandManager.register(new DidApplyRefactoringCommand(telemetryReporter)); + commandManager.register(new SelectRefactorCommand(this.client, didApplyRefactoringCommand)); } public static readonly metadata: vscode.CodeActionProviderMetadata = { @@ -209,6 +269,15 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { vscode.CodeActionKind.Refactor, ...allKnownCodeActionKinds.map(x => x.kind), ], + documentation: [ + { + kind: vscode.CodeActionKind.Refactor, + command: { + command: LearnMoreAboutRefactoringsCommand.id, + title: localize('refactor.documentation.title', "Learn more about JS/TS refactorings") + } + } + ] }; public async provideCodeActions( @@ -216,7 +285,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { rangeOrSelection: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken - ): Promise<vscode.CodeAction[] | undefined> { + ): Promise<TsCodeAction[] | undefined> { if (!this.shouldTrigger(rangeOrSelection, context)) { return undefined; } @@ -231,7 +300,10 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } this.formattingOptionsManager.ensureConfigurationForDocument(document, token); - const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); + const args: Proto.GetApplicableRefactorsRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection), + triggerReason: this.toTsTriggerReason(context), + }; return this.client.execute('getApplicableRefactors', args, token); }); if (response?.type !== 'response' || !response.body) { @@ -242,28 +314,39 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { if (!context.only) { return actions; } - return this.appendInvalidActions(actions); + return this.pruneInvalidActions(this.appendInvalidActions(actions), context.only, /* numberOfInvalid = */ 5); } + public async resolveCodeAction( + codeAction: TsCodeAction, + token: vscode.CancellationToken, + ): Promise<TsCodeAction> { + if (codeAction instanceof InlinedCodeAction) { + await codeAction.resolve(token); + } + return codeAction; + } + + private toTsTriggerReason(context: vscode.CodeActionContext): Proto.RefactorTriggerReason | undefined { + if (!context.only) { + return; + } + return 'invoked'; + } private convertApplicableRefactors( body: Proto.ApplicableRefactorInfo[], document: vscode.TextDocument, rangeOrSelection: vscode.Range | vscode.Selection - ) { - const actions: vscode.CodeAction[] = []; + ): TsCodeAction[] { + const actions: TsCodeAction[] = []; for (const info of body) { if (info.inlineable === false) { - const codeAction = new vscode.CodeAction(info.description, vscode.CodeActionKind.Refactor); - codeAction.command = { - title: info.description, - command: SelectRefactorCommand.ID, - arguments: [document, info, rangeOrSelection] - }; + const codeAction = new SelectCodeAction(info, document, rangeOrSelection); actions.push(codeAction); } else { for (const action of info.actions) { - actions.push(this.refactorActionToCodeAction(action, document, info, rangeOrSelection)); + actions.push(this.refactorActionToCodeAction(action, document, info, rangeOrSelection, info.actions)); } } } @@ -271,18 +354,26 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } private refactorActionToCodeAction( - action: Proto.RefactorActionInfo, + action: Experimental.RefactorActionInfo, document: vscode.TextDocument, info: Proto.ApplicableRefactorInfo, - rangeOrSelection: vscode.Range | vscode.Selection - ) { - const codeAction = new vscode.CodeAction(action.description, TypeScriptRefactorProvider.getKind(action)); - codeAction.command = { - title: action.description, - command: ApplyRefactoringCommand.ID, - arguments: [document, info.name, action.name, rangeOrSelection], - }; - codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action); + rangeOrSelection: vscode.Range | vscode.Selection, + allActions: readonly Proto.RefactorActionInfo[], + ): InlinedCodeAction { + const codeAction = new InlinedCodeAction(this.client, action.description, TypeScriptRefactorProvider.getKind(action), document, info.name, action.name, rangeOrSelection); + + // https://github.com/microsoft/TypeScript/pull/37871 + if (action.notApplicableReason) { + codeAction.disabled = { reason: action.notApplicableReason }; + } else { + codeAction.command = { + title: action.description, + command: DidApplyRefactoringCommand.ID, + arguments: [<DidApplyRefactoringCommand_Args>{ codeAction }], + }; + } + + codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action, allActions); return codeAction; } @@ -300,10 +391,26 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } private static isPreferred( - action: Proto.RefactorActionInfo + action: Proto.RefactorActionInfo, + allActions: readonly Proto.RefactorActionInfo[], ): boolean { if (Extract_Constant.matches(action)) { - return action.name.endsWith('scope_0'); + // Only mark the action with the lowest scope as preferred + const getScope = (name: string) => { + const scope = name.match(/scope_(\d)/)?.[1]; + return scope ? +scope : undefined; + }; + const scope = getScope(action.name); + if (typeof scope !== 'number') { + return false; + } + + return allActions + .filter(otherAtion => otherAtion !== action && Extract_Constant.matches(otherAtion)) + .every(otherAction => { + const otherScope = getScope(otherAction.name); + return typeof otherScope === 'number' ? scope < otherScope : true; + }); } if (Extract_Type.matches(action) || Extract_Interface.matches(action)) { return true; @@ -312,6 +419,11 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } private appendInvalidActions(actions: vscode.CodeAction[]): vscode.CodeAction[] { + if (this.client.apiVersion.gte(API.v400)) { + // Invalid actions come from TS server instead + return actions; + } + if (!actions.some(action => action.kind && Extract_Constant.kind.contains(action.kind))) { const disabledAction = new vscode.CodeAction( localize('extractConstant.disabled.title', "Extract to constant"), @@ -337,17 +449,53 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } return actions; } + + private pruneInvalidActions(actions: vscode.CodeAction[], only?: vscode.CodeActionKind, numberOfInvalid?: number): vscode.CodeAction[] { + if (this.client.apiVersion.lt(API.v400)) { + // Older TS version don't return extra actions + return actions; + } + + const availableActions: vscode.CodeAction[] = []; + const invalidCommonActions: vscode.CodeAction[] = []; + const invalidUncommonActions: vscode.CodeAction[] = []; + for (const action of actions) { + if (!action.disabled) { + availableActions.push(action); + continue; + } + + // These are the common refactors that we should always show if applicable. + if (action.kind && (Extract_Constant.kind.contains(action.kind) || Extract_Function.kind.contains(action.kind))) { + invalidCommonActions.push(action); + continue; + } + + // These are the remaining refactors that we can show if we haven't reached the max limit with just common refactors. + invalidUncommonActions.push(action); + } + + const prioritizedActions: vscode.CodeAction[] = []; + prioritizedActions.push(...invalidCommonActions); + prioritizedActions.push(...invalidUncommonActions); + const topNInvalid = prioritizedActions.filter(action => !only || (action.kind && only.contains(action.kind))).slice(0, numberOfInvalid); + availableActions.push(...topNInvalid); + return availableActions; + } } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, formattingOptionsManager: FormattingOptionsManager, commandManager: CommandManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, TypeScriptRefactorProvider.minVersion, () => { - return vscode.languages.registerCodeActionsProvider(selector, + return conditionalRegistration([ + requireMinVersion(client, TypeScriptRefactorProvider.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeActionsProvider(selector.semantic, new TypeScriptRefactorProvider(client, formattingOptionsManager, commandManager, telemetryReporter), TypeScriptRefactorProvider.metadata); }); diff --git a/extensions/typescript-language-features/src/features/references.ts b/extensions/typescript-language-features/src/languageFeatures/references.ts similarity index 74% rename from extensions/typescript-language-features/src/features/references.ts rename to extensions/typescript-language-features/src/languageFeatures/references.ts index d77ecc5b10b..1ee8d150429 100644 --- a/extensions/typescript-language-features/src/features/references.ts +++ b/extensions/typescript-language-features/src/languageFeatures/references.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptReferenceSupport implements vscode.ReferenceProvider { @@ -42,9 +44,13 @@ class TypeScriptReferenceSupport implements vscode.ReferenceProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient ) { - return vscode.languages.registerReferenceProvider(selector, - new TypeScriptReferenceSupport(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerReferenceProvider(selector.syntax, + new TypeScriptReferenceSupport(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/languageFeatures/rename.ts similarity index 75% rename from extensions/typescript-language-features/src/features/rename.ts rename to extensions/typescript-language-features/src/languageFeatures/rename.ts index aff2c508c53..e3e1b3c694e 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/languageFeatures/rename.ts @@ -6,9 +6,11 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; @@ -25,8 +27,12 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { position: vscode.Position, token: vscode.CancellationToken ): Promise<vscode.Range | null> { + if (this.client.apiVersion.lt(API.v310)) { + return null; + } + const response = await this.execRename(document, position, token); - if (!response || response.type !== 'response' || !response.body) { + if (response?.type !== 'response' || !response.body) { return null; } @@ -35,14 +41,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { return Promise.reject<vscode.Range>(renameInfo.localizedErrorMessage); } - if (this.client.apiVersion.gte(API.v310)) { - const triggerSpan = renameInfo.triggerSpan; - if (triggerSpan) { - return typeConverters.Range.fromTextSpan(triggerSpan); - } - } - - return null; + return typeConverters.Range.fromTextSpan(renameInfo.triggerSpan); } public async provideRenameEdits( @@ -61,17 +60,15 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { return Promise.reject<vscode.WorkspaceEdit>(renameInfo.localizedErrorMessage); } - - if (this.client.apiVersion.gte(API.v310)) { - if (renameInfo.fileToRename) { - const edits = await this.renameFile(renameInfo.fileToRename, newName, token); - if (edits) { - return edits; - } else { - return Promise.reject<vscode.WorkspaceEdit>(localize('fileRenameFail', "An error occurred while renaming file")); - } + if (renameInfo.fileToRename) { + const edits = await this.renameFile(renameInfo.fileToRename, newName, token); + if (edits) { + return edits; + } else { + return Promise.reject<vscode.WorkspaceEdit>(localize('fileRenameFail', "An error occurred while renaming file")); } } + return this.updateLocs(response.body.locs, newName); } @@ -104,11 +101,9 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { const edit = new vscode.WorkspaceEdit(); for (const spanGroup of locations) { const resource = this.client.toResource(spanGroup.file); - if (resource) { - for (const textSpan of spanGroup.locs as Proto.RenameTextSpan[]) { - edit.replace(resource, typeConverters.Range.fromTextSpan(textSpan), - (textSpan.prefixText || '') + newName + (textSpan.suffixText || '')); - } + for (const textSpan of spanGroup.locs) { + edit.replace(resource, typeConverters.Range.fromTextSpan(textSpan), + (textSpan.prefixText || '') + newName + (textSpan.suffixText || '')); } } return edit; @@ -144,10 +139,14 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager, ) { - return vscode.languages.registerRenameProvider(selector, - new TypeScriptRenameProvider(client, fileConfigurationManager)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerRenameProvider(selector.semantic, + new TypeScriptRenameProvider(client, fileConfigurationManager)); + }); } diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts similarity index 78% rename from extensions/typescript-language-features/src/features/semanticTokens.ts rename to extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts index 3c3aff06202..3c2dd7a6458 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts @@ -3,23 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService'; -import * as Proto from '../protocol'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import API from '../utils/api'; - // all constants are const -import { TokenType, TokenModifier, TokenEncodingConsts, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; +import { TokenEncodingConsts, TokenModifier, TokenType, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; + const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}.${VersionRequirement.minor}`); -export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { - return new VersionDependentRegistration(client, minTypeScriptVersion, () => { +// as we don't do deltas, for performance reasons, don't compute semantic tokens for documents above that limit +const CONTENT_LENGTH_LIMIT = 100000; + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, +) { + return conditionalRegistration([ + requireMinVersion(client, minTypeScriptVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { const provider = new DocumentSemanticTokensProvider(client); return vscode.Disposable.from( - vscode.languages.registerDocumentSemanticTokensProvider(selector, provider, provider.getLegend()), - vscode.languages.registerDocumentRangeSemanticTokensProvider(selector, provider, provider.getLegend()), + // register only as a range provider + vscode.languages.registerDocumentRangeSemanticTokensProvider(selector.semantic, provider, provider.getLegend()), ); }); } @@ -35,18 +45,12 @@ class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensPro } getLegend(): vscode.SemanticTokensLegend { - if (tokenTypes.length !== TokenType._) { - console.warn('typescript-vscode-sh-plugin has added new tokens types.'); - } - if (tokenModifiers.length !== TokenModifier._) { - console.warn('typescript-vscode-sh-plugin has added new tokens modifiers.'); - } return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers); } async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> { const file = this.client.toOpenedFilePath(document); - if (!file) { + if (!file || document.getText().length > CONTENT_LENGTH_LIMIT) { return null; } return this._provideSemanticTokens(document, { file, start: 0, length: document.getText().length }, token); @@ -54,9 +58,10 @@ class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensPro async provideDocumentRangeSemanticTokens(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.SemanticTokens | null> { const file = this.client.toOpenedFilePath(document); - if (!file) { + if (!file || (document.offsetAt(range.end) - document.offsetAt(range.start) > CONTENT_LENGTH_LIMIT)) { return null; } + const start = document.offsetAt(range.start); const length = document.offsetAt(range.end) - start; return this._provideSemanticTokens(document, { file, start, length }, token); @@ -68,7 +73,7 @@ class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensPro return null; } - const versionBeforeRequest = document.version; + let versionBeforeRequest = document.version; const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token); if (response.type !== 'response' || !response.body) { @@ -80,7 +85,15 @@ class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensPro if (versionBeforeRequest !== versionAfterRequest) { // cannot convert result's offsets to (line;col) values correctly // a new request will come in soon... - return null; + // + // here we cannot return null, because returning null would remove all semantic tokens. + // we must throw to indicate that the semantic tokens should not be removed. + // using the string busy here because it is not logged to error telemetry if the error text contains busy. + + // as the new request will come in right after our response, we first wait for the document activity to stop + await waitForDocumentChangesToEnd(document); + + throw new Error('busy'); } const tokenSpan = response.body.spans; @@ -115,10 +128,24 @@ class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensPro builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers); } } - return new vscode.SemanticTokens(builder.build()); + return builder.build(); } } +function waitForDocumentChangesToEnd(document: vscode.TextDocument) { + let version = document.version; + return new Promise<void>((s) => { + let iv = setInterval(_ => { + if (document.version === version) { + clearInterval(iv); + s(); + } + version = document.version; + }, 400); + }); +} + + // typescript-vscode-sh-plugin encodes type and modifiers in the classification: // TSClassification = (TokenType + 1) << 8 + TokenModifier @@ -142,6 +169,7 @@ tokenTypes[TokenType.typeParameter] = 'typeParameter'; tokenTypes[TokenType.type] = 'type'; tokenTypes[TokenType.parameter] = 'parameter'; tokenTypes[TokenType.variable] = 'variable'; +tokenTypes[TokenType.enumMember] = 'enumMember'; tokenTypes[TokenType.property] = 'property'; tokenTypes[TokenType.function] = 'function'; tokenTypes[TokenType.member] = 'member'; @@ -151,6 +179,16 @@ tokenModifiers[TokenModifier.async] = 'async'; tokenModifiers[TokenModifier.declaration] = 'declaration'; tokenModifiers[TokenModifier.readonly] = 'readonly'; tokenModifiers[TokenModifier.static] = 'static'; +tokenModifiers[TokenModifier.local] = 'local'; +tokenModifiers[TokenModifier.defaultLibrary] = 'defaultLibrary'; + +// make sure token types and modifiers are complete +if (tokenTypes.filter(t => !!t).length !== TokenType._) { + console.warn('typescript-vscode-sh-plugin has added new tokens types.'); +} +if (tokenModifiers.filter(t => !!t).length !== TokenModifier._) { + console.warn('typescript-vscode-sh-plugin has added new tokens modifiers.'); +} // mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available) const tokenTypeMap: number[] = []; diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts similarity index 70% rename from extensions/typescript-language-features/src/features/signatureHelp.ts rename to extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts index f7ad9ad7ee9..93901b9bb25 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as Previewer from '../utils/previewer'; import * as typeConverters from '../utils/typeConverters'; @@ -40,14 +42,27 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { const info = response.body; const result = new vscode.SignatureHelp(); - result.activeSignature = info.selectedItemIndex; - result.activeParameter = this.getActiveParmeter(info); result.signatures = info.items.map(signature => this.convertSignature(signature)); + result.activeSignature = this.getActiveSignature(context, info, result.signatures); + result.activeParameter = this.getActiveParameter(info); return result; } - private getActiveParmeter(info: Proto.SignatureHelpItems): number { + private getActiveSignature(context: vscode.SignatureHelpContext, info: Proto.SignatureHelpItems, signatures: readonly vscode.SignatureInformation[]): number { + // Try matching the previous active signature's label to keep it selected + const previouslyActiveSignature = context.activeSignatureHelp?.signatures[context.activeSignatureHelp.activeSignature]; + if (previouslyActiveSignature && context.isRetrigger) { + const existingIndex = signatures.findIndex(other => other.label === previouslyActiveSignature?.label); + if (existingIndex >= 0) { + return existingIndex; + } + } + + return info.selectedItemIndex; + } + + private getActiveParameter(info: Proto.SignatureHelpItems): number { const activeSignature = info.items[info.selectedItemIndex]; if (activeSignature && activeSignature.isVariadic) { return Math.min(info.argumentIndex, activeSignature.parameters.length - 1); @@ -107,12 +122,16 @@ function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.Signatur } } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerSignatureHelpProvider(selector, - new TypeScriptSignatureHelpProvider(client), { - triggerCharacters: TypeScriptSignatureHelpProvider.triggerCharacters, - retriggerCharacters: TypeScriptSignatureHelpProvider.retriggerCharacters + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerSignatureHelpProvider(selector.syntax, + new TypeScriptSignatureHelpProvider(client), { + triggerCharacters: TypeScriptSignatureHelpProvider.triggerCharacters, + retriggerCharacters: TypeScriptSignatureHelpProvider.retriggerCharacters + }); }); } diff --git a/extensions/typescript-language-features/src/features/smartSelect.ts b/extensions/typescript-language-features/src/languageFeatures/smartSelect.ts similarity index 80% rename from extensions/typescript-language-features/src/features/smartSelect.ts rename to extensions/typescript-language-features/src/languageFeatures/smartSelect.ts index 3d05d1309a2..f769347f15c 100644 --- a/extensions/typescript-language-features/src/features/smartSelect.ts +++ b/extensions/typescript-language-features/src/languageFeatures/smartSelect.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class SmartSelection implements vscode.SelectionRangeProvider { @@ -49,9 +50,12 @@ class SmartSelection implements vscode.SelectionRangeProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, SmartSelection.minVersion, () => - vscode.languages.registerSelectionRangeProvider(selector, new SmartSelection(client))); -} \ No newline at end of file + return conditionalRegistration([ + requireMinVersion(client, SmartSelection.minVersion), + ], () => { + return vscode.languages.registerSelectionRangeProvider(selector.syntax, new SmartSelection(client)); + }); +} diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts similarity index 74% rename from extensions/typescript-language-features/src/features/tagClosing.ts rename to extensions/typescript-language-features/src/languageFeatures/tagClosing.ts index 1123b175531..289ce73b293 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { ConditionalRegistration, ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion, requireConfiguration, Condition } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TagClosing extends Disposable { @@ -135,41 +136,29 @@ class TagClosing extends Disposable { } } -export class ActiveDocumentDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly selector: vscode.DocumentSelector, - register: () => vscode.Disposable, - ) { - super(); - this._registration = this._register(new ConditionalRegistration(register)); - vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); - vscode.workspace.onDidOpenTextDocument(this.onDidOpenDocument, this, this._disposables); - this.update(); - } - - private update() { - const editor = vscode.window.activeTextEditor; - const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); - this._registration.update(enabled); - } - - private onDidOpenDocument(openedDocument: vscode.TextDocument) { - if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document === openedDocument) { - // The active document's language may have changed - this.update(); - } - } +function requireActiveDocument( + selector: vscode.DocumentSelector +) { + return new Condition( + () => { + const editor = vscode.window.activeTextEditor; + return !!(editor && vscode.languages.match(selector, editor.document)); + }, + handler => { + return vscode.Disposable.from( + vscode.window.onDidChangeActiveTextEditor(handler), + vscode.workspace.onDidOpenTextDocument(handler)); + }); } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, TagClosing.minVersion, () => - new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () => - new ActiveDocumentDependentRegistration(selector, () => - new TagClosing(client)))); + return conditionalRegistration([ + requireMinVersion(client, TagClosing.minVersion), + requireConfiguration(modeId, 'autoClosingTags'), + requireActiveDocument(selector.syntax) + ], () => new TagClosing(client)); } diff --git a/extensions/typescript-language-features/src/features/tsconfig.ts b/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts similarity index 100% rename from extensions/typescript-language-features/src/features/tsconfig.ts rename to extensions/typescript-language-features/src/languageFeatures/tsconfig.ts diff --git a/extensions/typescript-language-features/src/features/typeDefinitions.ts b/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts similarity index 62% rename from extensions/typescript-language-features/src/features/typeDefinitions.ts rename to extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts index 6f63e44df0a..4aef4f41ed4 100644 --- a/extensions/typescript-language-features/src/features/typeDefinitions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import DefinitionProviderBase from './definitionProviderBase'; export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements vscode.TypeDefinitionProvider { @@ -14,9 +16,13 @@ export default class TypeScriptTypeDefinitionProvider extends DefinitionProvider } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerTypeDefinitionProvider(selector, - new TypeScriptTypeDefinitionProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerTypeDefinitionProvider(selector.syntax, + new TypeScriptTypeDefinitionProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts similarity index 94% rename from extensions/typescript-language-features/src/features/updatePathsOnRename.ts rename to extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts index 5f209f327e4..2c6354c01a8 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts @@ -6,12 +6,12 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import type * as Proto from '../protocol'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { Delayer } from '../utils/async'; import { nulToken } from '../utils/cancellation'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; import { doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription'; @@ -294,6 +294,10 @@ export function register( fileConfigurationManager: FileConfigurationManager, handles: (uri: vscode.Uri) => Promise<boolean>, ) { - return new VersionDependentRegistration(client, UpdateImportsOnFileRenameHandler.minVersion, () => - new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles)); + return conditionalRegistration([ + requireMinVersion(client, UpdateImportsOnFileRenameHandler.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles); + }); } diff --git a/extensions/typescript-language-features/src/features/workspaceSymbols.ts b/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts similarity index 54% rename from extensions/typescript-language-features/src/features/workspaceSymbols.ts rename to extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts index 857969be008..73a142440e5 100644 --- a/extensions/typescript-language-features/src/features/workspaceSymbols.ts +++ b/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts @@ -4,47 +4,59 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; +import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; import * as fileSchemes from '../utils/fileSchemes'; import { doesResourceLookLikeAJavaScriptFile, doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription'; import * as typeConverters from '../utils/typeConverters'; +import { parseKindModifier } from '../utils/modifiers'; function getSymbolKind(item: Proto.NavtoItem): vscode.SymbolKind { switch (item.kind) { - case 'method': return vscode.SymbolKind.Method; - case 'enum': return vscode.SymbolKind.Enum; - case 'function': return vscode.SymbolKind.Function; - case 'class': return vscode.SymbolKind.Class; - case 'interface': return vscode.SymbolKind.Interface; - case 'var': return vscode.SymbolKind.Variable; + case PConst.Kind.method: return vscode.SymbolKind.Method; + case PConst.Kind.enum: return vscode.SymbolKind.Enum; + case PConst.Kind.enumMember: return vscode.SymbolKind.EnumMember; + case PConst.Kind.function: return vscode.SymbolKind.Function; + case PConst.Kind.class: return vscode.SymbolKind.Class; + case PConst.Kind.interface: return vscode.SymbolKind.Interface; + case PConst.Kind.type: return vscode.SymbolKind.Class; + case PConst.Kind.memberVariable: return vscode.SymbolKind.Field; + case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Field; + case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Field; + case PConst.Kind.variable: return vscode.SymbolKind.Variable; default: return vscode.SymbolKind.Variable; } } class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { + public constructor( private readonly client: ITypeScriptServiceClient, - private readonly modeIds: string[] + private readonly modeIds: readonly string[], ) { } public async provideWorkspaceSymbols( search: string, token: vscode.CancellationToken ): Promise<vscode.SymbolInformation[]> { - const document = this.getDocument(); - if (!document) { - return []; - } + let file: string | undefined; + if (this.searchAllOpenProjects) { + file = undefined; + } else { + const document = this.getDocument(); + file = document ? await this.toOpenedFiledPath(document) : undefined; - const filepath = await this.toOpenedFiledPath(document); - if (!filepath) { - return []; + if (!file && this.client.apiVersion.lt(API.v390)) { + return []; + } } const args: Proto.NavtoRequestArgs = { - file: filepath, - searchValue: search + file, + searchValue: search, + maxResultCount: 256, }; const response = await this.client.execute('navto', args, token); @@ -52,16 +64,14 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide return []; } - const result: vscode.SymbolInformation[] = []; - for (const item of response.body) { - if (!item.containerName && item.kind === 'alias') { - continue; - } - const label = TypeScriptWorkspaceSymbolProvider.getLabel(item); - result.push(new vscode.SymbolInformation(label, getSymbolKind(item), item.containerName || '', - typeConverters.Location.fromTextSpan(this.client.toResource(item.file), item))); - } - return result; + return response.body + .filter(item => item.containerName || item.kind !== 'alias') + .map(item => this.toSymbolInformation(item)); + } + + private get searchAllOpenProjects() { + return this.client.apiVersion.gte(API.v390) + && vscode.workspace.getConfiguration('typescript').get('workspaceSymbols.scope', 'allOpenProjects') === 'allOpenProjects'; } private async toOpenedFiledPath(document: vscode.TextDocument) { @@ -79,6 +89,20 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide return this.client.toOpenedFilePath(document); } + private toSymbolInformation(item: Proto.NavtoItem) { + const label = TypeScriptWorkspaceSymbolProvider.getLabel(item); + const info = new vscode.SymbolInformation( + label, + getSymbolKind(item), + item.containerName || '', + typeConverters.Location.fromTextSpan(this.client.toResource(item.file), item)); + const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined; + if (kindModifiers?.has(PConst.KindModifiers.depreacted)) { + info.tags = [vscode.SymbolTag.Deprecated]; + } + return info; + } + private static getLabel(item: Proto.NavtoItem) { const label = item.name; if (item.kind === 'method' || item.kind === 'function') { @@ -111,7 +135,8 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide export function register( client: ITypeScriptServiceClient, - modeIds: string[], + modeIds: readonly string[], ) { - return vscode.languages.registerWorkspaceSymbolProvider(new TypeScriptWorkspaceSymbolProvider(client, modeIds)); + return vscode.languages.registerWorkspaceSymbolProvider( + new TypeScriptWorkspaceSymbolProvider(client, modeIds)); } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 644eed40441..92ab84be308 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -5,15 +5,15 @@ import { basename } from 'path'; import * as vscode from 'vscode'; +import { DiagnosticKind } from './languageFeatures/diagnostics'; +import FileConfigurationManager from './languageFeatures/fileConfigurationManager'; import { CachedResponse } from './tsServer/cachedResponse'; -import { DiagnosticKind } from './features/diagnostics'; -import FileConfigurationManager from './features/fileConfigurationManager'; import TypeScriptServiceClient from './typescriptServiceClient'; -import { CommandManager } from './utils/commandManager'; +import { CommandManager } from './commands/commandManager'; import { Disposable } from './utils/dispose'; +import { DocumentSelector } from './utils/documentSelector'; import * as fileSchemes from './utils/fileSchemes'; import { LanguageDescription } from './utils/languageDescription'; -import { memoize } from './utils/memoize'; import { TelemetryReporter } from './utils/telemetry'; import TypingsStatus from './utils/typingsStatus'; @@ -39,15 +39,17 @@ export default class LanguageProvider extends Disposable { client.onReady(() => this.registerProviders()); } - @memoize - private get documentSelector(): vscode.DocumentFilter[] { - const documentSelector = []; + private get documentSelector(): DocumentSelector { + const semantic: vscode.DocumentFilter[] = []; + const syntax: vscode.DocumentFilter[] = []; for (const language of this.description.modeIds) { - for (const scheme of fileSchemes.supportedSchemes) { - documentSelector.push({ language, scheme }); + syntax.push({ language }); + for (const scheme of fileSchemes.semanticSupportedSchemes) { + semantic.push({ language, scheme }); } } - return documentSelector; + + return { semantic, syntax }; } private async registerProviders(): Promise<void> { @@ -56,30 +58,30 @@ export default class LanguageProvider extends Disposable { const cachedResponse = new CachedResponse(); await Promise.all([ - import('./features/completions').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))), - import('./features/definitions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), - import('./features/documentSymbol').then(provider => this._register(provider.register(selector, this.client, cachedResponse))), - import('./features/folding').then(provider => this._register(provider.register(selector, this.client))), - import('./features/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))), - import('./features/hover').then(provider => this._register(provider.register(selector, this.client))), - import('./features/implementations').then(provider => this._register(provider.register(selector, this.client))), - import('./features/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), - import('./features/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client))), - import('./features/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), - import('./features/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))), - import('./features/fixAll').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager))), - import('./features/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))), - import('./features/references').then(provider => this._register(provider.register(selector, this.client))), - import('./features/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), - import('./features/rename').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), - import('./features/smartSelect').then(provider => this._register(provider.register(selector, this.client))), - import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), - import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), - import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), - import('./features/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/completions').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))), + import('./languageFeatures/definitions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/documentSymbol').then(provider => this._register(provider.register(selector, this.client, cachedResponse))), + import('./languageFeatures/folding').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))), + import('./languageFeatures/hover').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/implementations').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/codeLens/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), + import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client))), + import('./languageFeatures/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), + import('./languageFeatures/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))), + import('./languageFeatures/fixAll').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager))), + import('./languageFeatures/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))), + import('./languageFeatures/references').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/codeLens/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), + import('./languageFeatures/rename').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), + import('./languageFeatures/smartSelect').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), + import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), ]); } @@ -122,16 +124,22 @@ export default class LanguageProvider extends Disposable { this.client.bufferSyncSupport.requestAllDiagnostics(); } - public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: vscode.Uri, diagnostics: (vscode.Diagnostic & { reportUnnecessary: any })[]): void { + public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: vscode.Uri, diagnostics: (vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[]): void { const config = vscode.workspace.getConfiguration(this.id, file); const reportUnnecessary = config.get<boolean>('showUnused', true); + const reportDeprecated = config.get<boolean>('showDeprecated', true); this.client.diagnosticsManager.updateDiagnostics(file, this._diagnosticLanguage, diagnosticsKind, diagnostics.filter(diag => { + // Don't both reporting diagnostics we know will not be rendered if (!reportUnnecessary) { - diag.tags = undefined; if (diag.reportUnnecessary && diag.severity === vscode.DiagnosticSeverity.Hint) { return false; } } + if (!reportDeprecated) { + if (diag.reportDeprecated && diag.severity === vscode.DiagnosticSeverity.Hint) { + return false; + } + } return true; })); } diff --git a/extensions/typescript-language-features/src/lazyClientHost.ts b/extensions/typescript-language-features/src/lazyClientHost.ts new file mode 100644 index 00000000000..73dee25e6c3 --- /dev/null +++ b/extensions/typescript-language-features/src/lazyClientHost.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { CommandManager } from './commands/commandManager'; +import { OngoingRequestCancellerFactory } from './tsServer/cancellation'; +import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider'; +import { TsServerProcessFactory } from './tsServer/server'; +import { ITypeScriptVersionProvider } from './tsServer/versionProvider'; +import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; +import { flatten } from './utils/arrays'; +import * as fileSchemes from './utils/fileSchemes'; +import { standardLanguageDescriptions } from './utils/languageDescription'; +import { lazy, Lazy } from './utils/lazy'; +import ManagedFileContextManager from './utils/managedFileContext'; +import { PluginManager } from './utils/plugins'; + +export function createLazyClientHost( + context: vscode.ExtensionContext, + onCaseInsenitiveFileSystem: boolean, + services: { + pluginManager: PluginManager, + commandManager: CommandManager, + logDirectoryProvider: ILogDirectoryProvider, + cancellerFactory: OngoingRequestCancellerFactory, + versionProvider: ITypeScriptVersionProvider, + processFactory: TsServerProcessFactory, + }, + onCompletionAccepted: (item: vscode.CompletionItem) => void, +): Lazy<TypeScriptServiceClientHost> { + return lazy(() => { + const clientHost = new TypeScriptServiceClientHost( + standardLanguageDescriptions, + context.workspaceState, + onCaseInsenitiveFileSystem, + services, + onCompletionAccepted); + + context.subscriptions.push(clientHost); + + return clientHost; + }); +} + +export function lazilyActivateClient( + lazyClientHost: Lazy<TypeScriptServiceClientHost>, + pluginManager: PluginManager, +): vscode.Disposable { + const disposables: vscode.Disposable[] = []; + + const supportedLanguage = flatten([ + ...standardLanguageDescriptions.map(x => x.modeIds), + ...pluginManager.plugins.map(x => x.languages) + ]); + + let hasActivated = false; + const maybeActivate = (textDocument: vscode.TextDocument): boolean => { + if (!hasActivated && isSupportedDocument(supportedLanguage, textDocument)) { + hasActivated = true; + // Force activation + void lazyClientHost.value; + + disposables.push(new ManagedFileContextManager(resource => { + return lazyClientHost.value.serviceClient.toPath(resource); + })); + return true; + } + return false; + }; + + const didActivate = vscode.workspace.textDocuments.some(maybeActivate); + if (!didActivate) { + const openListener = vscode.workspace.onDidOpenTextDocument(doc => { + if (maybeActivate(doc)) { + openListener.dispose(); + } + }, undefined, disposables); + } + + return vscode.Disposable.from(...disposables); +} + +function isSupportedDocument( + supportedLanguage: readonly string[], + document: vscode.TextDocument +): boolean { + return supportedLanguage.indexOf(document.languageId) >= 0 + && !fileSchemes.disabledSchemes.has(document.uri.scheme); +} diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index a44c175f295..210e962c9aa 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -21,7 +21,7 @@ export class Kind { public static readonly let = 'let'; public static readonly localFunction = 'local function'; public static readonly localVariable = 'local var'; - public static readonly memberFunction = 'method'; + public static readonly method = 'method'; public static readonly memberGetAccessor = 'getter'; public static readonly memberSetAccessor = 'setter'; public static readonly memberVariable = 'property'; @@ -45,6 +45,7 @@ export class DiagnosticCategory { export class KindModifiers { public static readonly optional = 'optional'; + public static readonly depreacted = 'deprecated'; public static readonly color = 'color'; public static readonly dtsFile = '.d.ts'; @@ -72,3 +73,19 @@ export class DisplayPartKind { public static readonly punctuation = 'punctuation'; public static readonly text = 'text'; } + +export enum EventName { + syntaxDiag = 'syntaxDiag', + semanticDiag = 'semanticDiag', + suggestionDiag = 'suggestionDiag', + configFileDiag = 'configFileDiag', + telemetry = 'telemetry', + projectLanguageServiceState = 'projectLanguageServiceState', + projectsUpdatedInBackground = 'projectsUpdatedInBackground', + beginInstallTypes = 'beginInstallTypes', + endInstallTypes = 'endInstallTypes', + typesInstallerInitializationFailed = 'typesInstallerInitializationFailed', + surveyReady = 'surveyReady', + projectLoadingStart = 'projectLoadingStart', + projectLoadingFinish = 'projectLoadingFinish', +} diff --git a/extensions/typescript-language-features/src/protocol.d.ts b/extensions/typescript-language-features/src/protocol.d.ts index 62d6c4793ca..e81fe81f2db 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -1,61 +1,12 @@ import * as Proto from 'typescript/lib/protocol'; export = Proto; -declare module "typescript/lib/protocol" { - // TODO: Remove this hardcoded type once we update to TS 3.8+ that brings in the proper types +declare enum ServerType { + Syntax = 'syntax', + Semantic = 'semantic', +} +declare module 'typescript/lib/protocol' { interface Response { - performanceData?: { - updateGraphDurationMs?: number; - } - } - - const enum CommandTypes { - PrepareCallHierarchy = "prepareCallHierarchy", - ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls", - ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls", - } - - interface CallHierarchyItem { - name: string; - kind: ScriptElementKind; - file: string; - span: TextSpan; - selectionSpan: TextSpan; - } - - interface CallHierarchyIncomingCall { - from: CallHierarchyItem; - fromSpans: TextSpan[]; - } - - interface CallHierarchyOutgoingCall { - to: CallHierarchyItem; - fromSpans: TextSpan[]; - } - - interface PrepareCallHierarchyRequest extends FileLocationRequest { - command: CommandTypes.PrepareCallHierarchy; - } - - interface PrepareCallHierarchyResponse extends Response { - readonly body: CallHierarchyItem | CallHierarchyItem[]; - } - - interface ProvideCallHierarchyIncomingCallsRequest extends FileLocationRequest { - command: CommandTypes.ProvideCallHierarchyIncomingCalls; - kind: ScriptElementKind; - } - - interface ProvideCallHierarchyIncomingCallsResponse extends Response { - readonly body: CallHierarchyIncomingCall[]; - } - - interface ProvideCallHierarchyOutgoingCallsRequest extends FileLocationRequest { - command: CommandTypes.ProvideCallHierarchyOutgoingCalls; - kind: ScriptElementKind; - } - - interface ProvideCallHierarchyOutgoingCallsResponse extends Response { - readonly body: CallHierarchyOutgoingCall[]; + readonly _serverType?: ServerType; } } diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/task/taskProvider.ts similarity index 82% rename from extensions/typescript-language-features/src/features/task.ts rename to extensions/typescript-language-features/src/task/taskProvider.ts index b63ff25019f..75ca51278dd 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/task/taskProvider.ts @@ -7,25 +7,25 @@ import * as jsonc from 'jsonc-parser'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import { wait } from '../test/testUtils'; import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { coalesce, flatten } from '../utils/arrays'; +import { Disposable } from '../utils/dispose'; +import { exists } from '../utils/fs'; import { isTsConfigFileName } from '../utils/languageDescription'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; -import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; +import { TSConfig, TsConfigProvider } from './tsconfigProvider'; const localize = nls.loadMessageBundle(); -type AutoDetect = 'on' | 'off' | 'build' | 'watch'; +enum AutoDetect { + on = 'on', + off = 'off', + build = 'build', + watch = 'watch' +} -const exists = async (resource: vscode.Uri): Promise<boolean> => { - try { - const stat = await vscode.workspace.fs.stat(resource); - // stat.type is an enum flag - return !!(stat.type & vscode.FileType.File); - } catch { - return false; - } -}; interface TypeScriptTaskDefinition extends vscode.TaskDefinition { tsconfig: string; @@ -35,29 +35,27 @@ interface TypeScriptTaskDefinition extends vscode.TaskDefinition { /** * Provides tasks for building `tsconfig.json` files in a project. */ -export default class TscTaskProvider implements vscode.TaskProvider { +class TscTaskProvider extends Disposable implements vscode.TaskProvider { private readonly projectInfoRequestTimeout = 2000; - private autoDetect: AutoDetect = 'on'; + private readonly findConfigFilesTimeout = 5000; + + private autoDetect = AutoDetect.on; private readonly tsconfigProvider: TsConfigProvider; - private readonly disposables: vscode.Disposable[] = []; public constructor( private readonly client: Lazy<ITypeScriptServiceClient> ) { + super(); this.tsconfigProvider = new TsConfigProvider(); - vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this.disposables); + this._register(vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this)); this.onConfigurationChanged(); } - dispose() { - this.disposables.forEach(x => x.dispose()); - } - public async provideTasks(token: vscode.CancellationToken): Promise<vscode.Task[]> { const folders = vscode.workspace.workspaceFolders; - if ((this.autoDetect === 'off') || !folders || !folders.length) { + if ((this.autoDetect === AutoDetect.off) || !folders || !folders.length) { return []; } @@ -100,17 +98,14 @@ export default class TscTaskProvider implements vscode.TaskProvider { } private async getAllTsConfigs(token: vscode.CancellationToken): Promise<TSConfig[]> { - const out = new Set<TSConfig>(); - const configs = [ - ...await this.getTsConfigForActiveFile(token), - ...await this.getTsConfigsInWorkspace() - ]; - for (const config of configs) { - if (await exists(config.uri)) { - out.add(config); - } - } - return Array.from(out); + const configs = flatten(await Promise.all([ + this.getTsConfigForActiveFile(token), + this.getTsConfigsInWorkspace(token), + ])); + + return Promise.all( + configs.map(async config => await exists(config.uri) ? config : undefined), + ).then(coalesce); } private async getTsConfigForActiveFile(token: vscode.CancellationToken): Promise<TSConfig[]> { @@ -159,8 +154,17 @@ export default class TscTaskProvider implements vscode.TaskProvider { return []; } - private async getTsConfigsInWorkspace(): Promise<TSConfig[]> { - return Array.from(await this.tsconfigProvider.getConfigsForWorkspace()); + private async getTsConfigsInWorkspace(token: vscode.CancellationToken): Promise<TSConfig[]> { + const getConfigsTimeout = new vscode.CancellationTokenSource(); + token.onCancellationRequested(() => getConfigsTimeout.cancel()); + + return Promise.race([ + this.tsconfigProvider.getConfigsForWorkspace(getConfigsTimeout.token).then(x => Array.from(x)), + wait(this.findConfigFilesTimeout).then(() => { + getConfigsTimeout.cancel(); + return []; + }), + ]); } private static async getCommand(project: TSConfig): Promise<string> { @@ -203,7 +207,7 @@ export default class TscTaskProvider implements vscode.TaskProvider { } private getBuildTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], buildTaskidentifier: TypeScriptTaskDefinition): vscode.Task { - const buildTask = new vscode.Task2( + const buildTask = new vscode.Task( buildTaskidentifier, workspaceFolder || vscode.TaskScope.Workspace, localize('buildTscLabel', 'build - {0}', label), @@ -235,11 +239,11 @@ export default class TscTaskProvider implements vscode.TaskProvider { const tasks: vscode.Task[] = []; - if (this.autoDetect === 'build' || this.autoDetect === 'on') { + if (this.autoDetect === AutoDetect.build || this.autoDetect === AutoDetect.on) { tasks.push(this.getBuildTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label })); } - if (this.autoDetect === 'watch' || this.autoDetect === 'on') { + if (this.autoDetect === AutoDetect.watch || this.autoDetect === AutoDetect.on) { tasks.push(this.getWatchTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label, option: 'watch' })); } @@ -288,6 +292,12 @@ export default class TscTaskProvider implements vscode.TaskProvider { private onConfigurationChanged(): void { const type = vscode.workspace.getConfiguration('typescript.tsc').get<AutoDetect>('autoDetect'); - this.autoDetect = typeof type === 'undefined' ? 'on' : type; + this.autoDetect = typeof type === 'undefined' ? AutoDetect.on : type; } } + +export function register( + lazyClient: Lazy<ITypeScriptServiceClient>, +) { + return vscode.tasks.registerTaskProvider('typescript', new TscTaskProvider(lazyClient)); +} diff --git a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts b/extensions/typescript-language-features/src/task/tsconfigProvider.ts similarity index 69% rename from extensions/typescript-language-features/src/utils/tsconfigProvider.ts rename to extensions/typescript-language-features/src/task/tsconfigProvider.ts index e77dfb306a9..16dca92cc62 100644 --- a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts +++ b/extensions/typescript-language-features/src/task/tsconfigProvider.ts @@ -12,13 +12,14 @@ export interface TSConfig { readonly workspaceFolder?: vscode.WorkspaceFolder; } -export default class TsConfigProvider { - public async getConfigsForWorkspace(): Promise<Iterable<TSConfig>> { +export class TsConfigProvider { + public async getConfigsForWorkspace(token: vscode.CancellationToken): Promise<Iterable<TSConfig>> { if (!vscode.workspace.workspaceFolders) { return []; } + const configs = new Map<string, TSConfig>(); - for (const config of await vscode.workspace.findFiles('**/tsconfig*.json', '**/{node_modules,.*}/**')) { + for (const config of await this.findConfigFiles(token)) { const root = vscode.workspace.getWorkspaceFolder(config); if (root) { configs.set(config.fsPath, { @@ -31,4 +32,8 @@ export default class TsConfigProvider { } return configs.values(); } + + private async findConfigFiles(token: vscode.CancellationToken): Promise<vscode.Uri[]> { + return await vscode.workspace.findFiles('**/tsconfig*.json', '**/{node_modules,.*}/**', undefined, token); + } } diff --git a/extensions/typescript-language-features/src/test/cachedResponse.test.ts b/extensions/typescript-language-features/src/test/cachedResponse.test.ts index 919d5d4d03c..ae05f5d4426 100644 --- a/extensions/typescript-language-features/src/test/cachedResponse.test.ts +++ b/extensions/typescript-language-features/src/test/cachedResponse.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { CachedResponse } from '../tsServer/cachedResponse'; import { ServerResponse } from '../typescriptService'; diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts index f35d0cae73c..92a7da63f92 100644 --- a/extensions/typescript-language-features/src/test/completions.test.ts +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -3,117 +3,115 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { disposeAll } from '../utils/dispose'; -import { createTestEditor, joinLines, wait } from './testUtils'; import { acceptFirstSuggestion, typeCommitCharacter } from './suggestTestHelpers'; +import { assertEditorContents, Config, createTestEditor, joinLines, updateConfig, VsCodeConfiguration, wait, enumerateConfig } from './testUtils'; const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); -type VsCodeConfiguration = { [key: string]: any }; - -async function updateConfig(newConfig: VsCodeConfiguration): Promise<VsCodeConfiguration> { - const oldConfig: VsCodeConfiguration = {}; - const config = vscode.workspace.getConfiguration(undefined, testDocumentUri); - for (const configKey of Object.keys(newConfig)) { - oldConfig[configKey] = config.get(configKey); - await new Promise((resolve, reject) => - config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global) - .then(() => resolve(), reject)); - } - return oldConfig; -} - -namespace Config { - export const suggestSelection = 'editor.suggestSelection'; - export const completeFunctionCalls = 'typescript.suggest.completeFunctionCalls'; -} +const insertModes = Object.freeze(['insert', 'replace']); suite('TypeScript Completions', () => { const configDefaults: VsCodeConfiguration = Object.freeze({ + [Config.autoClosingBrackets]: 'always', + [Config.typescriptCompleteFunctionCalls]: false, + [Config.insertMode]: 'insert', + [Config.snippetSuggestions]: 'none', [Config.suggestSelection]: 'first', - [Config.completeFunctionCalls]: false, + [Config.javascriptQuoteStyle]: 'double', + [Config.typescriptQuoteStyle]: 'double', }); const _disposables: vscode.Disposable[] = []; let oldConfig: { [key: string]: any } = {}; setup(async () => { - await wait(100); + await wait(500); // Save off config and apply defaults - oldConfig = await updateConfig(configDefaults); + oldConfig = await updateConfig(testDocumentUri, configDefaults); }); teardown(async () => { disposeAll(_disposables); // Restore config - await updateConfig(oldConfig); + await updateConfig(testDocumentUri, oldConfig); return vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); test('Basic var completion', async () => { - await createTestEditor(testDocumentUri, - `const abcdef = 123;`, - `ab$0;` - ); - - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), - joinLines( + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, `const abcdef = 123;`, - `abcdef;` - )); + `ab$0;` + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `const abcdef = 123;`, + `abcdef;` + ), + `config: ${config}` + ); + }); }); test('Should treat period as commit character for var completions', async () => { - await createTestEditor(testDocumentUri, - `const abcdef = 123;`, - `ab$0;` - ); - - const document = await typeCommitCharacter(testDocumentUri, '.', _disposables); - assert.strictEqual( - document.getText(), - joinLines( + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, `const abcdef = 123;`, - `abcdef.;` - )); + `ab$0;` + ); + + await typeCommitCharacter(testDocumentUri, '.', _disposables); + + assertEditorContents(editor, + joinLines( + `const abcdef = 123;`, + `abcdef.;` + ), + `config: ${config}`); + }); }); test('Should treat paren as commit character for function completions', async () => { - await createTestEditor(testDocumentUri, - `function abcdef() {};`, - `ab$0;` - ); - - const document = await typeCommitCharacter(testDocumentUri, '(', _disposables); - assert.strictEqual( - document.getText(), - joinLines( + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, `function abcdef() {};`, - `abcdef();` - )); + `ab$0;` + ); + + await typeCommitCharacter(testDocumentUri, '(', _disposables); + + assertEditorContents(editor, + joinLines( + `function abcdef() {};`, + `abcdef();` + ), `config: ${config}`); + }); }); test('Should insert backets when completing dot properties with spaces in name', async () => { - await createTestEditor(testDocumentUri, - 'const x = { "hello world": 1 };', - 'x.$0' - ); - - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), - joinLines( + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, 'const x = { "hello world": 1 };', - 'x["hello world"]' - )); + 'x.$0' + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + 'const x = { "hello world": 1 };', + 'x["hello world"]' + ), `config: ${config}`); + }); }); test('Should allow commit characters for backet completions', async () => { @@ -121,14 +119,14 @@ suite('TypeScript Completions', () => { { char: '.', insert: '.' }, { char: '(', insert: '()' }, ]) { - await createTestEditor(testDocumentUri, + const editor = await createTestEditor(testDocumentUri, 'const x = { "hello world2": 1 };', 'x.$0' ); - const document = await typeCommitCharacter(testDocumentUri, char, _disposables); - assert.strictEqual( - document.getText(), + await typeCommitCharacter(testDocumentUri, char, _disposables); + + assertEditorContents(editor, joinLines( 'const x = { "hello world2": 1 };', `x["hello world2"]${insert}` @@ -140,23 +138,26 @@ suite('TypeScript Completions', () => { }); test('Should not prioritize bracket accessor completions. #63100', async () => { - // 'a' should be first entry in completion list - await createTestEditor(testDocumentUri, - 'const x = { "z-z": 1, a: 1 };', - 'x.$0' - ); - - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), - joinLines( + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + // 'a' should be first entry in completion list + const editor = await createTestEditor(testDocumentUri, 'const x = { "z-z": 1, a: 1 };', - 'x.a' - )); + 'x.$0' + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + 'const x = { "z-z": 1, a: 1 };', + 'x.a' + ), + `config: ${config}`); + }); }); test('Accepting a string completion should replace the entire string. #53962', async () => { - await createTestEditor(testDocumentUri, + const editor = await createTestEditor(testDocumentUri, 'interface TFunction {', ` (_: 'abc.abc2', __ ?: {}): string;`, ` (_: 'abc.abc', __?: {}): string;`, @@ -165,9 +166,9 @@ suite('TypeScript Completions', () => { `f('abc.abc$0')` ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables, { useLineRange: true }); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( 'interface TFunction {', ` (_: 'abc.abc2', __ ?: {}): string;`, @@ -178,35 +179,18 @@ suite('TypeScript Completions', () => { )); }); - test.skip('Accepting a member completion should result in valid code. #58597', async () => { - await createTestEditor(testDocumentUri, - `const abc = 123;`, - `ab$0c` - ); - - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), - joinLines( - `const abc = 123;`, - `abc` - )); - }); - test('completeFunctionCalls should complete function parameters when at end of word', async () => { - await updateConfig({ - [Config.completeFunctionCalls]: true, - }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); // Complete with-in word - await createTestEditor(testDocumentUri, + const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, `abcdef$0` ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( `function abcdef(x, y, z) { }`, `abcdef(x, y, z)` @@ -214,18 +198,16 @@ suite('TypeScript Completions', () => { }); test.skip('completeFunctionCalls should complete function parameters when within word', async () => { - await updateConfig({ - [Config.completeFunctionCalls]: true, - }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); - await createTestEditor(testDocumentUri, + const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, `abcd$0ef` ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( `function abcdef(x, y, z) { }`, `abcdef(x, y, z)` @@ -233,18 +215,16 @@ suite('TypeScript Completions', () => { }); test('completeFunctionCalls should not complete function parameters at end of word if we are already in something that looks like a function call, #18131', async () => { - await updateConfig({ - [Config.completeFunctionCalls]: true, - }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); - await createTestEditor(testDocumentUri, + const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, `abcdef$0(1, 2, 3)` ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( `function abcdef(x, y, z) { }`, `abcdef(1, 2, 3)` @@ -252,45 +232,421 @@ suite('TypeScript Completions', () => { }); test.skip('completeFunctionCalls should not complete function parameters within word if we are already in something that looks like a function call, #18131', async () => { - await updateConfig({ - [Config.completeFunctionCalls]: true, - }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); - await createTestEditor(testDocumentUri, + const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, `abcd$0ef(1, 2, 3)` ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( `function abcdef(x, y, z) { }`, `abcdef(1, 2, 3)` )); }); - test('should not de-prioritized this.member suggestion, #74164', async () => { - await createTestEditor(testDocumentUri, - `class A {`, - ` private detail = '';`, + test('should not de-prioritize `this.member` suggestion, #74164', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` private detail = '';`, + ` foo() {`, + ` det$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` private detail = '';`, + ` foo() {`, + ` this.detail`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Member completions for string property name should insert `this.` and use brackets', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` ['xyz 123'] = 1`, + ` foo() {`, + ` xyz$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` ['xyz 123'] = 1`, + ` foo() {`, + ` this["xyz 123"]`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Member completions for string property name already using `this.` should add brackets', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` ['xyz 123'] = 1`, + ` foo() {`, + ` this.xyz$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` ['xyz 123'] = 1`, + ` foo() {`, + ` this["xyz 123"]`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Accepting a completion in word using `insert` mode should insert', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' }); + + const editor = await createTestEditor(testDocumentUri, + `const abc = 123;`, + `ab$0c` + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `const abc = 123;`, + `abcc` + )); + }); + + test('Accepting a completion in word using `replace` mode should replace', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' }); + + const editor = await createTestEditor(testDocumentUri, + `const abc = 123;`, + `ab$0c` + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `const abc = 123;`, + `abc` + )); + }); + + test('Accepting a member completion in word using `insert` mode add `this.` and insert', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' }); + + const editor = await createTestEditor(testDocumentUri, + `class Foo {`, + ` abc = 1;`, ` foo() {`, - ` det$0`, + ` ab$0c`, ` }`, `}`, ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class Foo {`, + ` abc = 1;`, + ` foo() {`, + ` this.abcc`, + ` }`, + `}`, + )); + }); + + test('Accepting a member completion in word using `replace` mode should add `this.` and replace', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' }); + + const editor = await createTestEditor(testDocumentUri, + `class Foo {`, + ` abc = 1;`, + ` foo() {`, + ` ab$0c`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class Foo {`, + ` abc = 1;`, + ` foo() {`, + ` this.abc`, + ` }`, + `}`, + )); + }); + + test('Accepting string completion inside string using `insert` mode should insert', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' }); + + const editor = await createTestEditor(testDocumentUri, + `const abc = { 'xy z': 123 }`, + `abc["x$0y w"]` + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `const abc = { 'xy z': 123 }`, + `abc["xy zy w"]` + )); + }); + + // Waiting on https://github.com/microsoft/TypeScript/issues/35602 + test.skip('Accepting string completion inside string using insert mode should insert', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' }); + + const editor = await createTestEditor(testDocumentUri, + `const abc = { 'xy z': 123 }`, + `abc["x$0y w"]` + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `const abc = { 'xy z': 123 }`, + `abc["xy w"]` + )); + }); + + test('Private field completions on `this.#` should work', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` this.#$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` this.#xyz`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Private field completions on `#` should insert `this.`', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` #$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` this.#xyz`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Private field completions should not require strict prefix match (#89556)', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` this.xyz$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` this.#xyz`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Private field completions without `this.` should not require strict prefix match (#89556)', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` xyz$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` #xyz = 1;`, + ` foo() {`, + ` this.#xyz`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Accepting a completion for async property in `insert` mode should insert and add await', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' }); + + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` xyz = Promise.resolve({ 'abc': 1 });`, + ` async foo() {`, + ` this.xyz.ab$0c`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( `class A {`, - ` private detail = '';`, + ` xyz = Promise.resolve({ 'abc': 1 });`, + ` async foo() {`, + ` (await this.xyz).abcc`, + ` }`, + `}`, + )); + }); + + test('Accepting a completion for async property in `replace` mode should replace and add await', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' }); + + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` xyz = Promise.resolve({ 'abc': 1 });`, + ` async foo() {`, + ` this.xyz.ab$0c`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` xyz = Promise.resolve({ 'abc': 1 });`, + ` async foo() {`, + ` (await this.xyz).abc`, + ` }`, + `}`, + )); + }); + + test.skip('Accepting a completion for async string property should add await plus brackets', async () => { + await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => { + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` xyz = Promise.resolve({ 'ab c': 1 });`, + ` async foo() {`, + ` this.xyz.ab$0`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` xyz = Promise.resolve({ 'abc': 1 });`, + ` async foo() {`, + ` (await this.xyz)["ab c"]`, + ` }`, + `}`, + ), + `Config: ${config}`); + }); + }); + + test('Replace should work after this. (#91105)', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' }); + + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` abc = 1`, + ` foo() {`, + ` this.$0abc`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` abc = 1`, ` foo() {`, - ` this.detail`, + ` this.abc`, ` }`, `}`, )); }); }); - diff --git a/extensions/typescript-language-features/src/test/fixAll.test.ts b/extensions/typescript-language-features/src/test/fixAll.test.ts new file mode 100644 index 00000000000..37fae0bcfc8 --- /dev/null +++ b/extensions/typescript-language-features/src/test/fixAll.test.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; +import { createTestEditor, wait, joinLines } from './testUtils'; + +const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); + +const emptyRange = new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)); + +suite('TypeScript Fix All', () => { + + const _disposables: vscode.Disposable[] = []; + + teardown(async () => { + disposeAll(_disposables); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Fix all should remove unreachable code', async () => { + const editor = await createTestEditor(testDocumentUri, + `function foo() {`, + ` return 1;`, + ` return 2;`, + `};`, + `function boo() {`, + ` return 3;`, + ` return 4;`, + `};`, + ); + + await wait(2000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + emptyRange, + vscode.CodeActionKind.SourceFixAll + ); + + await vscode.workspace.applyEdit(fixes![0].edit!); + + assert.strictEqual(editor.document.getText(), joinLines( + `function foo() {`, + ` return 1;`, + `};`, + `function boo() {`, + ` return 3;`, + `};`, + )); + + }); + + test('Fix all should implement interfaces', async () => { + const editor = await createTestEditor(testDocumentUri, + `interface I {`, + ` x: number;`, + `}`, + `class A implements I {}`, + `class B implements I {}`, + ); + + await wait(2000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + emptyRange, + vscode.CodeActionKind.SourceFixAll + ); + + await vscode.workspace.applyEdit(fixes![0].edit!); + assert.strictEqual(editor.document.getText(), joinLines( + `interface I {`, + ` x: number;`, + `}`, + `class A implements I {`, + ` x: number;`, + `}`, + `class B implements I {`, + ` x: number;`, + `}`, + )); + }); + + test('Remove unused should handle nested ununused', async () => { + const editor = await createTestEditor(testDocumentUri, + `export const _ = 1;`, + `function unused() {`, + ` const a = 1;`, + `}`, + `function used() {`, + ` const a = 1;`, + `}`, + `used();` + ); + + await wait(2000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + emptyRange, + vscode.CodeActionKind.Source.append('removeUnused') + ); + + await vscode.workspace.applyEdit(fixes![0].edit!); + assert.strictEqual(editor.document.getText(), joinLines( + `export const _ = 1;`, + `function used() {`, + `}`, + `used();` + )); + }); + + test('Remove unused should remove unused interfaces', async () => { + const editor = await createTestEditor(testDocumentUri, + `export const _ = 1;`, + `interface Foo {}` + ); + + await wait(2000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + emptyRange, + vscode.CodeActionKind.Source.append('removeUnused') + ); + + await vscode.workspace.applyEdit(fixes![0].edit!); + assert.strictEqual(editor.document.getText(), joinLines( + `export const _ = 1;`, + `` + )); + }); +}); diff --git a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts index 1289b87b4bc..5c8cf18c73c 100644 --- a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts @@ -128,4 +128,13 @@ suite('typescript function call snippets', () => { ).snippet.value, 'foobar(${1:param})$0'); }); + + test('Should skip over this parameter', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foobar', }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foobar", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "this", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ",", "kind": "punctuation" }, { "text": "param", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'foobar(${1:param})$0'); + }); }); diff --git a/extensions/typescript-language-features/src/test/jsDocCompletions.test.ts b/extensions/typescript-language-features/src/test/jsDocCompletions.test.ts index ffd836f5ab3..63d3f144f9a 100644 --- a/extensions/typescript-language-features/src/test/jsDocCompletions.test.ts +++ b/extensions/typescript-language-features/src/test/jsDocCompletions.test.ts @@ -3,42 +3,59 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { disposeAll } from '../utils/dispose'; -import { createTestEditor, joinLines, wait } from './testUtils'; import { acceptFirstSuggestion } from './suggestTestHelpers'; +import { assertEditorContents, Config, createTestEditor, CURSOR, enumerateConfig, insertModesValues, joinLines, updateConfig, VsCodeConfiguration, wait } from './testUtils'; const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); suite('JSDoc Completions', () => { const _disposables: vscode.Disposable[] = []; + const configDefaults: VsCodeConfiguration = Object.freeze({ + [Config.snippetSuggestions]: 'inline', + }); + + let oldConfig: { [key: string]: any } = {}; + setup(async () => { await wait(100); + + // Save off config and apply defaults + oldConfig = await updateConfig(testDocumentUri, configDefaults); }); teardown(async () => { disposeAll(_disposables); + + // Restore config + await updateConfig(testDocumentUri, oldConfig); + + return vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); test('Should complete jsdoc inside single line comment', async () => { - await createTestEditor(testDocumentUri, - `/**$0 */`, - `function abcdef(x, y) { }`, - ); + await enumerateConfig(testDocumentUri, Config.insertMode, insertModesValues, async config => { - const document = await acceptFirstSuggestion(testDocumentUri, _disposables, { useLineRange: true}); - assert.strictEqual( - document.getText(), - joinLines( - `/**`, - ` *`, - ` * @param {*} x `, - ` * @param {*} y `, - ` */`, + const editor = await createTestEditor(testDocumentUri, + `/**$0 */`, `function abcdef(x, y) { }`, - )); + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `/**`, + ` * `, + ` * @param x ${CURSOR}`, + ` * @param y `, + ` */`, + `function abcdef(x, y) { }`, + ), + `Config: ${config}`); + }); }); }); diff --git a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts index a982e46221f..d9e17a75644 100644 --- a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import 'mocha'; -import { templateToSnippet } from '../features/jsDocCompletions'; +import { templateToSnippet } from '../languageFeatures/jsDocCompletions'; const joinLines = (...args: string[]) => args.join('\n'); diff --git a/extensions/typescript-language-features/src/test/onEnter.test.ts b/extensions/typescript-language-features/src/test/onEnter.test.ts index c7d6c3a45dd..72e59b4482f 100644 --- a/extensions/typescript-language-features/src/test/onEnter.test.ts +++ b/extensions/typescript-language-features/src/test/onEnter.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { CURSOR, withRandomFileEditor } from './testUtils'; +import { CURSOR, withRandomFileEditor, wait, joinLines } from './testUtils'; const onDocumentChange = (doc: vscode.TextDocument): Promise<vscode.TextDocument> => { return new Promise<vscode.TextDocument>(resolve => { @@ -31,28 +31,24 @@ suite('OnEnter', () => { test('should indent after if block with braces', () => { return withRandomFileEditor(`if (true) {${CURSOR}`, 'js', async (_editor, document) => { await type(document, '\nx'); - assert.strictEqual(document.getText(), `if (true) {\n x`); + assert.strictEqual( + document.getText(), + joinLines( + `if (true) {`, + ` x`)); }); }); test('should indent within empty object literal', () => { return withRandomFileEditor(`({${CURSOR}})`, 'js', async (_editor, document) => { await type(document, '\nx'); - assert.strictEqual(document.getText(), `({\n x\n})`); - }); - }); + await wait(500); - test('should indent after simple jsx tag with attributes', () => { - return withRandomFileEditor(`const a = <div onclick={bla}>${CURSOR}`, 'jsx', async (_editor, document) => { - await type(document, '\nx'); - assert.strictEqual(document.getText(), `const a = <div onclick={bla}>\n x`); + assert.strictEqual( + document.getText(), + joinLines(`({`, + ` x`, + `})`)); }); }); - - test('should indent after simple jsx tag with attributes', () => { - return withRandomFileEditor(`const a = <div onclick={bla}>${CURSOR}`, 'jsx', async (_editor, document) => { - await type(document, '\nx'); - assert.strictEqual(document.getText(), `const a = <div onclick={bla}>\n x`); - }); - }); -}); \ No newline at end of file +}); diff --git a/extensions/typescript-language-features/src/test/previewer.test.ts b/extensions/typescript-language-features/src/test/previewer.test.ts index 011187b2af4..b92c0019631 100644 --- a/extensions/typescript-language-features/src/test/previewer.test.ts +++ b/extensions/typescript-language-features/src/test/previewer.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import 'mocha'; -import { tagsMarkdownPreview } from '../utils/previewer'; +import { tagsMarkdownPreview, markdownDocumentation } from '../utils/previewer'; suite('typescript.previewer', () => { test('Should ignore hyphens after a param tag', async () => { @@ -18,5 +18,51 @@ suite('typescript.previewer', () => { ]), '*@param* `a` — b'); }); + + test('Should parse url jsdoc @link', async () => { + assert.strictEqual( + markdownDocumentation('x {@link http://www.example.com/foo} y {@link https://api.jquery.com/bind/#bind-eventType-eventData-handler} z', []).value, + 'x [http://www.example.com/foo](http://www.example.com/foo) y [https://api.jquery.com/bind/#bind-eventType-eventData-handler](https://api.jquery.com/bind/#bind-eventType-eventData-handler) z'); + }); + + test('Should parse url jsdoc @link with text', async () => { + assert.strictEqual( + markdownDocumentation('x {@link http://www.example.com/foo abc xyz} y {@link http://www.example.com/bar|b a z} z', []).value, + 'x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z'); + }); + + test('Should treat @linkcode jsdocs links as monospace', async () => { + assert.strictEqual( + markdownDocumentation('x {@linkcode http://www.example.com/foo} y {@linkplain http://www.example.com/bar} z', []).value, + 'x [`http://www.example.com/foo`](http://www.example.com/foo) y [http://www.example.com/bar](http://www.example.com/bar) z'); + }); + + test('Should parse url jsdoc @link in param tag', async () => { + assert.strictEqual( + tagsMarkdownPreview([ + { + name: 'param', + text: 'a x {@link http://www.example.com/foo abc xyz} y {@link http://www.example.com/bar|b a z} z' + } + ]), + '*@param* `a` — x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z'); + }); + + test('Should ignore unclosed jsdocs @link', async () => { + assert.strictEqual( + markdownDocumentation('x {@link http://www.example.com/foo y {@link http://www.example.com/bar bar} z', []).value, + 'x {@link http://www.example.com/foo y [bar](http://www.example.com/bar) z'); + }); + + test('Should support non-ascii characters in parameter name (#90108)', async () => { + assert.strictEqual( + tagsMarkdownPreview([ + { + name: 'param', + text: 'parámetroConDiacríticos this will not' + } + ]), + '*@param* `parámetroConDiacríticos` — this will not'); + }); }); diff --git a/extensions/typescript-language-features/src/test/quickFix.test.ts b/extensions/typescript-language-features/src/test/quickFix.test.ts new file mode 100644 index 00000000000..13200dbcf50 --- /dev/null +++ b/extensions/typescript-language-features/src/test/quickFix.test.ts @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; +import { createTestEditor, joinLines, retryUntilDocumentChanges, wait } from './testUtils'; + +suite('TypeScript Quick Fix', () => { + + const _disposables: vscode.Disposable[] = []; + + teardown(async () => { + disposeAll(_disposables); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Fix all should not be marked as preferred #97866', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); + + const editor = await createTestEditor(testDocumentUri, + `export const _ = 1;`, + `const a$0 = 1;`, + `const b = 2;`, + ); + + await retryUntilDocumentChanges(testDocumentUri, { retries: 10, timeout: 500 }, _disposables, () => { + return vscode.commands.executeCommand('editor.action.autoFix'); + }); + + assert.strictEqual(editor.document.getText(), joinLines( + `export const _ = 1;`, + `const b = 2;`, + )); + }); + + test('Add import should be a preferred fix if there is only one possible import', async () => { + const testDocumentUri = workspaceFile('foo.ts'); + + await createTestEditor(testDocumentUri, + `export const foo = 1;`); + + const editor = await createTestEditor(workspaceFile('index.ts'), + `export const _ = 1;`, + `foo$0;` + ); + + await retryUntilDocumentChanges(testDocumentUri, { retries: 10, timeout: 500 }, _disposables, () => { + return vscode.commands.executeCommand('editor.action.autoFix'); + }); + + // Document should not have been changed here + + assert.strictEqual(editor.document.getText(), joinLines( + `import { foo } from "./foo";`, + ``, + `export const _ = 1;`, + `foo;` + )); + }); + + test('Add import should not be a preferred fix if are multiple possible imports', async () => { + await createTestEditor(workspaceFile('foo.ts'), + `export const foo = 1;`); + + await createTestEditor(workspaceFile('bar.ts'), + `export const foo = 1;`); + + const editor = await createTestEditor(workspaceFile('index.ts'), + `export const _ = 1;`, + `foo$0;` + ); + + await wait(3000); + + await vscode.commands.executeCommand('editor.action.autoFix'); + + await wait(500); + + assert.strictEqual(editor.document.getText(), joinLines( + `export const _ = 1;`, + `foo;` + )); + }); + + test('Only a single ts-ignore should be returned if there are multiple errors on one line #98274', async () => { + const testDocumentUri = workspaceFile('foojs.js'); + const editor = await createTestEditor(testDocumentUri, + `//@ts-check`, + `const a = require('./bla');`); + + await wait(3000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + editor.document.lineAt(1).range + ); + + const ignoreFixes = fixes?.filter(x => x.title === 'Ignore this error message'); + assert.strictEqual(ignoreFixes?.length, 1); + }); + + test('Should prioritize implement interface over remove unused #94212', async () => { + const testDocumentUri = workspaceFile('foo.ts'); + const editor = await createTestEditor(testDocumentUri, + `export interface IFoo { value: string; }`, + `class Foo implements IFoo { }`); + + await wait(3000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + editor.document.lineAt(1).range + ); + + assert.strictEqual(fixes?.length, 2); + assert.strictEqual(fixes![0].title, `Implement interface 'IFoo'`); + assert.strictEqual(fixes![1].title, `Remove unused declaration for: 'Foo'`); + }); + + test('Should prioritize implement abstract class over remove unused #101486', async () => { + const testDocumentUri = workspaceFile('foo.ts'); + const editor = await createTestEditor(testDocumentUri, + `export abstract class Foo { abstract foo(): number; }`, + `class ConcreteFoo extends Foo { }`, + ); + + await wait(3000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + testDocumentUri, + editor.document.lineAt(1).range + ); + + assert.strictEqual(fixes?.length, 2); + assert.strictEqual(fixes![0].title, `Implement inherited abstract class`); + assert.strictEqual(fixes![1].title, `Remove unused declaration for: 'ConcreteFoo'`); + }); + + test('Add all missing imports should come after other add import fixes #98613', async () => { + await createTestEditor(workspaceFile('foo.ts'), + `export const foo = 1;`); + + await createTestEditor(workspaceFile('bar.ts'), + `export const foo = 1;`); + + const editor = await createTestEditor(workspaceFile('index.ts'), + `export const _ = 1;`, + `foo$0;`, + `foo$0;` + ); + + await wait(3000); + + const fixes = await vscode.commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', + workspaceFile('index.ts'), + editor.document.lineAt(1).range + ); + + assert.strictEqual(fixes?.length, 3); + assert.strictEqual(fixes![0].title, `Import 'foo' from module "./bar"`); + assert.strictEqual(fixes![1].title, `Import 'foo' from module "./foo"`); + assert.strictEqual(fixes![2].title, `Add all missing imports`); + }); +}); + +function workspaceFile(fileName: string) { + return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, fileName); +} diff --git a/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts index 7ca35ed89cc..f848d0f4d03 100644 --- a/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts +++ b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts @@ -17,7 +17,7 @@ async function updateConfig(newConfig: VsCodeConfiguration): Promise<VsCodeConfi const config = vscode.workspace.getConfiguration(undefined); for (const configKey of Object.keys(newConfig)) { oldConfig[configKey] = config.get(configKey); - await new Promise((resolve, reject) => + await new Promise<void>((resolve, reject) => config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global) .then(() => resolve(), reject)); } @@ -91,6 +91,20 @@ suite('TypeScript References', () => { const codeLenses = await getCodeLenses(testDocumentUri); assert.strictEqual(codeLenses?.length, 0); }); + + test.skip('Should not show duplicate references on ES5 class (https://github.com/microsoft/vscode/issues/90396)', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test3.js'); + await createTestEditor(testDocumentUri, + `function A() {`, + ` console.log("hi");`, + `}`, + `A.x = {};`, + ); + + await wait(500); + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 1); + }); }); function getCodeLenses(document: vscode.Uri): Thenable<readonly vscode.CodeLens[] | undefined> { diff --git a/extensions/typescript-language-features/src/test/server.test.ts b/extensions/typescript-language-features/src/test/server.test.ts index fd3210670ff..5caad737a48 100644 --- a/extensions/typescript-language-features/src/test/server.test.ts +++ b/extensions/typescript-language-features/src/test/server.test.ts @@ -6,12 +6,14 @@ import * as assert from 'assert'; import 'mocha'; import * as stream from 'stream'; -import { PipeRequestCanceller, TsServerProcess, ProcessBasedTsServer } from '../tsServer/server'; +import type * as Proto from '../protocol'; +import { NodeRequestCanceller } from '../tsServer/cancellation.electron'; +import { ProcessBasedTsServer, TsServerProcess } from '../tsServer/server'; +import { ServerType } from '../typescriptService'; import { nulToken } from '../utils/cancellation'; -import Logger from '../utils/logger'; +import { Logger } from '../utils/logger'; import { TelemetryReporter } from '../utils/telemetry'; import Tracer from '../utils/tracer'; -import * as Proto from '../protocol'; const NoopTelemetryReporter = new class implements TelemetryReporter { @@ -43,8 +45,9 @@ class FakeServerProcess implements TsServerProcess { }); } - - on(_name: any, _handler: any) { /* noop */ } + onData(_handler: any) { /* noop */ } + onError(_handler: any) { /* noop */ } + onExit(_handler: any) { /* noop */ } kill(): void { /* noop */ } @@ -62,7 +65,7 @@ suite('Server', () => { test('should send requests with increasing sequence numbers', async () => { const process = new FakeServerProcess(); - const server = new ProcessBasedTsServer('semantic', process, undefined, new PipeRequestCanceller('semantic', undefined, tracer), undefined!, NoopTelemetryReporter, tracer); + const server = new ProcessBasedTsServer('semantic', ServerType.Semantic, process, undefined, new NodeRequestCanceller('semantic', tracer), undefined!, NoopTelemetryReporter, tracer); const onWrite1 = process.onWrite(); server.executeImpl('geterr', {}, { isAsync: false, token: nulToken, expectsResult: true }); diff --git a/extensions/typescript-language-features/src/test/suggestTestHelpers.ts b/extensions/typescript-language-features/src/test/suggestTestHelpers.ts index 7c64a26ad5c..f417ceaadb5 100644 --- a/extensions/typescript-language-features/src/test/suggestTestHelpers.ts +++ b/extensions/typescript-language-features/src/test/suggestTestHelpers.ts @@ -5,59 +5,20 @@ import 'mocha'; import * as vscode from 'vscode'; -import { wait } from './testUtils'; +import { onChangedDocument, wait, retryUntilDocumentChanges } from './testUtils'; -export async function acceptFirstSuggestion(uri: vscode.Uri, _disposables: vscode.Disposable[], options?: { useLineRange?: boolean }) { - const didChangeDocument = onChangedDocument(uri, _disposables); - const didSuggest = onDidSuggest(_disposables, options); - await vscode.commands.executeCommand('editor.action.triggerSuggest'); - await didSuggest; - // TODO: depends on reverting fix for https://github.com/Microsoft/vscode/issues/64257 - // Make sure we have time to resolve the suggestion because `acceptSelectedSuggestion` doesn't - await wait(40); - await vscode.commands.executeCommand('acceptSelectedSuggestion'); - return await didChangeDocument; +export async function acceptFirstSuggestion(uri: vscode.Uri, _disposables: vscode.Disposable[]) { + return retryUntilDocumentChanges(uri, { retries: 10, timeout: 0 }, _disposables, async () => { + await vscode.commands.executeCommand('editor.action.triggerSuggest'); + await wait(1000); + await vscode.commands.executeCommand('acceptSelectedSuggestion'); + }); } export async function typeCommitCharacter(uri: vscode.Uri, character: string, _disposables: vscode.Disposable[]) { const didChangeDocument = onChangedDocument(uri, _disposables); - const didSuggest = onDidSuggest(_disposables); await vscode.commands.executeCommand('editor.action.triggerSuggest'); - await didSuggest; + await wait(3000); // Give time for suggestions to show await vscode.commands.executeCommand('type', { text: character }); return await didChangeDocument; } - -export function onChangedDocument(documentUri: vscode.Uri, disposables: vscode.Disposable[]) { - return new Promise<vscode.TextDocument>(resolve => vscode.workspace.onDidChangeTextDocument(e => { - if (e.document.uri.toString() === documentUri.toString()) { - resolve(e.document); - } - }, undefined, disposables)); -} - - -function onDidSuggest(disposables: vscode.Disposable[], options?: { useLineRange?: boolean }) { - return new Promise(resolve => - disposables.push(vscode.languages.registerCompletionItemProvider('typescript', new class implements vscode.CompletionItemProvider { - provideCompletionItems(doc: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> { - // Return a fake item that will come first - const range = options && options.useLineRange - ? new vscode.Range(new vscode.Position(position.line, 0), position) - : doc.getWordRangeAtPosition(position.translate({ characterDelta: -1 })); - return [{ - label: '🦄', - insertText: doc.getText(range), - filterText: doc.getText(range), - preselect: true, - sortText: 'a', - range: range - }]; - } - async resolveCompletionItem(item: vscode.CompletionItem) { - await vscode.commands.executeCommand('selectNextSuggestion'); - resolve(); - return item; - } - }))); -} diff --git a/extensions/typescript-language-features/src/test/testUtils.ts b/extensions/typescript-language-features/src/test/testUtils.ts index 753814a99bd..c0ba9a47730 100644 --- a/extensions/typescript-language-features/src/test/testUtils.ts +++ b/extensions/typescript-language-features/src/test/testUtils.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import * as assert from 'assert'; import * as fs from 'fs'; import * as os from 'os'; import { join } from 'path'; +import * as vscode from 'vscode'; function rndName() { return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); @@ -67,17 +68,106 @@ export function withRandomFileEditor( }); } -export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +export const wait = (ms: number) => new Promise<void>(resolve => setTimeout(() => resolve(), ms)); -export const joinLines = (...args: string[]) => args.join('\n'); +export const joinLines = (...args: string[]) => args.join(os.platform() === 'win32' ? '\r\n' : '\n'); export async function createTestEditor(uri: vscode.Uri, ...lines: string[]) { const document = await vscode.workspace.openTextDocument(uri); - await vscode.window.showTextDocument(document); - const activeEditor = vscode.window.activeTextEditor; - if (!activeEditor) { - throw new Error('no active editor'); - } - - await activeEditor.insertSnippet(new vscode.SnippetString(joinLines(...lines)), new vscode.Range(0, 0, 1000, 0)); + const editor = await vscode.window.showTextDocument(document); + await editor.insertSnippet(new vscode.SnippetString(joinLines(...lines)), new vscode.Range(0, 0, 1000, 0)); + return editor; +} + +export function assertEditorContents(editor: vscode.TextEditor, expectedDocContent: string, message?: string): void { + const cursorIndex = expectedDocContent.indexOf(CURSOR); + + assert.strictEqual( + editor.document.getText(), + expectedDocContent.replace(CURSOR, ''), + message); + + if (cursorIndex >= 0) { + const expectedCursorPos = editor.document.positionAt(cursorIndex); + assert.deepEqual( + { line: editor.selection.active.line, character: editor.selection.active.line }, + { line: expectedCursorPos.line, character: expectedCursorPos.line }, + 'Cursor position' + ); + } +} + +export type VsCodeConfiguration = { [key: string]: any }; + +export async function updateConfig(documentUri: vscode.Uri, newConfig: VsCodeConfiguration): Promise<VsCodeConfiguration> { + const oldConfig: VsCodeConfiguration = {}; + const config = vscode.workspace.getConfiguration(undefined, documentUri); + + for (const configKey of Object.keys(newConfig)) { + oldConfig[configKey] = config.get(configKey); + await new Promise<void>((resolve, reject) => + config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global) + .then(() => resolve(), reject)); + } + return oldConfig; +} + +export const Config = Object.freeze({ + autoClosingBrackets: 'editor.autoClosingBrackets', + typescriptCompleteFunctionCalls: 'typescript.suggest.completeFunctionCalls', + insertMode: 'editor.suggest.insertMode', + snippetSuggestions: 'editor.snippetSuggestions', + suggestSelection: 'editor.suggestSelection', + javascriptQuoteStyle: 'javascript.preferences.quoteStyle', + typescriptQuoteStyle: 'typescript.preferences.quoteStyle', +} as const); + +export const insertModesValues = Object.freeze(['insert', 'replace']); + +export async function enumerateConfig( + documentUri: vscode.Uri, + configKey: string, + values: readonly string[], + f: (message: string) => Promise<void> +): Promise<void> { + for (const value of values) { + const newConfig = { [configKey]: value }; + await updateConfig(documentUri, newConfig); + await f(JSON.stringify(newConfig)); + } +} + + +export function onChangedDocument(documentUri: vscode.Uri, disposables: vscode.Disposable[]) { + return new Promise<vscode.TextDocument>(resolve => vscode.workspace.onDidChangeTextDocument(e => { + if (e.document.uri.toString() === documentUri.toString()) { + resolve(e.document); + } + }, undefined, disposables)); +} + +export async function retryUntilDocumentChanges( + documentUri: vscode.Uri, + options: { retries: number, timeout: number }, + disposables: vscode.Disposable[], + exec: () => Thenable<unknown>, +) { + const didChangeDocument = onChangedDocument(documentUri, disposables); + + let done = false; + + const result = await Promise.race([ + didChangeDocument, + (async () => { + for (let i = 0; i < options.retries; ++i) { + await wait(options.timeout); + if (done) { + return; + } + await exec(); + } + })(), + ]); + done = true; + return result; } diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts similarity index 86% rename from extensions/typescript-language-features/src/features/bufferSyncSupport.ts rename to extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts index 6a86599bcbe..40610b66f11 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import type * as Proto from '../protocol'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from '../utils/api'; import { coalesce } from '../utils/arrays'; import { Delayer } from '../utils/async'; @@ -68,11 +68,16 @@ type BufferOperation = CloseOperation | OpenOperation | ChangeOperation; */ class BufferSynchronizer { - private readonly _pending = new ResourceMap<BufferOperation>(); + private readonly _pending: ResourceMap<BufferOperation>; constructor( - private readonly client: ITypeScriptServiceClient - ) { } + private readonly client: ITypeScriptServiceClient, + onCaseInsenitiveFileSystem: boolean + ) { + this._pending = new ResourceMap<BufferOperation>(undefined, { + onCaseInsenitiveFileSystem + }); + } public open(resource: vscode.Uri, args: Proto.OpenRequestArgs) { if (this.supportsBatching) { @@ -82,12 +87,16 @@ class BufferSynchronizer { } } - public close(resource: vscode.Uri, filepath: string) { + /** + * @return Was the buffer open? + */ + public close(resource: vscode.Uri, filepath: string): boolean { if (this.supportsBatching) { - this.updatePending(resource, new CloseOperation(filepath)); + return this.updatePending(resource, new CloseOperation(filepath)); } else { const args: Proto.FileRequestArgs = { file: filepath }; this.client.executeWithoutWaitingForResponse('close', args); + return true; } } @@ -155,14 +164,14 @@ class BufferSynchronizer { return this.client.apiVersion.gte(API.v340); } - private updatePending(resource: vscode.Uri, op: BufferOperation): void { + private updatePending(resource: vscode.Uri, op: BufferOperation): boolean { switch (op.type) { case BufferOperationType.Close: const existing = this._pending.get(resource); switch (existing?.type) { case BufferOperationType.Open: this._pending.delete(resource); - return; // Open then close. No need to do anything + return false; // Open then close. No need to do anything } break; } @@ -172,6 +181,7 @@ class BufferSynchronizer { this.flush(); } this._pending.set(resource, op); + return true; } } @@ -232,9 +242,16 @@ class SyncedBuffer { } } - public close(): void { - this.synchronizer.close(this.resource, this.filepath); + /** + * @return Was the buffer open? + */ + public close(): boolean { + if (this.state !== BufferState.Open) { + this.state = BufferState.Closed; + return false; + } this.state = BufferState.Closed; + return this.synchronizer.close(this.resource, this.filepath); } public onContentChanged(events: readonly vscode.TextDocumentContentChangeEvent[]): void { @@ -263,7 +280,7 @@ class PendingDiagnostics extends ResourceMap<number> { .sort((a, b) => a.value - b.value) .map(entry => entry.resource); - const map = new ResourceMap<void>(); + const map = new ResourceMap<void>(undefined, this.config); for (const resource of orderedResources) { map.set(resource, undefined); } @@ -289,19 +306,28 @@ class GetErrRequest { public readonly files: ResourceMap<void>, onDone: () => void ) { - const args: Proto.GeterrRequestArgs = { - delay: 0, - files: coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource))) - }; + const allFiles = coalesce(Array.from(files.entries) + .filter(entry => client.hasCapabilityForResource(entry.resource, ClientCapability.Semantic)) + .map(entry => client.normalizedPath(entry.resource))); - client.executeAsync('geterr', args, this._token.token) - .finally(() => { + if (!allFiles.length || !client.capabilities.has(ClientCapability.Semantic)) { + this._done = true; + setImmediate(onDone); + } else { + const request = client.configuration.enableProjectDiagnostics + // Note that geterrForProject is almost certainly not the api we want here as it ends up computing far + // too many diagnostics + ? client.executeAsync('geterrForProject', { delay: 0, file: allFiles[0] }, this._token.token) + : client.executeAsync('geterr', { delay: 0, files: allFiles }, this._token.token); + + request.finally(() => { if (this._done) { return; } this._done = true; onDone(); }); + } } public cancel(): any { @@ -329,7 +355,8 @@ export default class BufferSyncSupport extends Disposable { constructor( client: ITypeScriptServiceClient, - modeIds: string[] + modeIds: readonly string[], + onCaseInsenitiveFileSystem: boolean ) { super(); this.client = client; @@ -338,9 +365,9 @@ export default class BufferSyncSupport extends Disposable { this.diagnosticDelayer = new Delayer<any>(300); const pathNormalizer = (path: vscode.Uri) => this.client.normalizedPath(path); - this.syncedBuffers = new SyncedBufferMap(pathNormalizer); - this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); - this.synchronizer = new BufferSynchronizer(client); + this.syncedBuffers = new SyncedBufferMap(pathNormalizer, { onCaseInsenitiveFileSystem }); + this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer, { onCaseInsenitiveFileSystem }); + this.synchronizer = new BufferSynchronizer(client, onCaseInsenitiveFileSystem); this.updateConfiguration(); vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); @@ -448,13 +475,17 @@ export default class BufferSyncSupport extends Disposable { this.pendingDiagnostics.delete(resource); this.pendingGetErr?.files.delete(resource); this.syncedBuffers.delete(resource); - syncedBuffer.close(); + const wasBufferOpen = syncedBuffer.close(); this._onDelete.fire(resource); - this.requestAllDiagnostics(); + if (wasBufferOpen) { + this.requestAllDiagnostics(); + } } public interuptGetErr<R>(f: () => R): R { - if (!this.pendingGetErr) { + if (!this.pendingGetErr + || this.client.configuration.enableProjectDiagnostics // `geterr` happens on seperate server so no need to cancel it. + ) { return f(); } @@ -501,7 +532,7 @@ export default class BufferSyncSupport extends Disposable { this.triggerDiagnostics(); } - public getErr(resources: vscode.Uri[]): any { + public getErr(resources: readonly vscode.Uri[]): any { const handledResources = resources.filter(resource => this.handles(resource)); if (!handledResources.length) { return; diff --git a/extensions/typescript-language-features/src/tsServer/cachedResponse.ts b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts index 2fb9b542281..baa1a87a88a 100644 --- a/extensions/typescript-language-features/src/tsServer/cachedResponse.ts +++ b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ServerResponse } from '../typescriptService'; type Resolve<T extends Proto.Response> = () => Promise<ServerResponse.Response<T>>; diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts index a5ac85388bb..4e0ec375ddc 100644 --- a/extensions/typescript-language-features/src/tsServer/callbackMap.ts +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ServerResponse } from '../typescriptService'; export interface CallbackItem<R> { diff --git a/extensions/typescript-language-features/src/tsServer/cancellation.electron.ts b/extensions/typescript-language-features/src/tsServer/cancellation.electron.ts new file mode 100644 index 00000000000..853ca0c1594 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/cancellation.electron.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { getTempFile } from '../utils/temp.electron'; +import Tracer from '../utils/tracer'; +import { OngoingRequestCanceller, OngoingRequestCancellerFactory } from './cancellation'; + +export class NodeRequestCanceller implements OngoingRequestCanceller { + public readonly cancellationPipeName: string; + + public constructor( + private readonly _serverId: string, + private readonly _tracer: Tracer, + ) { + this.cancellationPipeName = getTempFile('tscancellation'); + } + + public tryCancelOngoingRequest(seq: number): boolean { + if (!this.cancellationPipeName) { + return false; + } + this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`); + try { + fs.writeFileSync(this.cancellationPipeName + seq, ''); + } catch { + // noop + } + return true; + } +} + + +export const nodeRequestCancellerFactory = new class implements OngoingRequestCancellerFactory { + create(serverId: string, tracer: Tracer): OngoingRequestCanceller { + return new NodeRequestCanceller(serverId, tracer); + } +}; diff --git a/extensions/typescript-language-features/src/tsServer/cancellation.ts b/extensions/typescript-language-features/src/tsServer/cancellation.ts new file mode 100644 index 00000000000..0eda4e574dc --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/cancellation.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Tracer from '../utils/tracer'; + +export interface OngoingRequestCanceller { + readonly cancellationPipeName: string | undefined; + tryCancelOngoingRequest(seq: number): boolean; +} + +export interface OngoingRequestCancellerFactory { + create(serverId: string, tracer: Tracer): OngoingRequestCanceller; +} + +const noopRequestCanceller = new class implements OngoingRequestCanceller { + public readonly cancellationPipeName = undefined; + + public tryCancelOngoingRequest(_seq: number): boolean { + return false; + } +}; + +export const noopRequestCancellerFactory = new class implements OngoingRequestCancellerFactory { + create(_serverId: string, _tracer: Tracer): OngoingRequestCanceller { + return noopRequestCanceller; + } +}; diff --git a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts similarity index 84% rename from extensions/typescript-language-features/src/utils/logDirectoryProvider.ts rename to extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts index af6886e7043..60b6965b5a7 100644 --- a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts +++ b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts @@ -6,9 +6,10 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import { memoize } from './memoize'; +import { ILogDirectoryProvider } from './logDirectoryProvider'; +import { memoize } from '../utils/memoize'; -export default class LogDirectoryProvider { +export class NodeLogDirectoryProvider implements ILogDirectoryProvider { public constructor( private readonly context: vscode.ExtensionContext ) { } diff --git a/src/vs/platform/dialogs/node/dialogs.ts b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts similarity index 60% rename from src/vs/platform/dialogs/node/dialogs.ts rename to extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts index 754693da712..75ef2316309 100644 --- a/src/vs/platform/dialogs/node/dialogs.ts +++ b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; - -export interface INativeOpenDialogOptions { - forceNewWindow?: boolean; - - defaultPath?: string; - - telemetryEventName?: string; - telemetryExtraData?: ITelemetryData; +export interface ILogDirectoryProvider { + getNewLogDirectory(): string | undefined; } + +export const noopLogDirectoryProvider = new class implements ILogDirectoryProvider { + public getNewLogDirectory(): undefined { + return undefined; + } +}; diff --git a/extensions/typescript-language-features/src/tsServer/requestQueue.ts b/extensions/typescript-language-features/src/tsServer/requestQueue.ts index 33dceef83f2..4b75833a1e9 100644 --- a/extensions/typescript-language-features/src/tsServer/requestQueue.ts +++ b/extensions/typescript-language-features/src/tsServer/requestQueue.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; export enum RequestQueueingType { /** @@ -78,4 +78,4 @@ export class RequestQueue { arguments: args }; } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index b538083b7f7..d6294f2512d 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -3,58 +3,38 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as stream from 'stream'; import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { ServerResponse, TypeScriptRequests } from '../typescriptService'; +import type * as Proto from '../protocol'; +import { EventName } from '../protocol.const'; +import { CallbackMap } from '../tsServer/callbackMap'; +import { RequestItem, RequestQueue, RequestQueueingType } from '../tsServer/requestQueue'; +import { TypeScriptServerError } from '../tsServer/serverError'; +import { ServerResponse, ServerType, TypeScriptRequests } from '../typescriptService'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; import { TelemetryReporter } from '../utils/telemetry'; import Tracer from '../utils/tracer'; -import { TypeScriptVersion } from '../utils/versionProvider'; -import { Reader } from '../utils/wireProtocol'; -import { CallbackMap } from './callbackMap'; -import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue'; -import { TypeScriptServerError } from './serverError'; +import { OngoingRequestCanceller } from './cancellation'; +import { TypeScriptVersionManager } from './versionManager'; +import { TypeScriptVersion } from './versionProvider'; -export interface OngoingRequestCanceller { - tryCancelOngoingRequest(seq: number): boolean; -} - -export class PipeRequestCanceller implements OngoingRequestCanceller { - public constructor( - private readonly _serverId: string, - private readonly _cancellationPipeName: string | undefined, - private readonly _tracer: Tracer, - ) { } - - public tryCancelOngoingRequest(seq: number): boolean { - if (!this._cancellationPipeName) { - return false; - } - this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`); - try { - fs.writeFileSync(this._cancellationPipeName + seq, ''); - } catch { - // noop - } - return true; - } +export enum ExectuionTarget { + Semantic, + Syntax } export interface ITypeScriptServer { readonly onEvent: vscode.Event<Proto.Event>; readonly onExit: vscode.Event<any>; readonly onError: vscode.Event<any>; - readonly onReaderError: vscode.Event<Error>; readonly tsServerLogFile: string | undefined; kill(): void; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined; + executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExectuionTarget }): undefined; + executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>>; + executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>> | undefined; dispose(): void; } @@ -63,24 +43,41 @@ export interface TsServerDelegate { onFatalError(command: string, error: Error): void; } +export const enum TsServerProcessKind { + Main = 'main', + Syntax = 'syntax', + Semantic = 'semantic', + Diagnostics = 'diagnostics' +} + +export interface TsServerProcessFactory { + fork( + tsServerPath: string, + args: readonly string[], + kind: TsServerProcessKind, + configuration: TypeScriptServiceConfiguration, + versionManager: TypeScriptVersionManager, + ): TsServerProcess; +} + export interface TsServerProcess { - readonly stdout: stream.Readable; write(serverRequest: Proto.Request): void; - on(name: 'exit', handler: (code: number | null) => void): void; - on(name: 'error', handler: (error: Error) => void): void; + onData(handler: (data: Proto.Response) => void): void; + onExit(handler: (code: number | null) => void): void; + onError(handler: (error: Error) => void): void; kill(): void; } export class ProcessBasedTsServer extends Disposable implements ITypeScriptServer { - private readonly _reader: Reader<Proto.Response>; private readonly _requestQueue = new RequestQueue(); private readonly _callbacks = new CallbackMap<Proto.Response>(); private readonly _pendingResponses = new Set<number>(); constructor( private readonly _serverId: string, + private readonly _serverSource: ServerType, private readonly _process: TsServerProcess, private readonly _tsServerLogFile: string | undefined, private readonly _requestCanceller: OngoingRequestCanceller, @@ -89,14 +86,17 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe private readonly _tracer: Tracer, ) { super(); - this._reader = this._register(new Reader<Proto.Response>(this._process.stdout!)); - this._reader.onData(msg => this.dispatchMessage(msg)); - this._process.on('exit', code => { + this._process.onData(msg => { + this.dispatchMessage(msg); + }); + + this._process.onExit(code => { this._onExit.fire(code); this._callbacks.destroy('server exited'); }); - this._process.on('error', error => { + + this._process.onError(error => { this._onError.fire(error); this._callbacks.destroy('server errored'); }); @@ -111,8 +111,6 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe private readonly _onError = this._register(new vscode.EventEmitter<any>()); public readonly onError = this._onError.event; - public get onReaderError() { return this._reader.onError; } - public get tsServerLogFile() { return this._tsServerLogFile; } private write(serverRequest: Proto.Request) { @@ -133,7 +131,14 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe try { switch (message.type) { case 'response': - this.dispatchResponse(message as Proto.Response); + if (this._serverSource) { + this.dispatchResponse({ + ...(message as Proto.Response), + _serverType: this._serverSource + }); + } else { + this.dispatchResponse(message as Proto.Response); + } break; case 'event': @@ -197,9 +202,9 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe } } - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined { + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExectuionTarget }): undefined; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>>; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>> | undefined { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request, @@ -210,7 +215,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe let result: Promise<ServerResponse.Response<Proto.Response>> | undefined; if (executeInfo.expectsResult) { result = new Promise<ServerResponse.Response<Proto.Response>>((resolve, reject) => { - this._callbacks.add(request.seq, { onSuccess: resolve, onError: reject, queuingStartTime: Date.now(), isAsync: executeInfo.isAsync }, executeInfo.isAsync); + this._callbacks.add(request.seq, { onSuccess: resolve as () => ServerResponse.Response<Proto.Response> | undefined, onError: reject, queuingStartTime: Date.now(), isAsync: executeInfo.isAsync }, executeInfo.isAsync); if (executeInfo.token) { executeInfo.token.onCancellationRequested(() => { @@ -297,22 +302,146 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe } -export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServer { +interface ExecuteInfo { + readonly isAsync: boolean; + readonly token?: vscode.CancellationToken; + readonly expectsResult: boolean; + readonly lowPriority?: boolean; + readonly executionTarget?: ExectuionTarget; +} + +class RequestRouter { + + private static readonly sharedCommands = new Set<keyof TypeScriptRequests>([ + 'change', + 'close', + 'open', + 'updateOpen', + 'configure', + ]); + + constructor( + private readonly servers: ReadonlyArray<{ + readonly server: ITypeScriptServer; + canRun?(command: keyof TypeScriptRequests, executeInfo: ExecuteInfo): void; + }>, + private readonly delegate: TsServerDelegate, + ) { } + + public execute(command: keyof TypeScriptRequests, args: any, executeInfo: ExecuteInfo): Promise<ServerResponse.Response<Proto.Response>> | undefined { + if (RequestRouter.sharedCommands.has(command) && typeof executeInfo.executionTarget === 'undefined') { + // Dispatch shared commands to all servers but only return from first one + + const requestStates: RequestState.State[] = this.servers.map(() => RequestState.Unresolved); + + // Also make sure we never cancel requests to just one server + let token: vscode.CancellationToken | undefined = undefined; + if (executeInfo.token) { + const source = new vscode.CancellationTokenSource(); + executeInfo.token.onCancellationRequested(() => { + if (requestStates.some(state => state === RequestState.Resolved)) { + // Don't cancel. + // One of the servers completed this request so we don't want to leave the other + // in a different state. + return; + } + source.cancel(); + }); + token = source.token; + } + + let firstRequest: Promise<ServerResponse.Response<Proto.Response>> | undefined; + + for (let serverIndex = 0; serverIndex < this.servers.length; ++serverIndex) { + const server = this.servers[serverIndex].server; + + const request = server.executeImpl(command, args, { ...executeInfo, token }); + if (serverIndex === 0) { + firstRequest = request; + } + if (request) { + request + .then(result => { + requestStates[serverIndex] = RequestState.Resolved; + const erroredRequest = requestStates.find(state => state.type === RequestState.Type.Errored) as RequestState.Errored | undefined; + if (erroredRequest) { + // We've gone out of sync + this.delegate.onFatalError(command, erroredRequest.err); + } + return result; + }, err => { + requestStates[serverIndex] = new RequestState.Errored(err); + if (requestStates.some(state => state === RequestState.Resolved)) { + // We've gone out of sync + this.delegate.onFatalError(command, err); + } + throw err; + }); + } + } + + return firstRequest; + } + + for (const { canRun, server } of this.servers) { + if (!canRun || canRun(command, executeInfo)) { + return server.executeImpl(command, args, executeInfo); + } + } + + throw new Error(`Could not find server for command: '${command}'`); + } +} + +export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServer { + + private static readonly diagnosticEvents = new Set<string>([ + EventName.configFileDiag, + EventName.syntaxDiag, + EventName.semanticDiag, + EventName.suggestionDiag + ]); + + private readonly getErrServer: ITypeScriptServer; + private readonly mainServer: ITypeScriptServer; + private readonly router: RequestRouter; + public constructor( - private readonly syntaxServer: ITypeScriptServer, - private readonly semanticServer: ITypeScriptServer, - private readonly _delegate: TsServerDelegate, + servers: { getErr: ITypeScriptServer, primary: ITypeScriptServer }, + delegate: TsServerDelegate, ) { super(); - this._register(syntaxServer.onEvent(e => this._onEvent.fire(e))); - this._register(semanticServer.onEvent(e => this._onEvent.fire(e))); + this.getErrServer = servers.getErr; + this.mainServer = servers.primary; - this._register(semanticServer.onExit(e => { - this._onExit.fire(e); - this.syntaxServer.kill(); + this.router = new RequestRouter( + [ + { server: this.getErrServer, canRun: (command) => ['geterr', 'geterrForProject'].includes(command) }, + { server: this.mainServer, canRun: undefined /* gets all other commands */ } + ], + delegate); + + this._register(this.getErrServer.onEvent(e => { + if (GetErrRoutingTsServer.diagnosticEvents.has(e.event)) { + this._onEvent.fire(e); + } + // Ignore all other events + })); + this._register(this.mainServer.onEvent(e => { + if (!GetErrRoutingTsServer.diagnosticEvents.has(e.event)) { + this._onEvent.fire(e); + } + // Ignore all other events + })); + + this._register(this.getErrServer.onError(e => this._onError.fire(e))); + this._register(this.mainServer.onError(e => this._onError.fire(e))); + + this._register(this.mainServer.onExit(e => { + this._onExit.fire(e); + this.getErrServer.kill(); })); - this._register(semanticServer.onError(e => this._onError.fire(e))); } private readonly _onEvent = this._register(new vscode.EventEmitter<Proto.Event>()); @@ -324,16 +453,27 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ private readonly _onError = this._register(new vscode.EventEmitter<any>()); public readonly onError = this._onError.event; - public get onReaderError() { return this.semanticServer.onReaderError; } - - public get tsServerLogFile() { return this.semanticServer.tsServerLogFile; } + public get tsServerLogFile() { return this.mainServer.tsServerLogFile; } public kill(): void { - this.syntaxServer.kill(); - this.semanticServer.kill(); + this.getErrServer.kill(); + this.mainServer.kill(); } - private static readonly syntaxCommands = new Set<keyof TypeScriptRequests>([ + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExectuionTarget }): undefined; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>>; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>> | undefined { + return this.router.execute(command, args, executeInfo); + } +} + + +export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServer { + + /** + * Commands that should always be run on the syntax server. + */ + private static readonly syntaxAlwaysCommands = new Set<keyof TypeScriptRequests>([ 'navtree', 'getOutliningSpans', 'jsxClosingTag', @@ -342,86 +482,130 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ 'formatonkey', 'docCommentTemplate', ]); - private static readonly sharedCommands = new Set<keyof TypeScriptRequests>([ - 'change', - 'close', - 'open', - 'updateOpen', - 'configure', + + /** + * Commands that should always be run on the semantic server. + */ + private static readonly semanticCommands = new Set<keyof TypeScriptRequests>([ + 'geterr', + 'geterrForProject', + 'projectInfo', 'configurePlugin', ]); - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined { - if (SyntaxRoutingTsServer.syntaxCommands.has(command)) { - return this.syntaxServer.executeImpl(command, args, executeInfo); - } else if (SyntaxRoutingTsServer.sharedCommands.has(command)) { - // Dispatch to both server but only return from syntax one + /** + * Commands that can be run on the syntax server but would benefit from being upgraded to the semantic server. + */ + private static readonly syntaxAllowedCommands = new Set<keyof TypeScriptRequests>([ + 'completions', + 'completionEntryDetails', + 'completionInfo', + 'definition', + 'definitionAndBoundSpan', + 'documentHighlights', + 'implementation', + 'navto', + 'quickinfo', + 'references', + 'rename', + 'signatureHelp', + ]); - let syntaxRequestState: RequestState.State = RequestState.Unresolved; - let semanticRequestState: RequestState.State = RequestState.Unresolved; + private readonly syntaxServer: ITypeScriptServer; + private readonly semanticServer: ITypeScriptServer; + private readonly router: RequestRouter; - // Also make sure we never cancel requests to just one server - let token: vscode.CancellationToken | undefined = undefined; - if (executeInfo.token) { - const source = new vscode.CancellationTokenSource(); - executeInfo.token.onCancellationRequested(() => { - if (syntaxRequestState !== RequestState.Unresolved && semanticRequestState === RequestState.Unresolved - || syntaxRequestState === RequestState.Unresolved && semanticRequestState !== RequestState.Unresolved - ) { - // Don't cancel. - // One of the servers completed this request so we don't want to leave the other - // in a different state - return; + private _projectLoading = true; + + public constructor( + servers: { syntax: ITypeScriptServer, semantic: ITypeScriptServer }, + delegate: TsServerDelegate, + enableDynamicRouting: boolean, + ) { + super(); + + this.syntaxServer = servers.syntax; + this.semanticServer = servers.semantic; + + this.router = new RequestRouter( + [ + { + server: this.syntaxServer, + canRun: (command, execInfo) => { + switch (execInfo.executionTarget) { + case ExectuionTarget.Semantic: return false; + case ExectuionTarget.Syntax: return true; + } + + if (SyntaxRoutingTsServer.syntaxAlwaysCommands.has(command)) { + return true; + } + if (SyntaxRoutingTsServer.semanticCommands.has(command)) { + return false; + } + if (enableDynamicRouting && this.projectLoading && SyntaxRoutingTsServer.syntaxAllowedCommands.has(command)) { + return true; + } + return false; } - source.cancel(); - }); - token = source.token; - } + }, { + server: this.semanticServer, + canRun: undefined /* gets all other commands */ + } + ], + delegate); - const semanticRequest = this.semanticServer.executeImpl(command, args, { ...executeInfo, token }); - if (semanticRequest) { - semanticRequest - .then(result => { - semanticRequestState = RequestState.Resolved; - if (syntaxRequestState.type === RequestState.Type.Errored) { - // We've gone out of sync - this._delegate.onFatalError(command, syntaxRequestState.err); - } - return result; - }, err => { - semanticRequestState = new RequestState.Errored(err); - if (syntaxRequestState === RequestState.Resolved) { - // We've gone out of sync - this._delegate.onFatalError(command, err); - } - throw err; - }); + this._register(this.syntaxServer.onEvent(e => { + return this._onEvent.fire(e); + })); + + this._register(this.semanticServer.onEvent(e => { + switch (e.event) { + case EventName.projectLoadingStart: + this._projectLoading = true; + break; + + case EventName.projectLoadingFinish: + case EventName.semanticDiag: + case EventName.syntaxDiag: + case EventName.suggestionDiag: + case EventName.configFileDiag: + this._projectLoading = false; + break; } - const syntaxRequest = this.syntaxServer.executeImpl(command, args, { ...executeInfo, token }); - if (syntaxRequest) { - syntaxRequest - .then(result => { - syntaxRequestState = RequestState.Resolved; - if (semanticRequestState.type === RequestState.Type.Errored) { - // We've gone out of sync - this._delegate.onFatalError(command, semanticRequestState.err); - } - return result; - }, err => { - syntaxRequestState = new RequestState.Errored(err); - if (semanticRequestState === RequestState.Resolved) { - // We've gone out of sync - this._delegate.onFatalError(command, err); - } - throw err; - }); - } - return syntaxRequest; - } else { - return this.semanticServer.executeImpl(command, args, executeInfo); - } + return this._onEvent.fire(e); + })); + + this._register(this.semanticServer.onExit(e => { + this._onExit.fire(e); + this.syntaxServer.kill(); + })); + + this._register(this.semanticServer.onError(e => this._onError.fire(e))); + } + + private get projectLoading() { return this._projectLoading; } + + private readonly _onEvent = this._register(new vscode.EventEmitter<Proto.Event>()); + public readonly onEvent = this._onEvent.event; + + private readonly _onExit = this._register(new vscode.EventEmitter<any>()); + public readonly onExit = this._onExit.event; + + private readonly _onError = this._register(new vscode.EventEmitter<any>()); + public readonly onError = this._onError.event; + + public get tsServerLogFile() { return this.semanticServer.tsServerLogFile; } + + public kill(): void { + this.syntaxServer.kill(); + this.semanticServer.kill(); + } + + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExectuionTarget }): undefined; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>>; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise<ServerResponse.Response<Proto.Response>> | undefined { + return this.router.execute(command, args, executeInfo); } } diff --git a/extensions/typescript-language-features/src/tsServer/serverError.ts b/extensions/typescript-language-features/src/tsServer/serverError.ts index 5c844b5f4da..2653360c9be 100644 --- a/extensions/typescript-language-features/src/tsServer/serverError.ts +++ b/extensions/typescript-language-features/src/tsServer/serverError.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Proto from '../protocol'; -import { escapeRegExp } from '../utils/regexp'; -import { TypeScriptVersion } from '../utils/versionProvider'; +import type * as Proto from '../protocol'; +import { TypeScriptVersion } from './versionProvider'; export class TypeScriptServerError extends Error { @@ -14,16 +13,17 @@ export class TypeScriptServerError extends Error { version: TypeScriptVersion, response: Proto.Response ): TypeScriptServerError { - const parsedResult = TypeScriptServerError.parseErrorText(version, response); - return new TypeScriptServerError(serverId, version, response, parsedResult?.message, parsedResult?.stack); + const parsedResult = TypeScriptServerError.parseErrorText(response); + return new TypeScriptServerError(serverId, version, response, parsedResult?.message, parsedResult?.stack, parsedResult?.sanitizedStack); } private constructor( - serverId: string, - version: TypeScriptVersion, + public readonly serverId: string, + public readonly version: TypeScriptVersion, private readonly response: Proto.Response, public readonly serverMessage: string | undefined, - public readonly serverStack: string | undefined + public readonly serverStack: string | undefined, + private readonly sanitizedStack: string | undefined ) { super(`<${serverId}> TypeScript Server Error (${version.displayName})\n${serverMessage}\n${serverStack}`); } @@ -33,19 +33,19 @@ export class TypeScriptServerError extends Error { public get serverCommand() { return this.response.command; } public get telemetry() { + // The "sanitizedstack" has been purged of error messages, paths, and file names (other than tsserver) + // and, thus, can be classified as SystemMetaData, rather than CallstackOrException. /* __GDPR__FRAGMENT__ "TypeScriptRequestErrorProperties" : { "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } + "serverid" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "sanitizedstack" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ return { command: this.serverCommand, - message: this.serverMessage || '', - stack: this.serverStack || '', - errortext: this.serverErrorText || '', + serverid: this.serverId, + sanitizedstack: this.sanitizedStack || '', } as const; } @@ -53,27 +53,20 @@ export class TypeScriptServerError extends Error { * Given a `errorText` from a tsserver request indicating failure in handling a request, * prepares a payload for telemetry-logging. */ - private static parseErrorText(version: TypeScriptVersion, response: Proto.Response) { + private static parseErrorText(response: Proto.Response) { const errorText = response.message; if (errorText) { const errorPrefix = 'Error processing request. '; if (errorText.startsWith(errorPrefix)) { - let prefixFreeErrorText = errorText.substr(errorPrefix.length); - - // Prior to https://github.com/microsoft/TypeScript/pull/32785, this error - // returned and excessively long and detailed list of paths. Since server-side - // filtering doesn't have sufficient granularity to drop these specific - // messages, we sanitize them here. - if (prefixFreeErrorText.indexOf('Could not find sourceFile') >= 0) { - prefixFreeErrorText = prefixFreeErrorText.replace(/ in \[[^\]]*\]/g, ''); - } - + const prefixFreeErrorText = errorText.substr(errorPrefix.length); const newlineIndex = prefixFreeErrorText.indexOf('\n'); if (newlineIndex >= 0) { // Newline expected between message and stack. + const stack = prefixFreeErrorText.substring(newlineIndex + 1); return { message: prefixFreeErrorText.substring(0, newlineIndex), - stack: TypeScriptServerError.normalizeMessageStack(version, prefixFreeErrorText.substring(newlineIndex + 1)) + stack, + sanitizedStack: TypeScriptServerError.sanitizeStack(stack) }; } } @@ -82,12 +75,23 @@ export class TypeScriptServerError extends Error { } /** - * Try to replace full TS Server paths with 'tsserver.js' so that we don't have to post process the data as much + * Drop everything but ".js" and line/column numbers (though retain "tsserver" if that's the filename). */ - private static normalizeMessageStack(version: TypeScriptVersion, message: string | undefined) { + private static sanitizeStack(message: string | undefined) { if (!message) { return ''; } - return message.replace(new RegExp(`${escapeRegExp(version.path)}[/\\\\]tsserver.js:`, 'gi'), 'tsserver.js:'); + const regex = /(\btsserver)?(\.(?:ts|tsx|js|jsx)(?::\d+(?::\d+)?)?)\)?$/igm; + let serverStack = ''; + while (true) { + const match = regex.exec(message); + if (!match) { + break; + } + // [1] is 'tsserver' or undefined + // [2] is '.js:{line_number}:{column_number}' + serverStack += `${match[1] || 'suppressed'}${match[2]}\n`; + } + return serverStack; } } diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts new file mode 100644 index 00000000000..bc06d18ee35 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as Proto from '../protocol'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { TsServerProcess, TsServerProcessKind } from './server'; + +declare const Worker: any; +declare type Worker = any; + +export class WorkerServerProcess implements TsServerProcess { + + public static fork( + tsServerPath: string, + args: readonly string[], + _kind: TsServerProcessKind, + _configuration: TypeScriptServiceConfiguration, + ) { + const worker = new Worker(tsServerPath); + return new WorkerServerProcess(worker, [ + ...args, + + // Explicitly give TS Server its path so it can + // load local resources + '--executingFilePath', tsServerPath, + ]); + } + + private _onDataHandlers = new Set<(data: Proto.Response) => void>(); + private _onErrorHandlers = new Set<(err: Error) => void>(); + private _onExitHandlers = new Set<(code: number | null) => void>(); + + public constructor( + private readonly worker: Worker, + args: readonly string[], + ) { + worker.addEventListener('message', (msg: any) => { + for (const handler of this._onDataHandlers) { + handler(msg.data); + } + }); + worker.postMessage(args); + } + + write(serverRequest: Proto.Request): void { + this.worker.postMessage(serverRequest); + } + + onData(handler: (response: Proto.Response) => void): void { + this._onDataHandlers.add(handler); + } + + onError(handler: (err: Error) => void): void { + this._onErrorHandlers.add(handler); + // Todo: not implemented + } + + onExit(handler: (code: number | null) => void): void { + this._onExitHandlers.add(handler); + // Todo: not implemented + } + + kill(): void { + this.worker.terminate(); + } +} diff --git a/extensions/typescript-language-features/src/utils/wireProtocol.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts similarity index 50% rename from extensions/typescript-language-features/src/utils/wireProtocol.ts rename to extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts index 64450f14280..96b0c6b411e 100644 --- a/extensions/typescript-language-features/src/utils/wireProtocol.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts @@ -3,9 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as stream from 'stream'; +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import type { Readable } from 'stream'; import * as vscode from 'vscode'; -import { Disposable } from './dispose'; +import * as nls from 'vscode-nls'; +import type * as Proto from '../protocol'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Disposable } from '../utils/dispose'; +import { TsServerProcess, TsServerProcessKind } from './server'; +import { TypeScriptVersionManager } from './versionManager'; + +const localize = nls.loadMessageBundle(); const defaultSize: number = 8192; const contentLength: string = 'Content-Length: '; @@ -80,12 +90,12 @@ class ProtocolBuffer { } } -export class Reader<T> extends Disposable { +class Reader<T> extends Disposable { private readonly buffer: ProtocolBuffer = new ProtocolBuffer(); private nextMessageLength: number = -1; - public constructor(readable: stream.Readable) { + public constructor(readable: Readable) { super(); readable.on('data', data => this.onLengthData(data)); } @@ -123,3 +133,110 @@ export class Reader<T> extends Disposable { } } } + +export class ChildServerProcess extends Disposable implements TsServerProcess { + private readonly _reader: Reader<Proto.Response>; + + public static fork( + tsServerPath: string, + args: readonly string[], + kind: TsServerProcessKind, + configuration: TypeScriptServiceConfiguration, + versionManager: TypeScriptVersionManager, + ): ChildServerProcess { + if (!fs.existsSync(tsServerPath)) { + vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', tsServerPath)); + versionManager.reset(); + tsServerPath = versionManager.currentVersion.tsServerPath; + } + + const childProcess = child_process.fork(tsServerPath, args, { + silent: true, + cwd: undefined, + env: this.generatePatchedEnv(process.env, tsServerPath), + execArgv: this.getExecArgv(kind, configuration), + }); + + return new ChildServerProcess(childProcess); + } + + private static generatePatchedEnv(env: any, modulePath: string): any { + const newEnv = Object.assign({}, env); + + newEnv['ELECTRON_RUN_AS_NODE'] = '1'; + newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..'); + + // Ensure we always have a PATH set + newEnv['PATH'] = newEnv['PATH'] || process.env.PATH; + + return newEnv; + } + + private static getExecArgv(kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration): string[] { + const args: string[] = []; + + const debugPort = this.getDebugPort(kind); + if (debugPort) { + const inspectFlag = ChildServerProcess.getTssDebugBrk() ? '--inspect-brk' : '--inspect'; + args.push(`${inspectFlag}=${debugPort}`); + } + + if (configuration.maxTsServerMemory) { + args.push(`--max-old-space-size=${configuration.maxTsServerMemory}`); + } + + return args; + } + + private static getDebugPort(kind: TsServerProcessKind): number | undefined { + if (kind === TsServerProcessKind.Syntax) { + // We typically only want to debug the main semantic server + return undefined; + } + const value = ChildServerProcess.getTssDebugBrk() || ChildServerProcess.getTssDebug(); + if (value) { + const port = parseInt(value); + if (!isNaN(port)) { + return port; + } + } + return undefined; + } + + private static getTssDebug(): string | undefined { + return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG' : 'TSS_DEBUG']; + } + + private static getTssDebugBrk(): string | undefined { + return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK']; + } + + private constructor( + private readonly _process: child_process.ChildProcess, + ) { + super(); + this._reader = this._register(new Reader<Proto.Response>(this._process.stdout!)); + } + + write(serverRequest: Proto.Request): void { + this._process.stdin!.write(JSON.stringify(serverRequest) + '\r\n', 'utf8'); + } + + onData(handler: (data: Proto.Response) => void): void { + this._reader.onData(handler); + } + + onExit(handler: (code: number | null) => void): void { + this._process.on('exit', handler); + } + + onError(handler: (err: Error) => void): void { + this._process.on('error', handler); + this._reader.onError(handler); + } + + kill(): void { + this._process.kill(); + this._reader.dispose(); + } +} diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index dfd71edfc51..a3ad0abb1c7 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -3,66 +3,132 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as child_process from 'child_process'; import * as path from 'path'; -import * as stream from 'stream'; import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import { OngoingRequestCancellerFactory } from '../tsServer/cancellation'; +import { ClientCapabilities, ClientCapability, ServerType } from '../typescriptService'; import API from '../utils/api'; -import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; -import * as electron from '../utils/electron'; -import LogDirectoryProvider from '../utils/logDirectoryProvider'; -import Logger from '../utils/logger'; +import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Logger } from '../utils/logger'; +import { isWeb } from '../utils/platform'; import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; import { PluginManager } from '../utils/plugins'; import { TelemetryReporter } from '../utils/telemetry'; import Tracer from '../utils/tracer'; -import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; -import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess, TsServerDelegate } from './server'; +import { ILogDirectoryProvider } from './logDirectoryProvider'; +import { GetErrRoutingTsServer, ITypeScriptServer, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerDelegate, TsServerProcessFactory, TsServerProcessKind } from './server'; +import { TypeScriptVersionManager } from './versionManager'; +import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider'; -type ServerKind = 'main' | 'syntax' | 'semantic'; +const enum CompositeServerType { + /** Run a single server that handles all commands */ + Single, + + /** Run a separate server for syntax commands */ + SeparateSyntax, + + /** Use a separate syntax server while the project is loading */ + DynamicSeparateSyntax, + + /** Only enable the syntax server */ + SyntaxOnly +} export class TypeScriptServerSpawner { public constructor( - private readonly _versionProvider: TypeScriptVersionProvider, - private readonly _logDirectoryProvider: LogDirectoryProvider, + private readonly _versionProvider: ITypeScriptVersionProvider, + private readonly _versionManager: TypeScriptVersionManager, + private readonly _logDirectoryProvider: ILogDirectoryProvider, private readonly _pluginPathsProvider: TypeScriptPluginPathsProvider, private readonly _logger: Logger, private readonly _telemetryReporter: TelemetryReporter, private readonly _tracer: Tracer, + private readonly _factory: TsServerProcessFactory, ) { } public spawn( version: TypeScriptVersion, + capabilities: ClientCapabilities, configuration: TypeScriptServiceConfiguration, pluginManager: PluginManager, + cancellerFactory: OngoingRequestCancellerFactory, delegate: TsServerDelegate, ): ITypeScriptServer { - if (this.shouldUseSeparateSyntaxServer(version, configuration)) { - const syntaxServer = this.spawnTsServer('syntax', version, configuration, pluginManager); - const semanticServer = this.spawnTsServer('semantic', version, configuration, pluginManager); - return new SyntaxRoutingTsServer(syntaxServer, semanticServer, delegate); + let primaryServer: ITypeScriptServer; + const serverType = this.getCompositeServerType(version, capabilities, configuration); + switch (serverType) { + case CompositeServerType.SeparateSyntax: + case CompositeServerType.DynamicSeparateSyntax: + { + const enableDynamicRouting = serverType === CompositeServerType.DynamicSeparateSyntax; + primaryServer = new SyntaxRoutingTsServer({ + syntax: this.spawnTsServer(TsServerProcessKind.Syntax, version, configuration, pluginManager, cancellerFactory), + semantic: this.spawnTsServer(TsServerProcessKind.Semantic, version, configuration, pluginManager, cancellerFactory), + }, delegate, enableDynamicRouting); + break; + } + case CompositeServerType.Single: + { + primaryServer = this.spawnTsServer(TsServerProcessKind.Main, version, configuration, pluginManager, cancellerFactory); + break; + } + case CompositeServerType.SyntaxOnly: + { + primaryServer = this.spawnTsServer(TsServerProcessKind.Syntax, version, configuration, pluginManager, cancellerFactory); + break; + } } - return this.spawnTsServer('main', version, configuration, pluginManager); + if (this.shouldUseSeparateDiagnosticsServer(configuration)) { + return new GetErrRoutingTsServer({ + getErr: this.spawnTsServer(TsServerProcessKind.Diagnostics, version, configuration, pluginManager, cancellerFactory), + primary: primaryServer, + }, delegate); + } + + return primaryServer; } - private shouldUseSeparateSyntaxServer( + private getCompositeServerType( version: TypeScriptVersion, + capabilities: ClientCapabilities, + configuration: TypeScriptServiceConfiguration, + ): CompositeServerType { + if (!capabilities.has(ClientCapability.Semantic)) { + return CompositeServerType.SyntaxOnly; + } + + switch (configuration.separateSyntaxServer) { + case SeparateSyntaxServerConfiguration.Disabled: + return CompositeServerType.Single; + + case SeparateSyntaxServerConfiguration.Enabled: + if (version.apiVersion?.gte(API.v340)) { + return version.apiVersion?.gte(API.v400) + ? CompositeServerType.DynamicSeparateSyntax + : CompositeServerType.SeparateSyntax; + } + return CompositeServerType.Single; + } + } + + private shouldUseSeparateDiagnosticsServer( configuration: TypeScriptServiceConfiguration, ): boolean { - return configuration.useSeparateSyntaxServer && !!version.apiVersion && version.apiVersion.gte(API.v340); + return configuration.enableProjectDiagnostics; } private spawnTsServer( - kind: ServerKind, + kind: TsServerProcessKind, version: TypeScriptVersion, configuration: TypeScriptServiceConfiguration, pluginManager: PluginManager, + cancellerFactory: OngoingRequestCancellerFactory, ): ITypeScriptServer { const apiVersion = version.apiVersion || API.defaultVersion; - const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager); + const canceller = cancellerFactory.create(kind, this._tracer); + const { args, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager, canceller.cancellationPipeName); if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { if (tsServerLogFile) { @@ -73,42 +139,50 @@ export class TypeScriptServerSpawner { } this._logger.info(`<${kind}> Forking...`); - const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions(kind, configuration)); + const process = this._factory.fork(version.tsServerPath, args, kind, configuration, this._versionManager); this._logger.info(`<${kind}> Starting...`); return new ProcessBasedTsServer( kind, - new ChildServerProcess(childProcess), + this.kindToServerType(kind), + process!, tsServerLogFile, - new PipeRequestCanceller(kind, cancellationPipeName, this._tracer), + canceller, version, this._telemetryReporter, this._tracer); } - private getForkOptions(kind: ServerKind, configuration: TypeScriptServiceConfiguration) { - const debugPort = TypeScriptServerSpawner.getDebugPort(kind); - const tsServerForkOptions: electron.ForkOptions = { - execArgv: [ - ...(debugPort ? [`--inspect=${debugPort}`] : []), - ...(configuration.maxTsServerMemory ? [`--max-old-space-size=${configuration.maxTsServerMemory}`] : []) - ] - }; - return tsServerForkOptions; + private kindToServerType(kind: TsServerProcessKind): ServerType { + switch (kind) { + case TsServerProcessKind.Syntax: + return ServerType.Syntax; + + case TsServerProcessKind.Main: + case TsServerProcessKind.Semantic: + case TsServerProcessKind.Diagnostics: + default: + return ServerType.Semantic; + } } private getTsServerArgs( - kind: ServerKind, + kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration, currentVersion: TypeScriptVersion, apiVersion: API, pluginManager: PluginManager, - ): { args: string[], cancellationPipeName: string, tsServerLogFile: string | undefined } { + cancellationPipeName: string | undefined, + ): { args: string[], tsServerLogFile: string | undefined } { const args: string[] = []; let tsServerLogFile: string | undefined; - if (kind === 'syntax') { - args.push('--syntaxOnly'); + if (kind === TsServerProcessKind.Syntax) { + if (apiVersion.gte(API.v401)) { + args.push('--serverMode', 'partialSemantic'); + } else { + args.push('--syntaxOnly'); + } } if (apiVersion.gte(API.v250)) { @@ -117,41 +191,48 @@ export class TypeScriptServerSpawner { args.push('--useSingleInferredProject'); } - if (configuration.disableAutomaticTypeAcquisition || kind === 'syntax') { + if (configuration.disableAutomaticTypeAcquisition || kind === TsServerProcessKind.Syntax || kind === TsServerProcessKind.Diagnostics) { args.push('--disableAutomaticTypingAcquisition'); } - if (kind !== 'syntax') { + if (kind === TsServerProcessKind.Semantic || kind === TsServerProcessKind.Main) { args.push('--enableTelemetry'); } - const cancellationPipeName = electron.getTempFile('tscancellation'); - args.push('--cancellationPipeName', cancellationPipeName + '*'); - - if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { - const logDir = this._logDirectoryProvider.getNewLogDirectory(); - if (logDir) { - tsServerLogFile = path.join(logDir, `tsserver.log`); - args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel)); - args.push('--logFile', tsServerLogFile); - } + if (cancellationPipeName) { + args.push('--cancellationPipeName', cancellationPipeName + '*'); } - const pluginPaths = this._pluginPathsProvider.getPluginPaths(); - - if (pluginManager.plugins.length) { - args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); - - const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; - for (const plugin of pluginManager.plugins) { - if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { - pluginPaths.push(plugin.path); + if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { + if (isWeb()) { + args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel)); + } else { + const logDir = this._logDirectoryProvider.getNewLogDirectory(); + if (logDir) { + tsServerLogFile = path.join(logDir, `tsserver.log`); + args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel)); + args.push('--logFile', tsServerLogFile); } } } - if (pluginPaths.length !== 0) { - args.push('--pluginProbeLocations', pluginPaths.join(',')); + if (!isWeb()) { + const pluginPaths = this._pluginPathsProvider.getPluginPaths(); + + if (pluginManager.plugins.length) { + args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); + + const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; + for (const plugin of pluginManager.plugins) { + if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { + pluginPaths.push(plugin.path); + } + } + } + + if (pluginPaths.length !== 0) { + args.push('--pluginProbeLocations', pluginPaths.join(',')); + } } if (configuration.npmLocation) { @@ -170,22 +251,7 @@ export class TypeScriptServerSpawner { args.push('--validateDefaultNpmLocation'); } - return { args, cancellationPipeName, tsServerLogFile }; - } - - private static getDebugPort(kind: ServerKind): number | undefined { - if (kind === 'syntax') { - // We typically only want to debug the main semantic server - return undefined; - } - const value = process.env['TSS_DEBUG']; - if (value) { - const port = parseInt(value); - if (!isNaN(port)) { - return port; - } - } - return undefined; + return { args, tsServerLogFile }; } private static isLoggingEnabled(configuration: TypeScriptServiceConfiguration) { @@ -199,25 +265,3 @@ export class TypeScriptServerSpawner { } } -class ChildServerProcess implements TsServerProcess { - - public constructor( - private readonly _process: child_process.ChildProcess, - ) { } - - get stdout(): stream.Readable { return this._process.stdout!; } - - write(serverRequest: Proto.Request): void { - this._process.stdin!.write(JSON.stringify(serverRequest) + '\r\n', 'utf8'); - } - - on(name: 'exit', handler: (code: number | null) => void): void; - on(name: 'error', handler: (error: Error) => void): void; - on(name: any, handler: any) { - this._process.on(name, handler); - } - - kill(): void { - this._process.kill(); - } -} diff --git a/extensions/typescript-language-features/src/tsServer/versionManager.ts b/extensions/typescript-language-features/src/tsServer/versionManager.ts new file mode 100644 index 00000000000..4811fefd3a7 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/versionManager.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Disposable } from '../utils/dispose'; +import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider'; + +const localize = nls.loadMessageBundle(); + +const useWorkspaceTsdkStorageKey = 'typescript.useWorkspaceTsdk'; +const suppressPromptWorkspaceTsdkStorageKey = 'typescript.suppressPromptWorkspaceTsdk'; + +interface QuickPickItem extends vscode.QuickPickItem { + run(): void; +} + +export class TypeScriptVersionManager extends Disposable { + + private _currentVersion: TypeScriptVersion; + + public constructor( + private configuration: TypeScriptServiceConfiguration, + private readonly versionProvider: ITypeScriptVersionProvider, + private readonly workspaceState: vscode.Memento + ) { + super(); + + this._currentVersion = this.versionProvider.defaultVersion; + + if (this.useWorkspaceTsdkSetting) { + const localVersion = this.versionProvider.localVersion; + if (localVersion) { + this._currentVersion = localVersion; + } + } + + if (this.isInPromptWorkspaceTsdkState(configuration)) { + setImmediate(() => { + this.promptUseWorkspaceTsdk(); + }); + } + + } + + private readonly _onDidPickNewVersion = this._register(new vscode.EventEmitter<void>()); + public readonly onDidPickNewVersion = this._onDidPickNewVersion.event; + + public updateConfiguration(nextConfiguration: TypeScriptServiceConfiguration) { + const lastConfiguration = this.configuration; + this.configuration = nextConfiguration; + + if ( + !this.isInPromptWorkspaceTsdkState(lastConfiguration) + && this.isInPromptWorkspaceTsdkState(nextConfiguration) + ) { + this.promptUseWorkspaceTsdk(); + } + } + + public get currentVersion(): TypeScriptVersion { + return this._currentVersion; + } + + public reset(): void { + this._currentVersion = this.versionProvider.bundledVersion; + } + + public async promptUserForVersion(): Promise<void> { + const selected = await vscode.window.showQuickPick<QuickPickItem>([ + this.getBundledPickItem(), + ...this.getLocalPickItems(), + LearnMorePickItem, + ], { + placeHolder: localize( + 'selectTsVersion', + "Select the TypeScript version used for JavaScript and TypeScript language features"), + }); + + return selected?.run(); + } + + private getBundledPickItem(): QuickPickItem { + const bundledVersion = this.versionProvider.defaultVersion; + return { + label: (!this.useWorkspaceTsdkSetting + ? '• ' + : '') + localize('useVSCodeVersionOption', "Use VS Code's Version"), + description: bundledVersion.displayName, + detail: bundledVersion.pathLabel, + run: async () => { + await this.workspaceState.update(useWorkspaceTsdkStorageKey, false); + this.updateActiveVersion(bundledVersion); + }, + }; + } + + private getLocalPickItems(): QuickPickItem[] { + return this.versionProvider.localVersions.map(version => { + return { + label: (this.useWorkspaceTsdkSetting && this.currentVersion.eq(version) + ? '• ' + : '') + localize('useWorkspaceVersionOption', "Use Workspace Version"), + description: version.displayName, + detail: version.pathLabel, + run: async () => { + await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); + const tsConfig = vscode.workspace.getConfiguration('typescript'); + await tsConfig.update('tsdk', version.pathLabel, false); + this.updateActiveVersion(version); + }, + }; + }); + } + + private async promptUseWorkspaceTsdk(): Promise<void> { + const workspaceVersion = this.versionProvider.localVersion; + + if (workspaceVersion === undefined) { + throw new Error('Could not prompt to use workspace TypeScript version because no workspace version is specified'); + } + + const allowIt = localize('allow', 'Allow'); + const dismissPrompt = localize('dismiss', 'Dismiss'); + const suppressPrompt = localize('suppress prompt', 'Never in this Workspace'); + + const result = await vscode.window.showInformationMessage(localize('promptUseWorkspaceTsdk', 'This workspace contains a TypeScript version. Would you like to use the workspace TypeScript version for TypeScript and JavaScript language features?'), + allowIt, + dismissPrompt, + suppressPrompt + ); + + if (result === allowIt) { + await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); + this.updateActiveVersion(workspaceVersion); + } else if (result === suppressPrompt) { + await this.workspaceState.update(suppressPromptWorkspaceTsdkStorageKey, true); + } + } + + private updateActiveVersion(pickedVersion: TypeScriptVersion) { + const oldVersion = this.currentVersion; + this._currentVersion = pickedVersion; + if (!oldVersion.eq(pickedVersion)) { + this._onDidPickNewVersion.fire(); + } + } + + private get useWorkspaceTsdkSetting(): boolean { + return this.workspaceState.get<boolean>(useWorkspaceTsdkStorageKey, false); + } + + private get suppressPromptWorkspaceTsdkSetting(): boolean { + return this.workspaceState.get<boolean>(suppressPromptWorkspaceTsdkStorageKey, false); + } + + private isInPromptWorkspaceTsdkState(configuration: TypeScriptServiceConfiguration) { + return ( + configuration.localTsdk !== null + && configuration.enablePromptUseWorkspaceTsdk === true + && this.suppressPromptWorkspaceTsdkSetting === false + && this.useWorkspaceTsdkSetting === false + ); + } +} + +const LearnMorePickItem: QuickPickItem = { + label: localize('learnMore', 'Learn more about managing TypeScript versions'), + description: '', + run: () => { + vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); + } +}; diff --git a/extensions/typescript-language-features/src/utils/versionProvider.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts similarity index 72% rename from extensions/typescript-language-features/src/utils/versionProvider.ts rename to extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts index 2864d9f9273..760f82d7b09 100644 --- a/extensions/typescript-language-features/src/utils/versionProvider.ts +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -2,45 +2,152 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import API from './api'; -import { TypeScriptServiceConfiguration } from './configuration'; -import { RelativeWorkspacePathResolver } from './relativePathResolver'; +import API from '../utils/api'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { RelativeWorkspacePathResolver } from '../utils/relativePathResolver'; +import { ITypeScriptVersionProvider, localize, TypeScriptVersion, TypeScriptVersionSource } from './versionProvider'; -const localize = nls.loadMessageBundle(); +export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider { -export const enum TypeScriptVersionSource { - Bundled = 'bundled', - TsNightlyExtension = 'ts-nightly-extension', - NodeModules = 'node-modules', - UserSetting = 'user-setting', - WorkspaceSetting = 'workspace-setting', -} - -export class TypeScriptVersion { - constructor( - public readonly source: TypeScriptVersionSource, - public readonly path: string, - private readonly _pathLabel?: string + public constructor( + private configuration?: TypeScriptServiceConfiguration ) { } - public get tsServerPath(): string { - return path.join(this.path, 'tsserver.js'); + public updateConfiguration(configuration: TypeScriptServiceConfiguration): void { + this.configuration = configuration; } - public get pathLabel(): string { - return this._pathLabel ?? this.path; + public get defaultVersion(): TypeScriptVersion { + return this.globalVersion || this.bundledVersion; } - public get isValid(): boolean { - return this.apiVersion !== undefined; + public get globalVersion(): TypeScriptVersion | undefined { + if (this.configuration?.globalTsdk) { + const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk); + if (globals && globals.length) { + return globals[0]; + } + } + return this.contributedTsNextVersion; } - public get apiVersion(): API | undefined { - const version = this.getTypeScriptVersion(this.tsServerPath); + public get localVersion(): TypeScriptVersion | undefined { + const tsdkVersions = this.localTsdkVersions; + if (tsdkVersions && tsdkVersions.length) { + return tsdkVersions[0]; + } + + const nodeVersions = this.localNodeModulesVersions; + if (nodeVersions && nodeVersions.length === 1) { + return nodeVersions[0]; + } + return undefined; + } + + + public get localVersions(): TypeScriptVersion[] { + const allVersions = this.localTsdkVersions.concat(this.localNodeModulesVersions); + const paths = new Set<string>(); + return allVersions.filter(x => { + if (paths.has(x.path)) { + return false; + } + paths.add(x.path); + return true; + }); + } + + public get bundledVersion(): TypeScriptVersion { + const version = this.getContributedVersion(TypeScriptVersionSource.Bundled, 'vscode.typescript-language-features', ['..', 'node_modules']); + if (version) { + return version; + } + + vscode.window.showErrorMessage(localize( + 'noBundledServerFound', + 'VS Code\'s tsserver was deleted by another application such as a misbehaving virus detection tool. Please reinstall VS Code.')); + throw new Error('Could not find bundled tsserver.js'); + } + + private get contributedTsNextVersion(): TypeScriptVersion | undefined { + return this.getContributedVersion(TypeScriptVersionSource.TsNightlyExtension, 'ms-vscode.vscode-typescript-next', ['node_modules']); + } + + private getContributedVersion(source: TypeScriptVersionSource, extensionId: string, pathToTs: readonly string[]): TypeScriptVersion | undefined { + try { + const extension = vscode.extensions.getExtension(extensionId); + if (extension) { + const serverPath = path.join(extension.extensionPath, ...pathToTs, 'typescript', 'lib', 'tsserver.js'); + const bundledVersion = new TypeScriptVersion(source, serverPath, DiskTypeScriptVersionProvider.getApiVersion(serverPath), ''); + if (bundledVersion.isValid) { + return bundledVersion; + } + } + } catch { + // noop + } + return undefined; + } + + private get localTsdkVersions(): TypeScriptVersion[] { + const localTsdk = this.configuration?.localTsdk; + return localTsdk ? this.loadVersionsFromSetting(TypeScriptVersionSource.WorkspaceSetting, localTsdk) : []; + } + + private loadVersionsFromSetting(source: TypeScriptVersionSource, tsdkPathSetting: string): TypeScriptVersion[] { + if (path.isAbsolute(tsdkPathSetting)) { + const serverPath = path.join(tsdkPathSetting, 'tsserver.js'); + return [ + new TypeScriptVersion(source, + serverPath, + DiskTypeScriptVersionProvider.getApiVersion(serverPath), + tsdkPathSetting) + ]; + } + + const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(tsdkPathSetting); + if (workspacePath !== undefined) { + const serverPath = path.join(workspacePath, 'tsserver.js'); + return [ + new TypeScriptVersion(source, + serverPath, + DiskTypeScriptVersionProvider.getApiVersion(serverPath), + tsdkPathSetting) + ]; + } + + return this.loadTypeScriptVersionsFromPath(source, tsdkPathSetting); + } + + private get localNodeModulesVersions(): TypeScriptVersion[] { + return this.loadTypeScriptVersionsFromPath(TypeScriptVersionSource.NodeModules, path.join('node_modules', 'typescript', 'lib')) + .filter(x => x.isValid); + } + + private loadTypeScriptVersionsFromPath(source: TypeScriptVersionSource, relativePath: string): TypeScriptVersion[] { + if (!vscode.workspace.workspaceFolders) { + return []; + } + + const versions: TypeScriptVersion[] = []; + for (const root of vscode.workspace.workspaceFolders) { + let label: string = relativePath; + if (vscode.workspace.workspaceFolders.length > 1) { + label = path.join(root.name, relativePath); + } + + const serverPath = path.join(root.uri.fsPath, relativePath, 'tsserver.js'); + versions.push(new TypeScriptVersion(source, serverPath, DiskTypeScriptVersionProvider.getApiVersion(serverPath), label)); + } + return versions; + } + + private static getApiVersion(serverPath: string): API | undefined { + const version = DiskTypeScriptVersionProvider.getTypeScriptVersion(serverPath); if (version) { return version; } @@ -54,13 +161,7 @@ export class TypeScriptVersion { return undefined; } - public get displayName(): string { - const version = this.apiVersion; - return version ? version.displayName : localize( - 'couldNotLoadTsVersion', 'Could not load the TypeScript version at this path'); - } - - private getTypeScriptVersion(serverPath: string): API | undefined { + private static getTypeScriptVersion(serverPath: string): API | undefined { if (!fs.existsSync(serverPath)) { return undefined; } @@ -95,125 +196,3 @@ export class TypeScriptVersion { return desc.version ? API.fromVersionString(desc.version) : undefined; } } - -export class TypeScriptVersionProvider { - - public constructor( - private configuration: TypeScriptServiceConfiguration - ) { } - - public updateConfiguration(configuration: TypeScriptServiceConfiguration): void { - this.configuration = configuration; - } - - public get defaultVersion(): TypeScriptVersion { - return this.globalVersion || this.bundledVersion; - } - - public get globalVersion(): TypeScriptVersion | undefined { - if (this.configuration.globalTsdk) { - const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk); - if (globals && globals.length) { - return globals[0]; - } - } - return this.contributedTsNextVersion; - } - - public get localVersion(): TypeScriptVersion | undefined { - const tsdkVersions = this.localTsdkVersions; - if (tsdkVersions && tsdkVersions.length) { - return tsdkVersions[0]; - } - - const nodeVersions = this.localNodeModulesVersions; - if (nodeVersions && nodeVersions.length === 1) { - return nodeVersions[0]; - } - return undefined; - } - - public get localVersions(): TypeScriptVersion[] { - const allVersions = this.localTsdkVersions.concat(this.localNodeModulesVersions); - const paths = new Set<string>(); - return allVersions.filter(x => { - if (paths.has(x.path)) { - return false; - } - paths.add(x.path); - return true; - }); - } - - public get bundledVersion(): TypeScriptVersion { - const version = this.getContributedVersion(TypeScriptVersionSource.Bundled, 'vscode.typescript-language-features', ['..', 'node_modules']); - if (version) { - return version; - } - - vscode.window.showErrorMessage(localize( - 'noBundledServerFound', - 'VS Code\'s tsserver was deleted by another application such as a misbehaving virus detection tool. Please reinstall VS Code.')); - throw new Error('Could not find bundled tsserver.js'); - } - - private get contributedTsNextVersion(): TypeScriptVersion | undefined { - return this.getContributedVersion(TypeScriptVersionSource.TsNightlyExtension, 'ms-vscode.vscode-typescript-next', ['node_modules']); - } - - private getContributedVersion(source: TypeScriptVersionSource, extensionId: string, pathToTs: readonly string[]): TypeScriptVersion | undefined { - try { - const extension = vscode.extensions.getExtension(extensionId); - if (extension) { - const typescriptPath = path.join(extension.extensionPath, ...pathToTs, 'typescript', 'lib'); - const bundledVersion = new TypeScriptVersion(source, typescriptPath, ''); - if (bundledVersion.isValid) { - return bundledVersion; - } - } - } catch { - // noop - } - return undefined; - } - - private get localTsdkVersions(): TypeScriptVersion[] { - const localTsdk = this.configuration.localTsdk; - return localTsdk ? this.loadVersionsFromSetting(TypeScriptVersionSource.WorkspaceSetting, localTsdk) : []; - } - - private loadVersionsFromSetting(source: TypeScriptVersionSource, tsdkPathSetting: string): TypeScriptVersion[] { - if (path.isAbsolute(tsdkPathSetting)) { - return [new TypeScriptVersion(source, tsdkPathSetting)]; - } - - const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(tsdkPathSetting); - if (workspacePath !== undefined) { - return [new TypeScriptVersion(source, workspacePath, tsdkPathSetting)]; - } - - return this.loadTypeScriptVersionsFromPath(source, tsdkPathSetting); - } - - private get localNodeModulesVersions(): TypeScriptVersion[] { - return this.loadTypeScriptVersionsFromPath(TypeScriptVersionSource.NodeModules, path.join('node_modules', 'typescript', 'lib')) - .filter(x => x.isValid); - } - - private loadTypeScriptVersionsFromPath(source: TypeScriptVersionSource, relativePath: string): TypeScriptVersion[] { - if (!vscode.workspace.workspaceFolders) { - return []; - } - - const versions: TypeScriptVersion[] = []; - for (const root of vscode.workspace.workspaceFolders) { - let label: string = relativePath; - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { - label = path.join(root.name, relativePath); - } - - versions.push(new TypeScriptVersion(source, path.join(root.uri.fsPath, relativePath), label)); - } - return versions; - } -} diff --git a/extensions/typescript-language-features/src/tsServer/versionProvider.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.ts new file mode 100644 index 00000000000..43f16c7c19d --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vscode-nls'; +import API from '../utils/api'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; + +export const localize = nls.loadMessageBundle(); + +export const enum TypeScriptVersionSource { + Bundled = 'bundled', + TsNightlyExtension = 'ts-nightly-extension', + NodeModules = 'node-modules', + UserSetting = 'user-setting', + WorkspaceSetting = 'workspace-setting', +} + +export class TypeScriptVersion { + + constructor( + public readonly source: TypeScriptVersionSource, + public readonly path: string, + public readonly apiVersion: API | undefined, + private readonly _pathLabel?: string, + ) { } + + public get tsServerPath(): string { + return this.path; + } + + public get pathLabel(): string { + return this._pathLabel ?? this.path; + } + + public get isValid(): boolean { + return this.apiVersion !== undefined; + } + + public eq(other: TypeScriptVersion): boolean { + if (this.path !== other.path) { + return false; + } + + if (this.apiVersion === other.apiVersion) { + return true; + } + if (!this.apiVersion || !other.apiVersion) { + return false; + } + return this.apiVersion.eq(other.apiVersion); + } + + public get displayName(): string { + const version = this.apiVersion; + return version ? version.displayName : localize( + 'couldNotLoadTsVersion', 'Could not load the TypeScript version at this path'); + } +} + +export interface ITypeScriptVersionProvider { + updateConfiguration(configuration: TypeScriptServiceConfiguration): void; + + readonly defaultVersion: TypeScriptVersion; + readonly globalVersion: TypeScriptVersion | undefined; + readonly localVersion: TypeScriptVersion | undefined; + readonly localVersions: readonly TypeScriptVersion[]; + readonly bundledVersion: TypeScriptVersion; +} diff --git a/extensions/typescript-language-features/src/tsServer/versionStatus.ts b/extensions/typescript-language-features/src/tsServer/versionStatus.ts new file mode 100644 index 00000000000..20b9debc27b --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/versionStatus.ts @@ -0,0 +1,220 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { Command, CommandManager } from '../commands/commandManager'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import { coalesce } from '../utils/arrays'; +import { Disposable } from '../utils/dispose'; +import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { isImplicitProjectConfigFile, openOrCreateConfig, openProjectConfigForFile, openProjectConfigOrPromptToCreate, ProjectType } from '../utils/tsconfig'; +import { TypeScriptVersion } from './versionProvider'; + +const localize = nls.loadMessageBundle(); + + +namespace ProjectInfoState { + export const enum Type { None, Pending, Resolved } + + export const None = Object.freeze({ type: Type.None } as const); + + export class Pending { + public readonly type = Type.Pending; + + public readonly cancellation = new vscode.CancellationTokenSource(); + + constructor( + public readonly resource: vscode.Uri, + ) { } + } + + export class Resolved { + public readonly type = Type.Resolved; + + constructor( + public readonly resource: vscode.Uri, + public readonly configFile: string, + ) { } + } + + export type State = typeof None | Pending | Resolved; +} + +interface QuickPickItem extends vscode.QuickPickItem { + run(): void; +} + +class ProjectStatusCommand implements Command { + public readonly id = '_typescript.projectStatus'; + + public constructor( + private readonly _client: ITypeScriptServiceClient, + private readonly _delegate: () => ProjectInfoState.State, + ) { } + + public async execute(): Promise<void> { + const info = this._delegate(); + + + const result = await vscode.window.showQuickPick<QuickPickItem>(coalesce([ + this.getProjectItem(info), + this.getVersionItem(), + this.getHelpItem(), + ]), { + placeHolder: localize('projectQuickPick.placeholder', "TypeScript Project Info"), + }); + + return result?.run(); + } + + private getVersionItem(): QuickPickItem { + return { + label: localize('projectQuickPick.version.label', "Select TypeScript Version..."), + description: this._client.apiVersion.displayName, + run: () => { + this._client.showVersionPicker(); + } + }; + } + + private getProjectItem(info: ProjectInfoState.State): QuickPickItem | undefined { + const rootPath = info.type === ProjectInfoState.Type.Resolved ? this._client.getWorkspaceRootForResource(info.resource) : undefined; + if (!rootPath) { + return undefined; + } + + if (info.type === ProjectInfoState.Type.Resolved) { + if (isImplicitProjectConfigFile(info.configFile)) { + return { + label: localize('projectQuickPick.project.create', "Create tsconfig"), + detail: localize('projectQuickPick.project.create.description', "This file is currently not part of a tsconfig/jsconfig project"), + run: () => { + openOrCreateConfig(ProjectType.TypeScript, rootPath, this._client.configuration); + } + }; + } + } + + return { + label: localize('projectQuickPick.version.goProjectConfig', "Open tsconfig"), + description: info.type === ProjectInfoState.Type.Resolved ? vscode.workspace.asRelativePath(info.configFile) : undefined, + run: () => { + if (info.type === ProjectInfoState.Type.Resolved) { + openProjectConfigOrPromptToCreate(ProjectType.TypeScript, this._client, rootPath, info.configFile); + } else if (info.type === ProjectInfoState.Type.Pending) { + openProjectConfigForFile(ProjectType.TypeScript, this._client, info.resource); + } + } + }; + } + + private getHelpItem(): QuickPickItem { + return { + label: localize('projectQuickPick.help', "TypeScript help"), + run: () => { + vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); // TODO: + } + }; + } +} + +export default class VersionStatus extends Disposable { + + private readonly _statusBarEntry: vscode.StatusBarItem; + + private _ready = false; + private _state: ProjectInfoState.State = ProjectInfoState.None; + + constructor( + private readonly _client: ITypeScriptServiceClient, + commandManager: CommandManager, + ) { + super(); + + this._statusBarEntry = this._register(vscode.window.createStatusBarItem({ + id: 'status.typescript', + name: localize('projectInfo.name', "TypeScript: Project Info"), + alignment: vscode.StatusBarAlignment.Right, + priority: 99 /* to the right of editor status (100) */ + })); + + const command = new ProjectStatusCommand(this._client, () => this._state); + commandManager.register(command); + this._statusBarEntry.command = command.id; + + vscode.window.onDidChangeActiveTextEditor(this.updateStatus, this, this._disposables); + + this._client.onReady(() => { + this._ready = true; + this.updateStatus(); + }); + + this._register(this._client.onTsServerStarted(({ version }) => this.onDidChangeTypeScriptVersion(version))); + } + + private onDidChangeTypeScriptVersion(version: TypeScriptVersion) { + this._statusBarEntry.text = version.displayName; + this._statusBarEntry.tooltip = version.path; + this.updateStatus(); + } + + private async updateStatus() { + if (!vscode.window.activeTextEditor) { + this.hide(); + return; + } + + const doc = vscode.window.activeTextEditor.document; + if (isTypeScriptDocument(doc)) { + const file = this._client.normalizedPath(doc.uri); + if (file) { + this._statusBarEntry.show(); + if (!this._ready) { + return; + } + + const pendingState = new ProjectInfoState.Pending(doc.uri); + this.updateState(pendingState); + + const response = await this._client.execute('projectInfo', { file, needFileNameList: false }, pendingState.cancellation.token); + if (response.type === 'response' && response.body) { + if (this._state === pendingState) { + this.updateState(new ProjectInfoState.Resolved(doc.uri, response.body.configFileName)); + this._statusBarEntry.show(); + } + } + + return; + } + } + + if (!vscode.window.activeTextEditor.viewColumn) { + // viewColumn is undefined for the debug/output panel, but we still want + // to show the version info in the existing editor + return; + } + + this.hide(); + } + + private hide(): void { + this._statusBarEntry.hide(); + this.updateState(ProjectInfoState.None); + } + + private updateState(newState: ProjectInfoState.State): void { + if (this._state === newState) { + return; + } + + if (this._state.type === ProjectInfoState.Type.Pending) { + this._state.cancellation.cancel(); + this._state.cancellation.dispose(); + } + + this._state = newState; + } +} diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 6eeb2ed78ab..d225ef60224 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -5,75 +5,85 @@ /* -------------------------------------------------------------------------------------------- * Includes code from typescript-sublime-plugin project, obtained from - * https://github.com/Microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences + * https://github.com/microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; -import { DiagnosticKind } from './features/diagnostics'; -import FileConfigurationManager from './features/fileConfigurationManager'; +import { DiagnosticKind } from './languageFeatures/diagnostics'; +import FileConfigurationManager from './languageFeatures/fileConfigurationManager'; import LanguageProvider from './languageProvider'; import * as Proto from './protocol'; import * as PConst from './protocol.const'; +import { OngoingRequestCancellerFactory } from './tsServer/cancellation'; +import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider'; +import { TsServerProcessFactory } from './tsServer/server'; +import { ITypeScriptVersionProvider } from './tsServer/versionProvider'; +import VersionStatus from './tsServer/versionStatus'; import TypeScriptServiceClient from './typescriptServiceClient'; -import { CommandManager } from './utils/commandManager'; +import { coalesce, flatten } from './utils/arrays'; +import { CommandManager } from './commands/commandManager'; import { Disposable } from './utils/dispose'; +import * as errorCodes from './utils/errorCodes'; import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription'; -import LogDirectoryProvider from './utils/logDirectoryProvider'; import { PluginManager } from './utils/plugins'; import * as typeConverters from './utils/typeConverters'; import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'; -import VersionStatus from './utils/versionStatus'; -import { flatten, coalesce } from './utils/arrays'; +import * as ProjectStatus from './utils/largeProjectStatus'; + +namespace Experimental { + export interface Diagnostic extends Proto.Diagnostic { + readonly reportsDeprecated?: {} + } +} // Style check diagnostics that can be reported as warnings -const styleCheckDiagnostics = [ - 6133, // variable is declared but never used - 6138, // property is declared but its value is never read - 6192, // All imports are unused - 7027, // unreachable code detected - 7028, // unused label - 7029, // fall through case in switch - 7030 // not all code paths return a value -]; +const styleCheckDiagnostics = new Set([ + ...errorCodes.variableDeclaredButNeverUsed, + ...errorCodes.propertyDeclaretedButNeverUsed, + ...errorCodes.allImportsAreUnused, + ...errorCodes.unreachableCode, + ...errorCodes.unusedLabel, + ...errorCodes.fallThroughCaseInSwitch, + ...errorCodes.notAllCodePathsReturnAValue, +]); export default class TypeScriptServiceClientHost extends Disposable { - private readonly typingsStatus: TypingsStatus; + private readonly client: TypeScriptServiceClient; private readonly languages: LanguageProvider[] = []; private readonly languagePerId = new Map<string, LanguageProvider>(); - private readonly versionStatus: VersionStatus; + + private readonly typingsStatus: TypingsStatus; + private readonly fileConfigurationManager: FileConfigurationManager; private reportStyleCheckAsWarnings: boolean = true; + private readonly commandManager: CommandManager; + constructor( descriptions: LanguageDescription[], workspaceState: vscode.Memento, - pluginManager: PluginManager, - private readonly commandManager: CommandManager, - logDirectoryProvider: LogDirectoryProvider, + onCaseInsenitiveFileSystem: boolean, + services: { + pluginManager: PluginManager, + commandManager: CommandManager, + logDirectoryProvider: ILogDirectoryProvider, + cancellerFactory: OngoingRequestCancellerFactory, + versionProvider: ITypeScriptVersionProvider, + processFactory: TsServerProcessFactory, + }, onCompletionAccepted: (item: vscode.CompletionItem) => void, ) { super(); - const handleProjectCreateOrDelete = () => { - this.triggerAllDiagnostics(); - }; - const handleProjectChange = () => { - setTimeout(() => { - this.triggerAllDiagnostics(); - }, 1500); - }; - const configFileWatcher = this._register(vscode.workspace.createFileSystemWatcher('**/[tj]sconfig.json')); - configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this._disposables); - configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this._disposables); - configFileWatcher.onDidChange(handleProjectChange, this, this._disposables); - const allModeIds = this.getAllModeIds(descriptions, pluginManager); + this.commandManager = services.commandManager; + + const allModeIds = this.getAllModeIds(descriptions, services.pluginManager); this.client = this._register(new TypeScriptServiceClient( workspaceState, - version => this.versionStatus.onDidChangeTypeScriptVersion(version), - pluginManager, - logDirectoryProvider, + onCaseInsenitiveFileSystem, + services, allModeIds)); this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => { @@ -83,11 +93,12 @@ export default class TypeScriptServiceClientHost extends Disposable { this.client.onConfigDiagnosticsReceived(diag => this.configFileDiagnosticsReceived(diag), null, this._disposables); this.client.onResendModelsRequested(() => this.populateService(), null, this._disposables); - this.versionStatus = this._register(new VersionStatus(resource => this.client.toPath(resource))); - + this._register(new VersionStatus(this.client, services.commandManager)); this._register(new AtaProgressReporter(this.client)); this.typingsStatus = this._register(new TypingsStatus(this.client)); - this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client)); + this._register(ProjectStatus.create(this.client)); + + this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client, onCaseInsenitiveFileSystem)); for (const description of descriptions) { const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); @@ -96,33 +107,41 @@ export default class TypeScriptServiceClientHost extends Disposable { this.languagePerId.set(description.id, manager); } - import('./features/updatePathsOnRename').then(module => + import('./languageFeatures/updatePathsOnRename').then(module => this._register(module.register(this.client, this.fileConfigurationManager, uri => this.handles(uri)))); - import('./features/workspaceSymbols').then(module => + import('./languageFeatures/workspaceSymbols').then(module => this._register(module.register(this.client, allModeIds))); this.client.ensureServiceStarted(); this.client.onReady(() => { const languages = new Set<string>(); - for (const plugin of pluginManager.plugins) { - for (const language of plugin.languages) { - languages.add(language); + for (const plugin of services.pluginManager.plugins) { + if (plugin.configNamespace && plugin.languages.length) { + this.registerExtensionLanguageProvider({ + id: plugin.configNamespace, + modeIds: Array.from(plugin.languages), + diagnosticSource: 'ts-plugin', + diagnosticLanguage: DiagnosticLanguage.TypeScript, + diagnosticOwner: 'typescript', + isExternal: true + }, onCompletionAccepted); + } else { + for (const language of plugin.languages) { + languages.add(language); + } } } + if (languages.size) { - const description: LanguageDescription = { + this.registerExtensionLanguageProvider({ id: 'typescript-plugins', modeIds: Array.from(languages.values()), diagnosticSource: 'ts-plugin', diagnosticLanguage: DiagnosticLanguage.TypeScript, diagnosticOwner: 'typescript', isExternal: true - }; - const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); - this.languages.push(manager); - this._register(manager); - this.languagePerId.set(description.id, manager); + }, onCompletionAccepted); } }); @@ -134,6 +153,13 @@ export default class TypeScriptServiceClientHost extends Disposable { this.configurationChanged(); } + private registerExtensionLanguageProvider(description: LanguageDescription, onCompletionAccepted: (item: vscode.CompletionItem) => void) { + const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); + this.languages.push(manager); + this._register(manager); + this.languagePerId.set(description.id, manager); + } + private getAllModeIds(descriptions: LanguageDescription[], pluginManager: PluginManager) { const allModeIds = flatten([ ...descriptions.map(x => x.modeIds), @@ -183,12 +209,9 @@ export default class TypeScriptServiceClientHost extends Disposable { private populateService(): void { this.fileConfigurationManager.reset(); - // See https://github.com/Microsoft/TypeScript/issues/5530 - vscode.workspace.saveAll(false).then(() => { - for (const language of this.languagePerId.values()) { - language.reInitialize(); - } - }); + for (const language of this.languagePerId.values()) { + language.reInitialize(); + } } private async diagnosticsReceived( @@ -206,7 +229,7 @@ export default class TypeScriptServiceClientHost extends Disposable { } private configFileDiagnosticsReceived(event: Proto.ConfigFileDiagnosticEvent): void { - // See https://github.com/Microsoft/TypeScript/issues/10384 + // See https://github.com/microsoft/TypeScript/issues/10384 const body = event.body; if (!body || !body.diagnostics || !body.configFile) { return; @@ -229,11 +252,11 @@ export default class TypeScriptServiceClientHost extends Disposable { private createMarkerDatas( diagnostics: Proto.Diagnostic[], source: string - ): (vscode.Diagnostic & { reportUnnecessary: any })[] { + ): (vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[] { return diagnostics.map(tsDiag => this.tsDiagnosticToVsDiagnostic(tsDiag, source)); } - private tsDiagnosticToVsDiagnostic(diagnostic: Proto.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any } { + private tsDiagnosticToVsDiagnostic(diagnostic: Experimental.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any } { const { start, end, text } = diagnostic; const range = new vscode.Range(typeConverters.Position.fromLocation(start), typeConverters.Position.fromLocation(end)); const converted = new vscode.Diagnostic(range, text, this.getDiagnosticSeverity(diagnostic)); @@ -251,11 +274,19 @@ export default class TypeScriptServiceClientHost extends Disposable { return new vscode.DiagnosticRelatedInformation(typeConverters.Location.fromTextSpan(this.client.toResource(span.file), span), info.message); })); } + const tags: vscode.DiagnosticTag[] = []; if (diagnostic.reportsUnnecessary) { - converted.tags = [vscode.DiagnosticTag.Unnecessary]; + tags.push(vscode.DiagnosticTag.Unnecessary); } - (converted as vscode.Diagnostic & { reportUnnecessary: any }).reportUnnecessary = diagnostic.reportsUnnecessary; - return converted as vscode.Diagnostic & { reportUnnecessary: any }; + if (diagnostic.reportsDeprecated) { + tags.push(vscode.DiagnosticTag.Deprecated); + } + converted.tags = tags.length ? tags : undefined; + + const resultConverted = converted as vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any }; + resultConverted.reportUnnecessary = diagnostic.reportsUnnecessary; + resultConverted.reportDeprecated = diagnostic.reportsDeprecated; + return resultConverted; } private getDiagnosticSeverity(diagnostic: Proto.Diagnostic): vscode.DiagnosticSeverity { @@ -282,6 +313,6 @@ export default class TypeScriptServiceClientHost extends Disposable { } private isStyleCheckDiagnostic(code: number | undefined): boolean { - return code ? styleCheckDiagnostics.indexOf(code) !== -1 : false; + return typeof code === 'number' && styleCheckDiagnostics.has(code); } } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 043284c1244..58a78ad19b5 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -4,12 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import BufferSyncSupport from './features/bufferSyncSupport'; import * as Proto from './protocol'; +import BufferSyncSupport from './tsServer/bufferSyncSupport'; +import { ExectuionTarget } from './tsServer/server'; +import { TypeScriptVersion } from './tsServer/versionProvider'; import API from './utils/api'; import { TypeScriptServiceConfiguration } from './utils/configuration'; -import Logger from './utils/logger'; import { PluginManager } from './utils/plugins'; +import { TelemetryReporter } from './utils/telemetry'; + +export enum ServerType { + Syntax = 'syntax', + Semantic = 'semantic', +} export namespace ServerResponse { @@ -33,7 +40,7 @@ interface StandardTsServerRequests { 'completions': [Proto.CompletionsRequestArgs, Proto.CompletionsResponse]; 'configure': [Proto.ConfigureRequestArguments, Proto.ConfigureResponse]; 'definition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse]; - 'definitionAndBoundSpan': [Proto.FileLocationRequestArgs, Proto.DefinitionInfoAndBoundSpanReponse]; + 'definitionAndBoundSpan': [Proto.FileLocationRequestArgs, Proto.DefinitionInfoAndBoundSpanResponse]; 'docCommentTemplate': [Proto.FileLocationRequestArgs, Proto.DocCommandTemplateResponse]; 'documentHighlights': [Proto.DocumentHighlightsRequestArgs, Proto.DocumentHighlightsResponse]; 'format': [Proto.FormatRequestArgs, Proto.FormatResponse]; @@ -74,6 +81,7 @@ interface NoResponseTsServerRequests { interface AsyncTsServerRequests { 'geterr': [Proto.GeterrRequestArgs, Proto.Response]; + 'geterrForProject': [Proto.GeterrForProjectRequestArgs, Proto.Response]; } export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRequests & AsyncTsServerRequests; @@ -81,9 +89,39 @@ export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRe export type ExecConfig = { readonly lowPriority?: boolean; readonly nonRecoverable?: boolean; - readonly cancelOnResourceChange?: vscode.Uri + readonly cancelOnResourceChange?: vscode.Uri; + readonly executionTarget?: ExectuionTarget; }; +export enum ClientCapability { + /** + * Basic syntax server. All clients should support this. + */ + Syntax, + + /** + * Advanced syntax server that can provide single file IntelliSense. + */ + EnhancedSyntax, + + /** + * Complete, multi-file semantic server + */ + Semantic, +} + +export class ClientCapabilities { + private readonly capabilities: ReadonlySet<ClientCapability>; + + constructor(...capabilities: ClientCapability[]) { + this.capabilities = new Set(capabilities); + } + + public has(capability: ClientCapability): boolean { + return this.capabilities.has(capability); + } +} + export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). @@ -107,23 +145,36 @@ export interface ITypeScriptServiceClient { /** * Tries to ensure that a vscode document is open on the TS server. * - * Returns the normalized path. + * @return The normalized path or `undefined` if the document is not open on the server. */ toOpenedFilePath(document: vscode.TextDocument): string | undefined; + /** + * Checks if `resource` has a given capability. + */ + hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean; + getWorkspaceRootForResource(resource: vscode.Uri): string | undefined; - readonly onTsServerStarted: vscode.Event<API>; + readonly onTsServerStarted: vscode.Event<{ version: TypeScriptVersion, usedApiVersion: API }>; readonly onProjectLanguageServiceStateChanged: vscode.Event<Proto.ProjectLanguageServiceStateEventBody>; readonly onDidBeginInstallTypings: vscode.Event<Proto.BeginInstallTypesEventBody>; readonly onDidEndInstallTypings: vscode.Event<Proto.EndInstallTypesEventBody>; readonly onTypesInstallerInitializationFailed: vscode.Event<Proto.TypesInstallerInitializationFailedEventBody>; + readonly capabilities: ClientCapabilities; + readonly onDidChangeCapabilities: vscode.Event<void>; + + onReady(f: () => void): Promise<void>; + + showVersionPicker(): void; + readonly apiVersion: API; + readonly pluginManager: PluginManager; readonly configuration: TypeScriptServiceConfiguration; - readonly logger: Logger; readonly bufferSyncSupport: BufferSyncSupport; + readonly telemetryReporter: TelemetryReporter; execute<K extends keyof StandardTsServerRequests>( command: K, @@ -137,7 +188,11 @@ export interface ITypeScriptServiceClient { args: NoResponseTsServerRequests[K][0] ): void; - executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>>; + executeAsync<K extends keyof AsyncTsServerRequests>( + command: K, + args: AsyncTsServerRequests[K][0], + token: vscode.CancellationToken + ): Promise<ServerResponse.Response<Proto.Response>>; /** * Cancel on going geterr requests and re-queue them after `f` has been evaluated. diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 7046b67f4fc..2d9dad6392a 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -3,30 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import BufferSyncSupport from './features/bufferSyncSupport'; -import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; +import { DiagnosticKind, DiagnosticsManager } from './languageFeatures/diagnostics'; import * as Proto from './protocol'; -import { ITypeScriptServer } from './tsServer/server'; +import { EventName } from './protocol.const'; +import BufferSyncSupport from './tsServer/bufferSyncSupport'; +import { OngoingRequestCancellerFactory } from './tsServer/cancellation'; +import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider'; +import { ITypeScriptServer, TsServerProcessFactory } from './tsServer/server'; import { TypeScriptServerError } from './tsServer/serverError'; import { TypeScriptServerSpawner } from './tsServer/spawner'; -import { ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; +import { TypeScriptVersionManager } from './tsServer/versionManager'; +import { ITypeScriptVersionProvider, TypeScriptVersion } from './tsServer/versionProvider'; +import { ClientCapabilities, ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; import * as fileSchemes from './utils/fileSchemes'; -import LogDirectoryProvider from './utils/logDirectoryProvider'; -import Logger from './utils/logger'; +import { Logger } from './utils/logger'; +import { isWeb } from './utils/platform'; import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider'; import { PluginManager } from './utils/plugins'; -import { TelemetryReporter, VSCodeTelemetryReporter, TelemetryProperties } from './utils/telemetry'; +import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './utils/telemetry'; import Tracer from './utils/tracer'; -import { inferredProjectCompilerOptions } from './utils/tsconfig'; -import { TypeScriptVersionPicker } from './utils/versionPicker'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'; +import { inferredProjectCompilerOptions, ProjectType } from './utils/tsconfig'; const localize = nls.loadMessageBundle(); @@ -65,7 +67,7 @@ namespace ServerState { * Version reported by currently-running tsserver. */ public tsserverVersion: string | undefined, - public langaugeServiceEnabled: boolean, + public languageServiceEnabled: boolean, ) { } public readonly toCancelOnResourceChange = new Set<ToCancelOnResourceChanged>(); @@ -74,8 +76,8 @@ namespace ServerState { this.tsserverVersion = tsserverVersion; } - updateLangaugeServiceEnabled(enabled: boolean) { - this.langaugeServiceEnabled = enabled; + updateLanguageServiceEnabled(enabled: boolean) { + this.languageServiceEnabled = enabled; } } @@ -83,6 +85,7 @@ namespace ServerState { readonly type = Type.Errored; constructor( public readonly error: Error, + public readonly tsServerLogFile: string | undefined, ) { } } @@ -90,62 +93,83 @@ namespace ServerState { } export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { - private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; private readonly pathSeparator: string; private readonly inMemoryResourcePrefix = '^'; private _onReady?: { promise: Promise<void>; resolve: () => void; reject: () => void; }; private _configuration: TypeScriptServiceConfiguration; - private versionProvider: TypeScriptVersionProvider; private pluginPathsProvider: TypeScriptPluginPathsProvider; - private versionPicker: TypeScriptVersionPicker; + private readonly _versionManager: TypeScriptVersionManager; - private tracer: Tracer; - public readonly logger: Logger = new Logger(); + private readonly logger = new Logger(); + private readonly tracer = new Tracer(this.logger); private readonly typescriptServerSpawner: TypeScriptServerSpawner; private serverState: ServerState.State = ServerState.None; private lastStart: number; private numberRestarts: number; + private _isPromptingAfterCrash = false; private isRestarting: boolean = false; + private hasServerFatallyCrashedTooManyTimes = false; private readonly loadingIndicator = new ServerInitializingIndicator(); public readonly telemetryReporter: TelemetryReporter; - public readonly bufferSyncSupport: BufferSyncSupport; public readonly diagnosticsManager: DiagnosticsManager; + public readonly pluginManager: PluginManager; + + private readonly logDirectoryProvider: ILogDirectoryProvider; + private readonly cancellerFactory: OngoingRequestCancellerFactory; + private readonly versionProvider: ITypeScriptVersionProvider; + private readonly processFactory: TsServerProcessFactory; constructor( private readonly workspaceState: vscode.Memento, - private readonly onDidChangeTypeScriptVersion: (version: TypeScriptVersion) => void, - public readonly pluginManager: PluginManager, - private readonly logDirectoryProvider: LogDirectoryProvider, - allModeIds: string[] + onCaseInsenitiveFileSystem: boolean, + services: { + pluginManager: PluginManager, + logDirectoryProvider: ILogDirectoryProvider, + cancellerFactory: OngoingRequestCancellerFactory, + versionProvider: ITypeScriptVersionProvider, + processFactory: TsServerProcessFactory, + }, + allModeIds: readonly string[] ) { super(); + + this.pluginManager = services.pluginManager; + this.logDirectoryProvider = services.logDirectoryProvider; + this.cancellerFactory = services.cancellerFactory; + this.versionProvider = services.versionProvider; + this.processFactory = services.processFactory; + this.pathSeparator = path.sep; this.lastStart = Date.now(); - // eslint-disable-next-line no-var - var p = new Promise<void>((resolve, reject) => { - this._onReady = { promise: p, resolve, reject }; + let resolve: () => void; + let reject: () => void; + const p = new Promise<void>((res, rej) => { + resolve = res; + reject = rej; }); - this._onReady!.promise = p; + this._onReady = { promise: p, resolve: resolve!, reject: reject! }; this.numberRestarts = 0; this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); - this.versionProvider = new TypeScriptVersionProvider(this._configuration); + this.versionProvider.updateConfiguration(this._configuration); + this.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration); - this.versionPicker = new TypeScriptVersionPicker(this.versionProvider, this.workspaceState); + this._versionManager = this._register(new TypeScriptVersionManager(this._configuration, this.versionProvider, this.workspaceState)); + this._register(this._versionManager.onDidPickNewVersion(() => { + this.restartTsServer(); + })); - this.tracer = new Tracer(this.logger); - - this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds); + this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds, onCaseInsenitiveFileSystem); this.onReady(() => { this.bufferSyncSupport.listen(); }); - this.diagnosticsManager = new DiagnosticsManager('typescript'); + this.diagnosticsManager = new DiagnosticsManager('typescript', onCaseInsenitiveFileSystem); this.bufferSyncSupport.onDelete(resource => { this.cancelInflightRequestsForResource(resource); this.diagnosticsManager.delete(resource); @@ -160,6 +184,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); this.versionProvider.updateConfiguration(this._configuration); + this._versionManager.updateConfiguration(this._configuration); this.pluginPathsProvider.updateConfiguration(this._configuration); this.tracer.updateConfiguration(); @@ -185,7 +210,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.apiVersion.fullVersionString; })); - this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer); + this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); this._register(this.pluginManager.onDidUpdateConfig(update => { this.configurePlugin(update.pluginId, update.config); @@ -196,6 +221,28 @@ export default class TypeScriptServiceClient extends Disposable implements IType })); } + public get capabilities() { + if (isWeb()) { + return new ClientCapabilities( + ClientCapability.Syntax, + ClientCapability.EnhancedSyntax); + } + + if (this.apiVersion.gte(API.v400)) { + return new ClientCapabilities( + ClientCapability.Syntax, + ClientCapability.EnhancedSyntax, + ClientCapability.Semantic); + } + + return new ClientCapabilities( + ClientCapability.Syntax, + ClientCapability.Semantic); + } + + private readonly _onDidChangeCapabilities = this._register(new vscode.EventEmitter<void>()); + readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event; + private cancelInflightRequestsForResource(resource: vscode.Uri): void { if (this.serverState.type !== ServerState.Type.Running) { return; @@ -234,7 +281,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.serverState = this.startService(true); } - private readonly _onTsServerStarted = this._register(new vscode.EventEmitter<API>()); + private readonly _onTsServerStarted = this._register(new vscode.EventEmitter<{ version: TypeScriptVersion, usedApiVersion: API }>()); public readonly onTsServerStarted = this._onTsServerStarted.event; private readonly _onDiagnosticsReceived = this._register(new vscode.EventEmitter<TsDiagnostics>()); @@ -295,7 +342,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (newState.type === ServerState.Type.Running) { return newState; } - throw new Error('Could not create TS service'); + throw new Error(`Could not create TS service. Service state:${JSON.stringify(newState)}`); } public ensureServiceStarted() { @@ -306,24 +353,31 @@ export default class TypeScriptServiceClient extends Disposable implements IType private token: number = 0; private startService(resendModels: boolean = false): ServerState.State { + this.info(`Starting TS Server `); + if (this.isDisposed) { + this.info(`Not starting server. Disposed `); return ServerState.None; } - let currentVersion = this.versionPicker.currentVersion; - - this.info(`Using tsserver from: ${currentVersion.path}`); - if (!fs.existsSync(currentVersion.tsServerPath)) { - vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', currentVersion.path)); - - this.versionPicker.useBundledVersion(); - currentVersion = this.versionPicker.currentVersion; + if (this.hasServerFatallyCrashedTooManyTimes) { + this.info(`Not starting server. Too many crashes.`); + return ServerState.None; } - const apiVersion = this.versionPicker.currentVersion.apiVersion || API.defaultVersion; - this.onDidChangeTypeScriptVersion(currentVersion); + let version = this._versionManager.currentVersion; + if (!version.isValid) { + vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', version.path)); + + this._versionManager.reset(); + version = this._versionManager.currentVersion; + } + + this.info(`Using tsserver from: ${version.path}`); + + const apiVersion = version.apiVersion || API.defaultVersion; let mytoken = ++this.token; - const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.pluginManager, { + const handle = this.typescriptServerSpawner.spawn(version, this.capabilities, this.configuration, this.pluginManager, this.cancellerFactory, { onFatalError: (command, err) => this.fatalError(command, err), }); this.serverState = new ServerState.Running(handle, apiVersion, undefined, true); @@ -340,7 +394,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType */ this.logTelemetry('tsserver.spawned', { localTypeScriptVersion: this.versionProvider.localVersion ? this.versionProvider.localVersion.displayName : '', - typeScriptVersionSource: currentVersion.source, + typeScriptVersionSource: version.source, }); handle.onError((err: Error) => { @@ -353,7 +407,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType vscode.window.showErrorMessage(localize('serverExitedWithError', 'TypeScript language server exited with error. Error message is: {0}', err.message || err.name)); } - this.serverState = new ServerState.Errored(err); + this.serverState = new ServerState.Errored(err, handle.tsServerLogFile); this.error('TSServer errored with error.', err); if (handle.tsServerLogFile) { this.error(`TSServer log file: ${handle.tsServerLogFile}`); @@ -379,10 +433,12 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (code === null || typeof code === 'undefined') { this.info('TSServer exited'); } else { + // In practice, the exit code is an integer with no ties to any identity, + // so it can be classified as SystemMetaData, rather than CallstackOrException. this.error(`TSServer exited with code: ${code}`); /* __GDPR__ "tsserver.exitWithCode" : { - "code" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "code" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "${include}": [ "${TypeScriptCommonProperties}" ] @@ -398,32 +454,22 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.isRestarting = false; }); - handle.onReaderError(error => this.error('ReaderError', error)); handle.onEvent(event => this.dispatchEvent(event)); - this._onReady!.resolve(); - this._onTsServerStarted.fire(currentVersion.apiVersion); - - if (apiVersion.gte(API.v300)) { + if (apiVersion.gte(API.v300) && this.capabilities.has(ClientCapability.Semantic)) { this.loadingIndicator.startedLoadingProject(undefined /* projectName */); } this.serviceStarted(resendModels); + this._onReady!.resolve(); + this._onTsServerStarted.fire({ version: version, usedApiVersion: apiVersion }); + this._onDidChangeCapabilities.fire(); return this.serverState; } - public onVersionStatusClicked(): Thenable<void> { - return this.showVersionPicker(false); - } - - private showVersionPicker(firstRun: boolean): Thenable<void> { - return this.versionPicker.show(firstRun).then(change => { - if (firstRun || !change.newVersion || !change.oldVersion || change.oldVersion.path === change.newVersion.path) { - return; - } - this.restartTsServer(); - }); + public async showVersionPicker(): Promise<void> { + this._versionManager.promptUserForVersion(); } public async openTsServerLogFile(): Promise<boolean> { @@ -477,12 +523,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType private serviceStarted(resendModels: boolean): void { this.bufferSyncSupport.reset(); + const watchOptions = this.apiVersion.gte(API.v380) + ? this.configuration.watchOptions + : undefined; + const configureOptions: Proto.ConfigureRequestArguments = { hostInfo: 'vscode', preferences: { providePrefixAndSuffixTextForRename: true, allowRenameOfImportPath: true, - } + includePackageJsonAutoImports: this._configuration.includePackageJsonAutoImports, + }, + watchOptions }; this.executeWithoutWaitingForResponse('configure', configureOptions); this.setCompilerOptionsForInferredProjects(this._configuration); @@ -507,41 +559,40 @@ export default class TypeScriptServiceClient extends Disposable implements IType private getCompilerOptionsForInferredProjects(configuration: TypeScriptServiceConfiguration): Proto.ExternalProjectCompilerOptions { return { - ...inferredProjectCompilerOptions(true, configuration), + ...inferredProjectCompilerOptions(ProjectType.TypeScript, configuration), allowJs: true, allowSyntheticDefaultImports: true, allowNonTsExtensions: true, + resolveJsonModule: true, }; } private serviceExited(restart: boolean): void { this.loadingIndicator.reset(); - enum MessageAction { - reportIssue - } - - interface MyMessageItem extends vscode.MessageItem { - id: MessageAction; - } - + const previousState = this.serverState; this.serverState = ServerState.None; + if (restart) { const diff = Date.now() - this.lastStart; this.numberRestarts++; let startService = true; + + const reportIssueItem: vscode.MessageItem = { + title: localize('serverDiedReportIssue', 'Report Issue'), + }; + let prompt: Thenable<undefined | vscode.MessageItem> | undefined = undefined; + if (this.numberRestarts > 5) { - let prompt: Thenable<MyMessageItem | undefined> | undefined = undefined; this.numberRestarts = 0; if (diff < 10 * 1000 /* 10 seconds */) { this.lastStart = Date.now(); startService = false; - prompt = vscode.window.showErrorMessage<MyMessageItem>( + this.hasServerFatallyCrashedTooManyTimes = true; + prompt = vscode.window.showErrorMessage( localize('serverDiedAfterStart', 'The TypeScript language service died 5 times right after it got started. The service will not be restarted.'), - { - title: localize('serverDiedReportIssue', 'Report Issue'), - id: MessageAction.reportIssue, - }); + reportIssueItem); + /* __GDPR__ "serviceExited" : { "${include}": [ @@ -552,22 +603,32 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.logTelemetry('serviceExited'); } else if (diff < 60 * 1000 * 5 /* 5 Minutes */) { this.lastStart = Date.now(); - prompt = vscode.window.showWarningMessage<MyMessageItem>( + prompt = vscode.window.showWarningMessage( localize('serverDied', 'The TypeScript language service died unexpectedly 5 times in the last 5 Minutes.'), - { - title: localize('serverDiedReportIssue', 'Report Issue'), - id: MessageAction.reportIssue - }); + reportIssueItem); } - if (prompt) { - prompt.then(item => { - if (item && item.id === MessageAction.reportIssue) { - return vscode.commands.executeCommand('workbench.action.openIssueReporter'); - } - return undefined; - }); + } else if (['vscode-insiders', 'code-oss'].includes(vscode.env.uriScheme)) { + // Prompt after a single restart + if (!this._isPromptingAfterCrash && previousState.type === ServerState.Type.Errored && previousState.error instanceof TypeScriptServerError) { + this.numberRestarts = 0; + this._isPromptingAfterCrash = true; + prompt = vscode.window.showWarningMessage( + localize('serverDiedOnce', 'The TypeScript language service died unexpectedly.'), + reportIssueItem); } } + + prompt?.then(item => { + this._isPromptingAfterCrash = false; + + if (item === reportIssueItem) { + const args = previousState.type === ServerState.Type.Errored && previousState.error instanceof TypeScriptServerError + ? getReportIssueArgsForError(previousState.error, previousState.tsServerLogFile) + : undefined; + vscode.commands.executeCommand('workbench.action.openIssueReporter', args); + } + }); + if (startService) { this.startService(true); } @@ -575,27 +636,27 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public normalizedPath(resource: vscode.Uri): string | undefined { - if (resource.scheme === fileSchemes.walkThroughSnippet || resource.scheme === fileSchemes.untitled) { - const dirName = path.dirname(resource.path); - const fileName = this.inMemoryResourcePrefix + path.basename(resource.path); - return resource.with({ path: path.posix.join(dirName, fileName), query: '' }).toString(true); - } - - if (resource.scheme !== fileSchemes.file) { + if (fileSchemes.disabledSchemes.has(resource.scheme)) { return undefined; } - let result = resource.fsPath; - if (!result) { - return undefined; - } + switch (resource.scheme) { + case fileSchemes.file: + { + let result = resource.fsPath; + if (!result) { + return undefined; + } + result = path.normalize(result); - if (resource.scheme === fileSchemes.file) { - result = path.normalize(result); + // Both \ and / must be escaped in regular expressions + return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); + } + default: + { + return this.inMemoryResourcePrefix + resource.toString(true); + } } - - // Both \ and / must be escaped in regular expressions - return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); } public toPath(resource: vscode.Uri): string | undefined { @@ -604,32 +665,38 @@ export default class TypeScriptServiceClient extends Disposable implements IType public toOpenedFilePath(document: vscode.TextDocument): string | undefined { if (!this.bufferSyncSupport.ensureHasBuffer(document.uri)) { - console.error(`Unexpected resource ${document.uri}`); + if (!fileSchemes.disabledSchemes.has(document.uri.scheme)) { + console.error(`Unexpected resource ${document.uri}`); + } return undefined; } return this.toPath(document.uri) || undefined; } - public toResource(filepath: string): vscode.Uri { - if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':')) - ) { - let resource = vscode.Uri.parse(filepath); - const dirName = path.dirname(resource.path); - const fileName = path.basename(resource.path); - if (fileName.startsWith(this.inMemoryResourcePrefix)) { - resource = resource.with({ - path: path.posix.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length)) - }); - } + public hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean { + switch (capability) { + case ClientCapability.Semantic: + { + return fileSchemes.semanticSupportedSchemes.includes(resource.scheme); + } + case ClientCapability.Syntax: + case ClientCapability.EnhancedSyntax: + { + return true; + } + } + } + public toResource(filepath: string): vscode.Uri { + if (filepath.startsWith(this.inMemoryResourcePrefix)) { + const resource = vscode.Uri.parse(filepath.slice(1)); return this.bufferSyncSupport.toVsCodeResource(resource); } - return this.bufferSyncSupport.toResource(filepath); } public getWorkspaceRootForResource(resource: vscode.Uri): string | undefined { - const roots = vscode.workspace.workspaceFolders; + const roots = vscode.workspace.workspaceFolders ? Array.from(vscode.workspace.workspaceFolders) : undefined; if (!roots || !roots.length) { return undefined; } @@ -702,9 +769,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType }); } - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined { + private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, requireSemantic?: boolean }): undefined; + private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, requireSemantic?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; + private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, requireSemantic?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined { this.bufferSyncSupport.beforeCommand(command); const runningServerState = this.service(); return runningServerState.server.executeImpl(command, args, executeInfo); @@ -715,19 +782,16 @@ export default class TypeScriptServiceClient extends Disposable implements IType } private fatalError(command: string, error: unknown): void { - if (!(error instanceof TypeScriptServerError)) { - console.log('fdasfasdf'); - } /* __GDPR__ "fatalError" : { "${include}": [ "${TypeScriptCommonProperties}", "${TypeScriptRequestErrorProperties}" ], - "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.logTelemetry('fatalError', { command, ...(error instanceof TypeScriptServerError ? error.telemetry : {}) }); + this.logTelemetry('fatalError', { ...(error instanceof TypeScriptServerError ? error.telemetry : { command }) }); console.error(`A non-recoverable error occured while executing tsserver command: ${command}`); if (error instanceof TypeScriptServerError && error.serverErrorText) { console.error(error.serverErrorText); @@ -735,15 +799,19 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (this.serverState.type === ServerState.Type.Running) { this.info('Killing TS Server'); + const logfile = this.serverState.server.tsServerLogFile; this.serverState.server.kill(); + if (error instanceof TypeScriptServerError) { + this.serverState = new ServerState.Errored(error, logfile); + } } } private dispatchEvent(event: Proto.Event) { switch (event.event) { - case 'syntaxDiag': - case 'semanticDiag': - case 'suggestionDiag': + case EventName.syntaxDiag: + case EventName.semanticDiag: + case EventName.suggestionDiag: // This event also roughly signals that projects have been loaded successfully (since the TS server is synchronous) this.loadingIndicator.reset(); @@ -757,52 +825,54 @@ export default class TypeScriptServiceClient extends Disposable implements IType } break; - case 'configFileDiag': + case EventName.configFileDiag: this._onConfigDiagnosticsReceived.fire(event as Proto.ConfigFileDiagnosticEvent); break; - case 'telemetry': + case EventName.telemetry: { const body = (event as Proto.TelemetryEvent).body; this.dispatchTelemetryEvent(body); break; } - case 'projectLanguageServiceState': + case EventName.projectLanguageServiceState: { const body = (event as Proto.ProjectLanguageServiceStateEvent).body!; if (this.serverState.type === ServerState.Type.Running) { - this.serverState.updateLangaugeServiceEnabled(body.languageServiceEnabled); + this.serverState.updateLanguageServiceEnabled(body.languageServiceEnabled); } this._onProjectLanguageServiceStateChanged.fire(body); break; } - case 'projectsUpdatedInBackground': + case EventName.projectsUpdatedInBackground: + this.loadingIndicator.reset(); + const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; const resources = body.openFiles.map(file => this.toResource(file)); this.bufferSyncSupport.getErr(resources); break; - case 'beginInstallTypes': + case EventName.beginInstallTypes: this._onDidBeginInstallTypings.fire((event as Proto.BeginInstallTypesEvent).body); break; - case 'endInstallTypes': + case EventName.endInstallTypes: this._onDidEndInstallTypings.fire((event as Proto.EndInstallTypesEvent).body); break; - case 'typesInstallerInitializationFailed': + case EventName.typesInstallerInitializationFailed: this._onTypesInstallerInitializationFailed.fire((event as Proto.TypesInstallerInitializationFailedEvent).body); break; - case 'surveyReady': + case EventName.surveyReady: this._onSurveyReady.fire((event as Proto.SurveyReadyEvent).body); break; - case 'projectLoadingStart': + case EventName.projectLoadingStart: this.loadingIndicator.startedLoadingProject((event as Proto.ProjectLoadingStartEvent).body.projectName); break; - case 'projectLoadingFinish': + case EventName.projectLoadingFinish: this.loadingIndicator.finishedLoadingProject((event as Proto.ProjectLoadingFinishEvent).body.projectName); break; } @@ -865,6 +935,65 @@ export default class TypeScriptServiceClient extends Disposable implements IType } } +function getReportIssueArgsForError( + error: TypeScriptServerError, + logPath: string | undefined, +): { extensionId: string, issueTitle: string, issueBody: string } | undefined { + if (!error.serverStack || !error.serverMessage) { + return undefined; + } + + // Note these strings are intentionally not localized + // as we want users to file issues in english + + const sections = [ + `❗️❗️❗️ Please fill in the sections below to help us diagnose the issue ❗️❗️❗️`, + `**TypeScript Version:** ${error.version.apiVersion?.fullVersionString}`, + `**Steps to reproduce crash** + +1. +2. +3.`, + ]; + + if (logPath) { + sections.push(`**TS Server Log** + +❗️ Please review and upload this log file to help us diagnose this crash: + +\`${logPath}\` + +The log file may contain personal data, including full paths and source code from your workspace. You can scrub the log file to remove paths or other personal information. +`); + } else { + + sections.push(`**TS Server Log** + +❗️Server logging disabled. To help us fix crashes like this, please enable logging by setting: + +\`\`\`json +"typescript.tsserver.log": "verbose" +\`\`\` + +After enabling this setting, future crash reports will include the server log.`); + } + + sections.push(`**TS Server Error Stack** + +Server: \`${error.serverId}\` + +\`\`\` +${error.serverStack} +\`\`\``); + + return { + extensionId: 'vscode.typescript-language-features', + issueTitle: `TS Server fatal error: ${error.serverMessage}`, + + issueBody: sections.join('\n\n') + }; +} + function getDignosticsKind(event: Proto.Event) { switch (event.event) { case 'syntaxDiag': return DiagnosticKind.Syntax; @@ -895,7 +1024,7 @@ class ServerInitializingIndicator extends Disposable { vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: localize('serverLoading.progress', "Initializing JS/TS language features"), - }, () => new Promise((resolve, reject) => { + }, () => new Promise<void>((resolve, reject) => { this._task = { project: projectName, resolve, reject }; })); } diff --git a/extensions/typescript-language-features/src/typings/ref.d.ts b/extensions/typescript-language-features/src/typings/ref.d.ts index c9849d48e08..43a9cadf250 100644 --- a/extensions/typescript-language-features/src/typings/ref.d.ts +++ b/extensions/typescript-language-features/src/typings/ref.d.ts @@ -3,5 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// <reference path='../../../../src/vs/vscode.d.ts'/> /// <reference path='../../../../src/vs/vscode.proposed.d.ts'/> diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index fd2625b950e..52bcf441c42 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -9,7 +9,7 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); export default class API { - private static fromSimpleString(value: string): API { + public static fromSimpleString(value: string): API { return new API(value, value, value); } @@ -32,6 +32,10 @@ export default class API { public static readonly v345 = API.fromSimpleString('3.4.5'); public static readonly v350 = API.fromSimpleString('3.5.0'); public static readonly v380 = API.fromSimpleString('3.8.0'); + public static readonly v381 = API.fromSimpleString('3.8.1'); + public static readonly v390 = API.fromSimpleString('3.9.0'); + public static readonly v400 = API.fromSimpleString('4.0.0'); + public static readonly v401 = API.fromSimpleString('4.0.1'); public static fromVersionString(versionString: string): API { let version = semver.valid(versionString); @@ -64,6 +68,10 @@ export default class API { public readonly fullVersionString: string, ) { } + public eq(other: API): boolean { + return semver.eq(this.version, other.version); + } + public gte(other: API): boolean { return semver.gte(this.version, other.version); } diff --git a/extensions/typescript-language-features/src/utils/async.ts b/extensions/typescript-language-features/src/utils/async.ts index 069b0bffd57..a43cd07cbb4 100644 --- a/extensions/typescript-language-features/src/utils/async.ts +++ b/extensions/typescript-language-features/src/utils/async.ts @@ -12,7 +12,7 @@ export class Delayer<T> { public defaultDelay: number; private timeout: any; // Timer private completionPromise: Promise<T | null> | null; - private onSuccess: ((value?: T | Thenable<T>) => void) | null; + private onSuccess: ((value: T | PromiseLike<T> | undefined) => void) | null; private task: ITask<T> | null; constructor(defaultDelay: number) { @@ -30,7 +30,7 @@ export class Delayer<T> { } if (!this.completionPromise) { - this.completionPromise = new Promise<T>((resolve) => { + this.completionPromise = new Promise<T | undefined>((resolve) => { this.onSuccess = resolve; }).then(() => { this.completionPromise = null; @@ -59,4 +59,4 @@ export class Delayer<T> { this.timeout = null; } } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/utils/codeAction.ts b/extensions/typescript-language-features/src/utils/codeAction.ts index c45379e611a..df345e33517 100644 --- a/extensions/typescript-language-features/src/utils/codeAction.ts +++ b/extensions/typescript-language-features/src/utils/codeAction.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import * as typeConverters from './typeConverters'; @@ -42,4 +42,4 @@ export async function applyCodeActionCommands( } } return true; -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index 54c3a19b001..f549ff6f9e2 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -2,10 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import * as arrays from './arrays'; + import * as os from 'os'; import * as path from 'path'; +import * as vscode from 'vscode'; +import * as objects from '../utils/objects'; +import * as arrays from './arrays'; export enum TsServerLogLevel { Off, @@ -44,18 +46,27 @@ export namespace TsServerLogLevel { } } +export const enum SeparateSyntaxServerConfiguration { + Disabled, + Enabled, +} + export class TypeScriptServiceConfiguration { public readonly locale: string | null; public readonly globalTsdk: string | null; public readonly localTsdk: string | null; public readonly npmLocation: string | null; public readonly tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; - public readonly tsServerPluginPaths: string[]; + public readonly tsServerPluginPaths: readonly string[]; public readonly checkJs: boolean; public readonly experimentalDecorators: boolean; public readonly disableAutomaticTypeAcquisition: boolean; - public readonly useSeparateSyntaxServer: boolean; + public readonly separateSyntaxServer: SeparateSyntaxServerConfiguration; + public readonly enableProjectDiagnostics: boolean; public readonly maxTsServerMemory: number; + public readonly enablePromptUseWorkspaceTsdk: boolean; + public readonly watchOptions: protocol.WatchOptions | undefined; + public readonly includePackageJsonAutoImports: 'auto' | 'on' | 'off' | undefined; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -73,8 +84,12 @@ export class TypeScriptServiceConfiguration { this.checkJs = TypeScriptServiceConfiguration.readCheckJs(configuration); this.experimentalDecorators = TypeScriptServiceConfiguration.readExperimentalDecorators(configuration); this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration); - this.useSeparateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration); + this.separateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration); + this.enableProjectDiagnostics = TypeScriptServiceConfiguration.readEnableProjectDiagnostics(configuration); this.maxTsServerMemory = TypeScriptServiceConfiguration.readMaxTsServerMemory(configuration); + this.enablePromptUseWorkspaceTsdk = TypeScriptServiceConfiguration.readEnablePromptUseWorkspaceTsdk(configuration); + this.watchOptions = TypeScriptServiceConfiguration.readWatchOptions(configuration); + this.includePackageJsonAutoImports = TypeScriptServiceConfiguration.readIncludePackageJsonAutoImports(configuration); } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { @@ -87,8 +102,12 @@ export class TypeScriptServiceConfiguration { && this.experimentalDecorators === other.experimentalDecorators && this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths) - && this.useSeparateSyntaxServer === other.useSeparateSyntaxServer - && this.maxTsServerMemory === other.maxTsServerMemory; + && this.separateSyntaxServer === other.separateSyntaxServer + && this.enableProjectDiagnostics === other.enableProjectDiagnostics + && this.maxTsServerMemory === other.maxTsServerMemory + && objects.equals(this.watchOptions, other.watchOptions) + && this.enablePromptUseWorkspaceTsdk === other.enablePromptUseWorkspaceTsdk + && this.includePackageJsonAutoImports === other.includePackageJsonAutoImports; } private static fixPathPrefixes(inspectValue: string): string { @@ -146,8 +165,24 @@ export class TypeScriptServiceConfiguration { return configuration.get<string | null>('typescript.locale', null); } - private static readUseSeparateSyntaxServer(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get<boolean>('typescript.tsserver.useSeparateSyntaxServer', true); + private static readUseSeparateSyntaxServer(configuration: vscode.WorkspaceConfiguration): SeparateSyntaxServerConfiguration { + const value = configuration.get('typescript.tsserver.useSeparateSyntaxServer', true); + if (value === true) { + return SeparateSyntaxServerConfiguration.Enabled; + } + return SeparateSyntaxServerConfiguration.Disabled; + } + + private static readEnableProjectDiagnostics(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get<boolean>('typescript.tsserver.experimental.enableProjectDiagnostics', false); + } + + private static readWatchOptions(configuration: vscode.WorkspaceConfiguration): protocol.WatchOptions | undefined { + return configuration.get<protocol.WatchOptions>('typescript.tsserver.watchOptions'); + } + + private static readIncludePackageJsonAutoImports(configuration: vscode.WorkspaceConfiguration): 'auto' | 'on' | 'off' | undefined { + return configuration.get<'auto' | 'on' | 'off'>('typescript.preferences.includePackageJsonAutoImports'); } private static readMaxTsServerMemory(configuration: vscode.WorkspaceConfiguration): number { @@ -159,4 +194,8 @@ export class TypeScriptServiceConfiguration { } return Math.max(memoryInMB, minimumMaxMemory); } + + private static readEnablePromptUseWorkspaceTsdk(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get<boolean>('typescript.enablePromptUseWorkspaceTsdk', false); + } } diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 434379f57e1..29597ab50cb 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -4,28 +4,58 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from './api'; import { Disposable } from './dispose'; -export class ConditionalRegistration { +export class Condition extends Disposable { + private _value: boolean; + + constructor( + private readonly getValue: () => boolean, + onUpdate: (handler: () => void) => void, + ) { + super(); + this._value = this.getValue(); + + onUpdate(() => { + const newValue = this.getValue(); + if (newValue !== this._value) { + this._value = newValue; + this._onDidChange.fire(); + } + }); + } + + public get value(): boolean { return this._value; } + + private readonly _onDidChange = this._register(new vscode.EventEmitter<void>()); + public readonly onDidChange = this._onDidChange.event; +} + +class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; public constructor( - private readonly _doRegister: () => vscode.Disposable - ) { } - - public dispose() { - if (this.registration) { - this.registration.dispose(); - this.registration = undefined; + private readonly conditions: readonly Condition[], + private readonly doRegister: () => vscode.Disposable + ) { + for (const condition of conditions) { + condition.onDidChange(() => this.update()); } + this.update(); } - public update(enabled: boolean) { + public dispose() { + this.registration?.dispose(); + this.registration = undefined; + } + + private update() { + const enabled = this.conditions.every(condition => condition.value); if (enabled) { if (!this.registration) { - this.registration = this._doRegister(); + this.registration = this.doRegister(); } } else { if (this.registration) { @@ -36,56 +66,42 @@ export class ConditionalRegistration { } } -export class VersionDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly minVersion: API, - register: () => vscode.Disposable, - ) { - super(); - this._registration = new ConditionalRegistration(register); - - this.update(client.apiVersion); - - this.client.onTsServerStarted(() => { - this.update(this.client.apiVersion); - }, null, this._disposables); - } - - public dispose() { - super.dispose(); - this._registration.dispose(); - } - - private update(api: API) { - this._registration.update(api.gte(this.minVersion)); - } +export function conditionalRegistration( + conditions: readonly Condition[], + doRegister: () => vscode.Disposable, +): vscode.Disposable { + return new ConditionalRegistration(conditions, doRegister); } - -export class ConfigurationDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly language: string, - private readonly configValue: string, - register: () => vscode.Disposable, - ) { - super(); - this._registration = new ConditionalRegistration(register); - this.update(); - vscode.workspace.onDidChangeConfiguration(this.update, this, this._disposables); - } - - public dispose() { - super.dispose(); - this._registration.dispose(); - } - - private update() { - const config = vscode.workspace.getConfiguration(this.language, null); - this._registration.update(!!config.get<boolean>(this.configValue)); - } +export function requireMinVersion( + client: ITypeScriptServiceClient, + minVersion: API, +) { + return new Condition( + () => client.apiVersion.gte(minVersion), + client.onTsServerStarted + ); +} + +export function requireConfiguration( + language: string, + configValue: string, +) { + return new Condition( + () => { + const config = vscode.workspace.getConfiguration(language, null); + return !!config.get<boolean>(configValue); + }, + vscode.workspace.onDidChangeConfiguration + ); +} + +export function requireSomeCapability( + client: ITypeScriptServiceClient, + ...capabilities: readonly ClientCapability[] +) { + return new Condition( + () => capabilities.some(requiredCapability => client.capabilities.has(requiredCapability)), + client.onDidChangeCapabilities + ); } diff --git a/extensions/typescript-language-features/src/utils/documentSelector.ts b/extensions/typescript-language-features/src/utils/documentSelector.ts new file mode 100644 index 00000000000..780389320b8 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/documentSelector.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export interface DocumentSelector { + /** + * Selector for files which only require a basic syntax server. + */ + readonly syntax: readonly vscode.DocumentFilter[]; + + /** + * Selector for files which require semantic server support. + */ + readonly semantic: readonly vscode.DocumentFilter[]; +} diff --git a/extensions/typescript-language-features/src/utils/electron.ts b/extensions/typescript-language-features/src/utils/electron.ts deleted file mode 100644 index ea16bae4277..00000000000 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as temp from './temp'; -import path = require('path'); -import fs = require('fs'); -import cp = require('child_process'); -import process = require('process'); - - -const getRootTempDir = (() => { - let dir: string | undefined; - return () => { - if (!dir) { - dir = temp.getTempFile(`vscode-typescript${process.platform !== 'win32' && process.getuid ? process.getuid() : ''}`); - } - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - return dir; - }; -})(); - -export const getInstanceDir = (() => { - let dir: string | undefined; - return () => { - if (!dir) { - dir = path.join(getRootTempDir(), temp.makeRandomHexString(20)); - } - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - return dir; - }; -})(); - -export function getTempFile(prefix: string): string { - return path.join(getInstanceDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); -} - -function generatePatchedEnv(env: any, modulePath: string): any { - const newEnv = Object.assign({}, env); - - newEnv['ELECTRON_RUN_AS_NODE'] = '1'; - newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..'); - - // Ensure we always have a PATH set - newEnv['PATH'] = newEnv['PATH'] || process.env.PATH; - - return newEnv; -} - -export interface ForkOptions { - readonly cwd?: string; - readonly execArgv?: string[]; -} - -export function fork( - modulePath: string, - args: string[], - options: ForkOptions, -): cp.ChildProcess { - const newEnv = generatePatchedEnv(process.env, modulePath); - return cp.fork(modulePath, args, { - silent: true, - cwd: options.cwd, - env: newEnv, - execArgv: options.execArgv - }); -} diff --git a/extensions/typescript-language-features/src/utils/errorCodes.ts b/extensions/typescript-language-features/src/utils/errorCodes.ts new file mode 100644 index 00000000000..2f554e96584 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/errorCodes.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const variableDeclaredButNeverUsed = new Set([6196, 6133]); +export const propertyDeclaretedButNeverUsed = new Set([6138]); +export const allImportsAreUnused = new Set([6192]); +export const unreachableCode = new Set([7027]); +export const unusedLabel = new Set([7028]); +export const fallThroughCaseInSwitch = new Set([7029]); +export const notAllCodePathsReturnAValue = new Set([7030]); +export const incorrectlyImplementsInterface = new Set([2420]); +export const cannotFindName = new Set([2552, 2304]); +export const extendsShouldBeImplements = new Set([2689]); +export const asyncOnlyAllowedInAsyncFunctions = new Set([1308]); diff --git a/extensions/typescript-language-features/src/utils/fileSchemes.ts b/extensions/typescript-language-features/src/utils/fileSchemes.ts index 6efcfab4d3b..b072923b39a 100644 --- a/extensions/typescript-language-features/src/utils/fileSchemes.ts +++ b/extensions/typescript-language-features/src/utils/fileSchemes.ts @@ -6,14 +6,20 @@ export const file = 'file'; export const untitled = 'untitled'; export const git = 'git'; +/** Live share scheme */ +export const vsls = 'vsls'; export const walkThroughSnippet = 'walkThroughSnippet'; -export const supportedSchemes = [ +export const semanticSupportedSchemes = [ file, untitled, - walkThroughSnippet + walkThroughSnippet, ]; -export function isSupportedScheme(scheme: string): boolean { - return supportedSchemes.indexOf(scheme) >= 0; -} +/** + * File scheme for which JS/TS language feature should be disabled + */ +export const disabledSchemes = new Set([ + git, + vsls +]); diff --git a/extensions/typescript-language-features/src/utils/fileSystem.electron.ts b/extensions/typescript-language-features/src/utils/fileSystem.electron.ts new file mode 100644 index 00000000000..3a6711224e2 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/fileSystem.electron.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { getTempFile } from './temp.electron'; + +export const onCaseInsenitiveFileSystem = (() => { + let value: boolean | undefined; + return (): boolean => { + if (typeof value === 'undefined') { + if (process.platform === 'win32') { + value = true; + } else if (process.platform !== 'darwin') { + value = false; + } else { + const temp = getTempFile('typescript-case-check'); + fs.writeFileSync(temp, ''); + value = fs.existsSync(temp.toUpperCase()); + } + } + return value; + }; +})(); diff --git a/extensions/typescript-language-features/src/utils/fixNames.ts b/extensions/typescript-language-features/src/utils/fixNames.ts new file mode 100644 index 00000000000..98e47f0ebb2 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/fixNames.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const annotateWithTypeFromJSDoc = 'annotateWithTypeFromJSDoc'; +export const constructorForDerivedNeedSuperCall = 'constructorForDerivedNeedSuperCall'; +export const extendsInterfaceBecomesImplements = 'extendsInterfaceBecomesImplements'; +export const awaitInSyncFunction = 'fixAwaitInSyncFunction'; +export const classIncorrectlyImplementsInterface = 'fixClassIncorrectlyImplementsInterface'; +export const classDoesntImplementInheritedAbstractMember = 'fixClassDoesntImplementInheritedAbstractMember'; +export const unreachableCode = 'fixUnreachableCode'; +export const unusedIdentifier = 'unusedIdentifier'; +export const forgottenThisPropertyAccess = 'forgottenThisPropertyAccess'; +export const spelling = 'spelling'; +export const fixImport = 'import'; +export const addMissingAwait = 'addMissingAwait'; diff --git a/extensions/typescript-language-features/src/utils/fs.ts b/extensions/typescript-language-features/src/utils/fs.ts new file mode 100644 index 00000000000..88ce3e3aa75 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/fs.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export const exists = async (resource: vscode.Uri): Promise<boolean> => { + try { + const stat = await vscode.workspace.fs.stat(resource); + // stat.type is an enum flag + return !!(stat.type & vscode.FileType.File); + } catch { + return false; + } +}; diff --git a/extensions/typescript-language-features/src/utils/projectStatus.ts b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts similarity index 91% rename from extensions/typescript-language-features/src/utils/projectStatus.ts rename to extensions/typescript-language-features/src/utils/largeProjectStatus.ts index b9a3b2328c7..223d7cb4716 100644 --- a/extensions/typescript-language-features/src/utils/projectStatus.ts +++ b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { loadMessageBundle } from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; import { TelemetryReporter } from './telemetry'; -import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './tsconfig'; +import { isImplicitProjectConfigFile, openOrCreateConfig, ProjectType } from './tsconfig'; const localize = loadMessageBundle(); @@ -101,8 +101,8 @@ function onConfigureExcludesSelected( } else { const root = client.getWorkspaceRootForResource(vscode.Uri.file(configFileName)); if (root) { - openOrCreateConfigFile( - configFileName.match(/tsconfig\.?.*\.json/) !== null, + openOrCreateConfig( + /tsconfig\.?.*\.json/.test(configFileName) ? ProjectType.TypeScript : ProjectType.JavaScript, root, client.configuration); } @@ -111,16 +111,15 @@ function onConfigureExcludesSelected( export function create( client: ITypeScriptServiceClient, - telemetryReporter: TelemetryReporter -) { +): vscode.Disposable { const toDispose: vscode.Disposable[] = []; - const item = new ExcludeHintItem(telemetryReporter); + const item = new ExcludeHintItem(client.telemetryReporter); toDispose.push(vscode.commands.registerCommand('js.projectStatus.command', () => { if (item.configFileName) { onConfigureExcludesSelected(client, item.configFileName); } - let { message } = item.getCurrentHint(); + const { message } = item.getCurrentHint(); return vscode.window.showInformationMessage(message); })); @@ -128,4 +127,3 @@ export function create( return vscode.Disposable.from(...toDispose); } - diff --git a/extensions/typescript-language-features/src/utils/logger.ts b/extensions/typescript-language-features/src/utils/logger.ts index 18317e58e00..6afb44136d9 100644 --- a/extensions/typescript-language-features/src/utils/logger.ts +++ b/extensions/typescript-language-features/src/utils/logger.ts @@ -11,7 +11,7 @@ const localize = nls.loadMessageBundle(); type LogLevel = 'Trace' | 'Info' | 'Error'; -export default class Logger { +export class Logger { @memoize private get output(): vscode.OutputChannel { @@ -33,7 +33,7 @@ export default class Logger { } public error(message: string, data?: any): void { - // See https://github.com/Microsoft/TypeScript/issues/10496 + // See https://github.com/microsoft/TypeScript/issues/10496 if (data && data.message === 'No content available.') { return; } diff --git a/extensions/typescript-language-features/src/utils/modifiers.ts b/extensions/typescript-language-features/src/utils/modifiers.ts new file mode 100644 index 00000000000..589b5da3d52 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/modifiers.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function parseKindModifier(kindModifiers: string): Set<string> { + return new Set(kindModifiers.split(/,|\s+/g)); +} diff --git a/extensions/typescript-language-features/src/utils/objects.ts b/extensions/typescript-language-features/src/utils/objects.ts new file mode 100644 index 00000000000..a31467bd8d6 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/objects.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as array from './arrays'; + +export function equals(one: any, other: any): boolean { + if (one === other) { + return true; + } + if (one === null || one === undefined || other === null || other === undefined) { + return false; + } + if (typeof one !== typeof other) { + return false; + } + if (typeof one !== 'object') { + return false; + } + if (Array.isArray(one) !== Array.isArray(other)) { + return false; + } + + if (Array.isArray(one)) { + return array.equals(one, other, equals); + } else { + const oneKeys: string[] = []; + for (const key in one) { + oneKeys.push(key); + } + oneKeys.sort(); + const otherKeys: string[] = []; + for (const key in other) { + otherKeys.push(key); + } + otherKeys.sort(); + if (!array.equals(oneKeys, otherKeys)) { + return false; + } + return oneKeys.every(key => equals(one[key], other[key])); + } +} diff --git a/extensions/typescript-language-features/src/utils/platform.ts b/extensions/typescript-language-features/src/utils/platform.ts new file mode 100644 index 00000000000..2d754bf4054 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/platform.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function isWeb(): boolean { + // @ts-expect-error + return typeof navigator !== 'undefined' && vscode.env.uiKind === vscode.UIKind.Web; +} diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts index 71ce2430cb8..6d0708b15db 100644 --- a/extensions/typescript-language-features/src/utils/plugins.ts +++ b/extensions/typescript-language-features/src/utils/plugins.ts @@ -12,6 +12,7 @@ export interface TypeScriptServerPlugin { readonly name: string; readonly enableForWorkspaceTypeScriptVersions: boolean; readonly languages: ReadonlyArray<string>; + readonly configNamespace?: string } namespace TypeScriptServerPlugin { @@ -77,6 +78,7 @@ export class PluginManager extends Disposable { enableForWorkspaceTypeScriptVersions: !!plugin.enableForWorkspaceTypeScriptVersions, path: extension.extensionPath, languages: Array.isArray(plugin.languages) ? plugin.languages : [], + configNamespace: plugin.configNamespace, }); } if (plugins.length) { @@ -86,4 +88,4 @@ export class PluginManager extends Disposable { } return pluginMap; } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/utils/previewer.ts b/extensions/typescript-language-features/src/utils/previewer.ts index ca1da7b6e94..3d059dca331 100644 --- a/extensions/typescript-language-features/src/utils/previewer.ts +++ b/extensions/typescript-language-features/src/utils/previewer.ts @@ -4,7 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; + +function replaceLinks(text: string): string { + return text + // Http(s) links + .replace(/\{@(link|linkplain|linkcode) (https?:\/\/[^ |}]+?)(?:[| ]([^{}\n]+?))?\}/gi, (_, tag: string, link: string, text?: string) => { + switch (tag) { + case 'linkcode': + return `[\`${text ? text.trim() : link}\`](${link})`; + + default: + return `[${text ? text.trim() : link}](${link})`; + } + }); +} + +function processInlineTags(text: string): string { + return replaceLinks(text); +} function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined { if (!tag.text) { @@ -41,7 +59,7 @@ function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined { return makeCodeblock(tag.text); } - return tag.text; + return processInlineTags(tag.text); } function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined { @@ -50,15 +68,15 @@ function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined { case 'extends': case 'param': case 'template': - const body = (tag.text || '').split(/^([\w\.]+)\s*-?\s*/); - if (body && body.length === 3) { + const body = (tag.text || '').split(/^(\S+)\s*-?\s*/); + if (body?.length === 3) { const param = body[1]; const doc = body[2]; const label = `*@${tag.name}* \`${param}\``; if (!doc) { return label; } - return label + (doc.match(/\r\n|\n/g) ? ' \n' + doc : ` — ${doc}`); + return label + (doc.match(/\r\n|\n/g) ? ' \n' + processInlineTags(doc) : ` — ${processInlineTags(doc)}`); } } @@ -71,21 +89,19 @@ function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined { return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` — ${text}`); } -export function plain(parts: Proto.SymbolDisplayPart[]): string { - if (!parts) { - return ''; - } - return parts.map(part => part.text).join(''); +export function plain(parts: Proto.SymbolDisplayPart[] | string): string { + return processInlineTags( + typeof parts === 'string' + ? parts + : parts.map(part => part.text).join('')); } export function tagsMarkdownPreview(tags: Proto.JSDocTagInfo[]): string { - return (tags || []) - .map(getTagDocumentation) - .join(' \n\n'); + return tags.map(getTagDocumentation).join(' \n\n'); } export function markdownDocumentation( - documentation: Proto.SymbolDisplayPart[], + documentation: Proto.SymbolDisplayPart[] | string, tags: Proto.JSDocTagInfo[] ): vscode.MarkdownString { const out = new vscode.MarkdownString(); @@ -95,7 +111,7 @@ export function markdownDocumentation( export function addMarkdownDocumentation( out: vscode.MarkdownString, - documentation: Proto.SymbolDisplayPart[] | undefined, + documentation: Proto.SymbolDisplayPart[] | string | undefined, tags: Proto.JSDocTagInfo[] | undefined ): vscode.MarkdownString { if (documentation) { diff --git a/extensions/typescript-language-features/src/utils/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index e942fae4583..ea4889ded35 100644 --- a/extensions/typescript-language-features/src/utils/resourceMap.ts +++ b/extensions/typescript-language-features/src/utils/resourceMap.ts @@ -3,10 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as vscode from 'vscode'; -import { memoize } from './memoize'; -import { getTempFile } from './temp'; /** * Maps of file resources @@ -18,7 +15,10 @@ export class ResourceMap<T> { private readonly _map = new Map<string, { resource: vscode.Uri, value: T }>(); constructor( - private readonly _normalizePath: (resource: vscode.Uri) => string | undefined = (resource) => resource.fsPath + private readonly _normalizePath: (resource: vscode.Uri) => string | undefined = (resource) => resource.fsPath, + protected readonly config: { + readonly onCaseInsenitiveFileSystem: boolean, + }, ) { } public get size() { @@ -83,23 +83,10 @@ export class ResourceMap<T> { if (isWindowsPath(path)) { return true; } - return path[0] === '/' && this.onIsCaseInsenitiveFileSystem; - } - - @memoize - private get onIsCaseInsenitiveFileSystem() { - if (process.platform === 'win32') { - return true; - } - if (process.platform !== 'darwin') { - return false; - } - const temp = getTempFile('typescript-case-check'); - fs.writeFileSync(temp, ''); - return fs.existsSync(temp.toUpperCase()); + return path[0] === '/' && this.config.onCaseInsenitiveFileSystem; } } -export function isWindowsPath(path: string): boolean { +function isWindowsPath(path: string): boolean { return /^[a-zA-Z]:[\/\\]/.test(path); } diff --git a/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts index fbf2868d101..e0185db581b 100644 --- a/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts +++ b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; export function snippetForFunctionCall( @@ -73,7 +73,9 @@ function getParameterListParts( const next = displayParts[i + 1]; // Skip optional parameters const nameIsFollowedByOptionalIndicator = next && next.text === '?'; - if (!nameIsFollowedByOptionalIndicator) { + // Skip this parameter + const nameIsThis = part.text === 'this'; + if (!nameIsFollowedByOptionalIndicator && !nameIsThis) { parts.push(part); } hasOptionalParameters = hasOptionalParameters || nameIsFollowedByOptionalIndicator; diff --git a/extensions/typescript-language-features/src/utils/surveyor.ts b/extensions/typescript-language-features/src/utils/surveyor.ts deleted file mode 100644 index a98e84f7c33..00000000000 --- a/extensions/typescript-language-features/src/utils/surveyor.ts +++ /dev/null @@ -1,214 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import TypeScriptServiceClient from '../typescriptServiceClient'; -import { Disposable } from './dispose'; -import { memoize } from './memoize'; - -const localize = nls.loadMessageBundle(); - -interface SurveyData { - /** - * Internal id of the survey. Comes from TypeScript. - */ - readonly id: string; - - /** - * Text displayed to the user when survey is triggered. - */ - readonly prompt: string; - - /** - * Number of times to trigger with `surveyReady` before showing the survey. - * - * This is cumulative and shared across workspaces. - */ - readonly globalTriggerThreshold: number; - - /** - * Survey link. - */ - readonly url: vscode.Uri; - - /** - * Milliseconds to wait after 'Remind later' is chosen before trying to prompt again. - */ - readonly remindLaterDelayInMilliseconds: number; -} - -const allSurveys: ReadonlyArray<SurveyData> = [ - { - id: 'checkJs', - prompt: localize('survey.checkJs.prompt', "Help improve VS Code's support for [checkJs](https://code.visualstudio.com/Docs/languages/javascript#_type-checking) in JavaScript! Since you have been using this feature, would you consider taking a short survey about your experience?"), - globalTriggerThreshold: 10, - url: vscode.Uri.parse('https://www.surveymonkey.com/r/FH8PZQ3'), - remindLaterDelayInMilliseconds: 3 * 24 * 60 * 60 * 1000 // 3 days - } -]; - -class Survey { - - private _hasShownInThisSession = false; - - public constructor( - private readonly data: SurveyData, - private readonly memento: vscode.Memento - ) { } - - public get id(): string { return this.data.id; } - - public get prompt(): string { return this.data.prompt; } - - public get isActive(): boolean { - return !this._hasShownInThisSession && !this.memento.get<boolean>(this.isCompletedMementoKey); - } - - public open(): void { - this.markComplete(); - vscode.commands.executeCommand<void>('vscode.open', this.data.url); - } - - public remindLater(): void { - // Make sure we don't show again in this session (but don't persist as completed) - this._hasShownInThisSession = true; - - // And save off prompt time. - this.memento.update(this.lastPromptTimeMementoKey, Date.now()); - } - - public trigger(): boolean { - const triggerCount = this.triggerCount + 1; - this.memento.update(this.triggerCountMementoKey, triggerCount); - if (triggerCount >= this.data.globalTriggerThreshold) { - const lastPromptTime = this.memento.get<number>(this.lastPromptTimeMementoKey); - if (!lastPromptTime || isNaN(+lastPromptTime)) { - return true; - } - return (lastPromptTime + this.data.remindLaterDelayInMilliseconds < Date.now()); - } - return false; - } - - public willShow() { - this._hasShownInThisSession = true; - } - - public markComplete() { - this._hasShownInThisSession = true; - this.memento.update(this.isCompletedMementoKey, true); - } - - private get triggerCount(): number { - const count = this.memento.get<number>(this.triggerCountMementoKey); - return !count || isNaN(+count) ? 0 : +count; - } - - private getMementoKey(part: string): string { - return `survey.v0.${this.id}.${part}`; - } - - private get isCompletedMementoKey(): string { - return this.getMementoKey('isComplete'); - } - - private get lastPromptTimeMementoKey(): string { - return this.getMementoKey('lastPromptTime'); - } - - private get triggerCountMementoKey(): string { - return this.getMementoKey('globalTriggerCount'); - } -} - -export class Surveyor extends Disposable { - - public constructor( - private readonly memento: vscode.Memento, - serviceClient: TypeScriptServiceClient, - ) { - super(); - - this._register(serviceClient.onSurveyReady(e => this.surveyReady(e.surveyId))); - } - - @memoize - private get surveys(): Map<string, Survey> { - return new Map<string, Survey>( - allSurveys.map(data => [data.id, new Survey(data, this.memento)] as [string, Survey])); - } - - private surveyReady(surveyId: string): void { - const survey = this.tryGetActiveSurvey(surveyId); - if (survey && survey.trigger()) { - survey.willShow(); - this.showSurveyToUser(survey); - } - } - - private async showSurveyToUser(survey: Survey): Promise<void> { - enum Choice { - GoToSurvey = 1, - RemindLater = 2, - NeverAgain = 3, - } - - interface MessageItem extends vscode.MessageItem { - readonly choice: Choice; - } - - const response = await vscode.window.showInformationMessage<MessageItem>(survey.prompt, - { - title: localize('takeShortSurvey', "Take Short Survey"), - choice: Choice.GoToSurvey - }, { - title: localize('remindLater', "Remind Me Later"), - choice: Choice.RemindLater - }, { - title: localize('neverAgain', "Disable JS/TS Surveys"), - choice: Choice.NeverAgain - }); - - switch (response && response.choice) { - case Choice.GoToSurvey: - survey.open(); - break; - - case Choice.NeverAgain: - survey.markComplete(); - this.disableSurveys(); - break; - - case Choice.RemindLater: - default: // If user just closes the notification, treat this as a remind later. - survey.remindLater(); - break; - } - } - - private tryGetActiveSurvey(surveyId: string): Survey | undefined { - const survey = this.surveys.get(surveyId); - if (!survey) { - return undefined; - } - - if (this.areSurveysEnabled() && survey.isActive) { - return survey; - } - - return undefined; - } - - private areSurveysEnabled() { - const config = vscode.workspace.getConfiguration('typescript'); - return config.get<boolean>('surveys.enabled', true); - } - - private disableSurveys() { - const config = vscode.workspace.getConfiguration('typescript'); - config.update('surveys.enabled', false); - } -} diff --git a/extensions/typescript-language-features/src/utils/temp.electron.ts b/extensions/typescript-language-features/src/utils/temp.electron.ts new file mode 100644 index 00000000000..cd79b36415d --- /dev/null +++ b/extensions/typescript-language-features/src/utils/temp.electron.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as os from 'os'; +import * as fs from 'fs'; +import * as path from 'path'; + +function makeRandomHexString(length: number): string { + const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; + let result = ''; + for (let i = 0; i < length; i++) { + const idx = Math.floor(chars.length * Math.random()); + result += chars[idx]; + } + return result; +} + +const getRootTempDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + const filename = `vscode-typescript${process.platform !== 'win32' && process.getuid ? process.getuid() : ''}`; + dir = path.join(os.tmpdir(), filename); + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + return dir; + }; +})(); + +export const getInstanceTempDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + dir = path.join(getRootTempDir(), makeRandomHexString(20)); + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + return dir; + }; +})(); + +export function getTempFile(prefix: string): string { + return path.join(getInstanceTempDir(), `${prefix}-${makeRandomHexString(20)}.tmp`); +} diff --git a/extensions/typescript-language-features/src/utils/temp.ts b/extensions/typescript-language-features/src/utils/temp.ts deleted file mode 100644 index 2af5f1732b0..00000000000 --- a/extensions/typescript-language-features/src/utils/temp.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import path = require('path'); -import os = require('os'); - -export function makeRandomHexString(length: number): string { - const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; - let result = ''; - for (let i = 0; i < length; i++) { - const idx = Math.floor(chars.length * Math.random()); - result += chars[idx]; - } - return result; -} - -export function getTempFile(name: string): string { - return path.join(os.tmpdir(), name); -} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/tracer.ts b/extensions/typescript-language-features/src/utils/tracer.ts index b8e5b189a8b..445b828d0c6 100644 --- a/extensions/typescript-language-features/src/utils/tracer.ts +++ b/extensions/typescript-language-features/src/utils/tracer.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import Logger from './logger'; +import type * as Proto from '../protocol'; +import { Logger } from './logger'; enum Trace { Off, diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index df29267c79c..f6802ca3ccc 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -5,15 +5,25 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import * as nls from 'vscode-nls'; +import type * as Proto from '../protocol'; +import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { nulToken } from '../utils/cancellation'; import { TypeScriptServiceConfiguration } from './configuration'; +const localize = nls.loadMessageBundle(); + +export const enum ProjectType { + TypeScript, + JavaScript, +} + export function isImplicitProjectConfigFile(configFileName: string) { return configFileName.startsWith('/dev/null/'); } export function inferredProjectCompilerOptions( - isTypeScriptProject: boolean, + projectType: ProjectType, serviceConfig: TypeScriptServiceConfiguration, ): Proto.ExternalProjectCompilerOptions { const projectConfig: Proto.ExternalProjectCompilerOptions = { @@ -24,7 +34,7 @@ export function inferredProjectCompilerOptions( if (serviceConfig.checkJs) { projectConfig.checkJs = true; - if (isTypeScriptProject) { + if (projectType === ProjectType.TypeScript) { projectConfig.allowJs = true; } } @@ -33,7 +43,7 @@ export function inferredProjectCompilerOptions( projectConfig.experimentalDecorators = true; } - if (isTypeScriptProject) { + if (projectType === ProjectType.TypeScript) { projectConfig.sourceMap = true; } @@ -41,10 +51,10 @@ export function inferredProjectCompilerOptions( } function inferredProjectConfigSnippet( - isTypeScriptProject: boolean, + projectType: ProjectType, config: TypeScriptServiceConfiguration ) { - const baseConfig = inferredProjectCompilerOptions(isTypeScriptProject, config); + const baseConfig = inferredProjectCompilerOptions(projectType, config); const compilerOptions = Object.keys(baseConfig).map(key => `"${key}": ${JSON.stringify(baseConfig[key])}`); return new vscode.SnippetString(`{ "compilerOptions": { @@ -57,13 +67,13 @@ function inferredProjectConfigSnippet( }`); } -export async function openOrCreateConfigFile( - isTypeScriptProject: boolean, +export async function openOrCreateConfig( + projectType: ProjectType, rootPath: string, - config: TypeScriptServiceConfiguration + configuration: TypeScriptServiceConfiguration, ): Promise<vscode.TextEditor | null> { - const configFile = vscode.Uri.file(path.join(rootPath, isTypeScriptProject ? 'tsconfig.json' : 'jsconfig.json')); - const col = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; + const configFile = vscode.Uri.file(path.join(rootPath, projectType === ProjectType.TypeScript ? 'tsconfig.json' : 'jsconfig.json')); + const col = vscode.window.activeTextEditor?.viewColumn; try { const doc = await vscode.workspace.openTextDocument(configFile); return vscode.window.showTextDocument(doc, col); @@ -71,8 +81,79 @@ export async function openOrCreateConfigFile( const doc = await vscode.workspace.openTextDocument(configFile.with({ scheme: 'untitled' })); const editor = await vscode.window.showTextDocument(doc, col); if (editor.document.getText().length === 0) { - await editor.insertSnippet(inferredProjectConfigSnippet(isTypeScriptProject, config)); + await editor.insertSnippet(inferredProjectConfigSnippet(projectType, configuration)); } return editor; } } + +export async function openProjectConfigOrPromptToCreate( + projectType: ProjectType, + client: ITypeScriptServiceClient, + rootPath: string, + configFileName: string, +): Promise<void> { + if (!isImplicitProjectConfigFile(configFileName)) { + const doc = await vscode.workspace.openTextDocument(configFileName); + vscode.window.showTextDocument(doc, vscode.window.activeTextEditor?.viewColumn); + return; + } + + const CreateConfigItem: vscode.MessageItem = { + title: projectType === ProjectType.TypeScript + ? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json') + : localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'), + }; + + const selected = await vscode.window.showInformationMessage( + (projectType === ProjectType.TypeScript + ? localize('typescript.noTypeScriptProjectConfig', 'File is not part of a TypeScript project. Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=841896') + : localize('typescript.noJavaScriptProjectConfig', 'File is not part of a JavaScript project Click [here]({0}) to learn more.', 'https://go.microsoft.com/fwlink/?linkid=759670') + ), + CreateConfigItem); + + switch (selected) { + case CreateConfigItem: + openOrCreateConfig(projectType, rootPath, client.configuration); + return; + } +} + +export async function openProjectConfigForFile( + projectType: ProjectType, + client: ITypeScriptServiceClient, + resource: vscode.Uri, +): Promise<void> { + const rootPath = client.getWorkspaceRootForResource(resource); + if (!rootPath) { + vscode.window.showInformationMessage( + localize( + 'typescript.projectConfigNoWorkspace', + 'Please open a folder in VS Code to use a TypeScript or JavaScript project')); + return; + } + + const file = client.toPath(resource); + // TSServer errors when 'projectInfo' is invoked on a non js/ts file + if (!file || !await client.toPath(resource)) { + vscode.window.showWarningMessage( + localize( + 'typescript.projectConfigUnsupportedFile', + 'Could not determine TypeScript or JavaScript project. Unsupported file type')); + return; + } + + let res: ServerResponse.Response<protocol.ProjectInfoResponse> | undefined; + try { + res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); + } catch { + // noop + } + + if (res?.type !== 'response' || !res.body) { + vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project')); + return; + } + return openProjectConfigOrPromptToCreate(projectType, client, rootPath, res.body.configFileName); +} + diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index 2ccf306885f..78333b2da0b 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -8,7 +8,7 @@ */ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; @@ -107,7 +107,7 @@ export namespace SymbolKind { case PConst.Kind.interface: return vscode.SymbolKind.Interface; case PConst.Kind.indexSignature: return vscode.SymbolKind.Method; case PConst.Kind.callSignature: return vscode.SymbolKind.Method; - case PConst.Kind.memberFunction: return vscode.SymbolKind.Method; + case PConst.Kind.method: return vscode.SymbolKind.Method; case PConst.Kind.memberVariable: return vscode.SymbolKind.Property; case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Property; case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Property; diff --git a/extensions/typescript-language-features/src/utils/typingsStatus.ts b/extensions/typescript-language-features/src/utils/typingsStatus.ts index a070b205621..efb9a540e04 100644 --- a/extensions/typescript-language-features/src/utils/typingsStatus.ts +++ b/extensions/typescript-language-features/src/utils/typingsStatus.ts @@ -75,7 +75,7 @@ export class AtaProgressReporter extends Disposable { private _onBegin(eventId: number): void { const handle = setTimeout(() => this._onEndOrTimeout(eventId), typingsInstallTimeout); - const promise = new Promise(resolve => { + const promise = new Promise<void>(resolve => { this._promises.set(eventId, () => { clearTimeout(handle); resolve(); @@ -96,32 +96,24 @@ export class AtaProgressReporter extends Disposable { } } - private onTypesInstallerInitializationFailed() { - interface MyMessageItem extends vscode.MessageItem { - id: number; - } + private async onTypesInstallerInitializationFailed() { + const config = vscode.workspace.getConfiguration('typescript'); - if (vscode.workspace.getConfiguration('typescript').get<boolean>('check.npmIsInstalled', true)) { - vscode.window.showWarningMessage<MyMessageItem>( + if (config.get<boolean>('check.npmIsInstalled', true)) { + const dontShowAgain: vscode.MessageItem = { + title: localize('typesInstallerInitializationFailed.doNotCheckAgain', "Don't Show Again"), + }; + const selected = await vscode.window.showWarningMessage( localize( 'typesInstallerInitializationFailed.title', "Could not install typings files for JavaScript language features. Please ensure that NPM is installed or configure 'typescript.npm' in your user settings. Click [here]({0}) to learn more.", 'https://go.microsoft.com/fwlink/?linkid=847635' - ), { - title: localize('typesInstallerInitializationFailed.doNotCheckAgain', "Don't Show Again"), - id: 1 + ), + dontShowAgain); + + if (selected === dontShowAgain) { + config.update('check.npmIsInstalled', false, true); } - ).then(selected => { - if (!selected) { - return; - } - switch (selected.id) { - case 1: - const tsConfig = vscode.workspace.getConfiguration('typescript'); - tsConfig.update('check.npmIsInstalled', false, true); - break; - } - }); } } } diff --git a/extensions/typescript-language-features/src/utils/versionPicker.ts b/extensions/typescript-language-features/src/utils/versionPicker.ts deleted file mode 100644 index 20bae2ab64d..00000000000 --- a/extensions/typescript-language-features/src/utils/versionPicker.ts +++ /dev/null @@ -1,123 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './versionProvider'; - -const localize = nls.loadMessageBundle(); - -const useWorkspaceTsdkStorageKey = 'typescript.useWorkspaceTsdk'; - -interface MyQuickPickItem extends vscode.QuickPickItem { - id: MessageAction; - version?: TypeScriptVersion; -} - -enum MessageAction { - useLocal, - useBundled, - learnMore, -} - -export class TypeScriptVersionPicker { - private _currentVersion: TypeScriptVersion; - - public constructor( - private readonly versionProvider: TypeScriptVersionProvider, - private readonly workspaceState: vscode.Memento - ) { - this._currentVersion = this.versionProvider.defaultVersion; - - if (this.useWorkspaceTsdkSetting) { - const localVersion = this.versionProvider.localVersion; - if (localVersion) { - this._currentVersion = localVersion; - } - } - } - - public get useWorkspaceTsdkSetting(): boolean { - return this.workspaceState.get<boolean>(useWorkspaceTsdkStorageKey, false); - } - - public get currentVersion(): TypeScriptVersion { - return this._currentVersion; - } - - public useBundledVersion(): void { - this._currentVersion = this.versionProvider.bundledVersion; - } - - public async show(firstRun?: boolean): Promise<{ oldVersion?: TypeScriptVersion, newVersion?: TypeScriptVersion }> { - const pickOptions: MyQuickPickItem[] = []; - - const shippedVersion = this.versionProvider.defaultVersion; - pickOptions.push({ - label: (!this.useWorkspaceTsdkSetting - ? '• ' - : '') + localize('useVSCodeVersionOption', "Use VS Code's Version"), - description: shippedVersion.displayName, - detail: shippedVersion.pathLabel, - id: MessageAction.useBundled, - }); - - for (const version of this.versionProvider.localVersions) { - pickOptions.push({ - label: (this.useWorkspaceTsdkSetting && this.currentVersion.path === version.path - ? '• ' - : '') + localize('useWorkspaceVersionOption', "Use Workspace Version"), - description: version.displayName, - detail: version.pathLabel, - id: MessageAction.useLocal, - version - }); - } - - pickOptions.push({ - label: localize('learnMore', 'Learn More'), - description: '', - id: MessageAction.learnMore - }); - - const selected = await vscode.window.showQuickPick<MyQuickPickItem>(pickOptions, { - placeHolder: localize( - 'selectTsVersion', - "Select the TypeScript version used for JavaScript and TypeScript language features"), - ignoreFocusOut: firstRun, - }); - - if (!selected) { - return { oldVersion: this.currentVersion }; - } - - switch (selected.id) { - case MessageAction.useLocal: - await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); - if (selected.version) { - const tsConfig = vscode.workspace.getConfiguration('typescript'); - await tsConfig.update('tsdk', selected.version.pathLabel, false); - - const previousVersion = this.currentVersion; - this._currentVersion = selected.version; - return { oldVersion: previousVersion, newVersion: selected.version }; - } - return { oldVersion: this.currentVersion }; - - case MessageAction.useBundled: - await this.workspaceState.update(useWorkspaceTsdkStorageKey, false); - const previousVersion = this.currentVersion; - this._currentVersion = shippedVersion; - return { oldVersion: previousVersion, newVersion: shippedVersion }; - - case MessageAction.learnMore: - vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); - return { oldVersion: this.currentVersion }; - - default: - return { oldVersion: this.currentVersion }; - } - } -} diff --git a/extensions/typescript-language-features/src/utils/versionStatus.ts b/extensions/typescript-language-features/src/utils/versionStatus.ts deleted file mode 100644 index 9d6ebeda69e..00000000000 --- a/extensions/typescript-language-features/src/utils/versionStatus.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as languageModeIds from './languageModeIds'; -import { TypeScriptVersion } from './versionProvider'; -import { Disposable } from './dispose'; -import * as nls from 'vscode-nls'; - -const localize = nls.loadMessageBundle(); - -export default class VersionStatus extends Disposable { - private readonly _versionBarEntry: vscode.StatusBarItem; - - constructor( - private readonly _normalizePath: (resource: vscode.Uri) => string | undefined - ) { - super(); - this._versionBarEntry = this._register(vscode.window.createStatusBarItem({ - id: 'status.typescript.version', - name: localize('typescriptVersion', "TypeScript: Version"), - alignment: vscode.StatusBarAlignment.Right, - priority: 99 /* to the right of editor status (100) */ - })); - vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this, this._disposables); - } - - public onDidChangeTypeScriptVersion(version: TypeScriptVersion) { - this.showHideStatus(); - this._versionBarEntry.text = version.displayName; - this._versionBarEntry.tooltip = version.path; - this._versionBarEntry.command = 'typescript.selectTypeScriptVersion'; - } - - private showHideStatus() { - if (!vscode.window.activeTextEditor) { - this._versionBarEntry.hide(); - return; - } - - const doc = vscode.window.activeTextEditor.document; - if (vscode.languages.match([languageModeIds.typescript, languageModeIds.typescriptreact], doc)) { - if (this._normalizePath(doc.uri)) { - this._versionBarEntry.show(); - } else { - this._versionBarEntry.hide(); - } - return; - } - - if (!vscode.window.activeTextEditor.viewColumn) { - // viewColumn is undefined for the debug/output panel, but we still want - // to show the version info in the existing editor - return; - } - - this._versionBarEntry.hide(); - } -} diff --git a/extensions/typescript-language-features/test-workspace/bar.ts b/extensions/typescript-language-features/test-workspace/bar.ts new file mode 100644 index 00000000000..4098e94951d --- /dev/null +++ b/extensions/typescript-language-features/test-workspace/bar.ts @@ -0,0 +1 @@ +// export const foo = 1; \ No newline at end of file diff --git a/extensions/typescript-language-features/test-workspace/foo.ts b/extensions/typescript-language-features/test-workspace/foo.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/typescript-language-features/test-workspace/foojs.js b/extensions/typescript-language-features/test-workspace/foojs.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/typescript-language-features/test-workspace/index.ts b/extensions/typescript-language-features/test-workspace/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/typescript-language-features/test-workspace/tsconfig.json b/extensions/typescript-language-features/test-workspace/tsconfig.json new file mode 100644 index 00000000000..bf3bdd34f19 --- /dev/null +++ b/extensions/typescript-language-features/test-workspace/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "es2018", + "noEmit": true + } +} diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index d83a0817920..7a2eafbfa84 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -2,9 +2,16 @@ "extends": "../shared.tsconfig.json", "compilerOptions": { "outDir": "./out", - "experimentalDecorators": true + "experimentalDecorators": true, + // https://github.com/microsoft/TypeScript/issues/31869#issuecomment-515167432 + "baseUrl": "src/\u0000", + "paths": { + "vscode": [ + "../../../../src/vs/vscode.d.ts" + ] + } }, "include": [ "src/**/*" ] -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index bb261f27279..4ef8f032788 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -2,34 +2,66 @@ # yarn lockfile v1 -"@types/events@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + +"@npmcli/move-file@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" + integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw== + dependencies: + mkdirp "^1.0.4" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@types/glob@*": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" - integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== dependencies: - "@types/events" "*" "@types/minimatch" "*" "@types/node" "*" +"@types/json-schema@^7.0.5": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.6.tgz#076028d0b0400be8105b89a0a55550c86684ffec" - integrity "sha1-B2Ao0LBAC+gQW4mgpVVQyGaE/+w= sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==" + version "14.11.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" + integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== "@types/node@^12.11.7": - version "12.12.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.24.tgz#d4606afd8cf6c609036b854360367d1b2c78931f" - integrity "sha1-1GBq/Yz2xgkDa4VDYDZ9Gyx4kx8= sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug==" + version "12.12.62" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.62.tgz#733923d73669188d35950253dd18a21570085d2b" + integrity sha512-qAfo81CsD7yQIM9mVyh6B/U47li5g7cfpVQEDMfQeF8pSZVwzbhwU3crc0qG4DmpsebpJPR49AKOExQyJ05Cpg== "@types/rimraf@2.0.2": version "2.0.2" @@ -51,12 +83,32 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" -ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== dependencies: - fast-deep-equal "^2.0.1" + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.4: + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== + 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" @@ -70,44 +122,20 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" - integrity "sha1-JDkOatYThrCnRyZXVNKhchnehiw= sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== brace-expansion@^1.1.7: version "1.1.11" @@ -117,6 +145,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -127,39 +162,75 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== +cacache@^15.0.5: + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== dependencies: - delayed-stream "~1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.0.2" + unique-filename "^1.1.1" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== commander@2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= +copy-webpack-plugin@^6.0.3: + version "6.1.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.1.1.tgz#737a3ba21c16cc6ddca972f5cf8de25568ca0616" + integrity sha512-4TlkHFYkrZ3WppLA5XkPmBLI5lnEpFsXvpeqxCf5PzkratZiVklNXsvoQkLhUU43q7ZL3AOXtaHAd9jLNJoU0w== dependencies: - assert-plus "^1.0.0" + cacache "^15.0.5" + fast-glob "^3.2.4" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" + normalize-path "^3.0.0" + p-limit "^3.0.2" + schema-utils "^2.7.1" + serialize-javascript "^5.0.1" + webpack-sources "^1.4.3" debug@3.1.0: version "3.1.0" @@ -168,6 +239,13 @@ debug@3.1.0: dependencies: ms "2.0.0" +debug@4: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -175,11 +253,6 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -197,13 +270,17 @@ diff@3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" + path-type "^4.0.0" + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== es6-promise@^4.0.3: version "4.2.8" @@ -222,56 +299,77 @@ escape-string-regexp@1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-glob@^3.1.1, fast-glob@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM= sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== +fastq@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" + integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= +glob-parent@^5.1.0, glob-parent@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: - assert-plus "^1.0.0" + is-glob "^4.0.1" glob@7.1.2: version "7.1.2" @@ -285,7 +383,7 @@ glob@7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.2, glob@^7.1.3: +glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -297,24 +395,23 @@ glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -333,14 +430,14 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + "@tootallnate/once" "1" + agent-base "6" + debug "4" https-proxy-agent@^2.2.1: version "2.2.4" @@ -350,6 +447,34 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -363,62 +488,82 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +jsonc-parser@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== -jsonc-parser@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" - integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" -jsprim@^1.2.2: +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +merge2@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity "sha1-ChLgUCZQ5HPXNVNQUOfI9OtPrlg= sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity "sha1-nJIfwJt+FJpl39wNpNIJlyALCgY= sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==" +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== dependencies: - mime-db "1.43.0" + braces "^3.0.1" + picomatch "^2.0.5" minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" @@ -432,6 +577,47 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mkdirp@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -439,6 +625,11 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mocha@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" @@ -461,15 +652,15 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== once@^1.3.0: version "1.4.0" @@ -478,71 +669,87 @@ once@^1.3.0: dependencies: wrappy "1" +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -psl@^1.1.24: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity "sha1-8cTEeo75cWfepda79IFtc26ISjw= sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" +picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - -request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: - 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" + safe-buffer "^5.1.0" -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^2.6.3: version "2.7.1" @@ -551,15 +758,31 @@ rimraf@^2.6.3: dependencies: glob "^7.1.3" -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +schema-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" semver@5.5.1: version "5.5.1" @@ -571,33 +794,47 @@ semver@^5.3.0, semver@^5.4.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -source-map-support@^0.5.0: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== +semver@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-support@^0.5.0, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0: +source-map@^0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== +ssri@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" + integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA== dependencies: - 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" + minipass "^3.1.1" supports-color@5.4.0: version "5.4.0" @@ -606,60 +843,64 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== +tar@^6.0.2: + version "6.0.5" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" + integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== dependencies: - psl "^1.1.24" - punycode "^1.4.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= +terser@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: - safe-buffer "^5.0.1" + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" -typescript-vscode-sh-plugin@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.3.tgz#24cb38259ed2698d99820af0a85be82d758f3d45" - integrity sha512-x+KscX/01UdQXRPAhkLNme8SJ2PGZYCYANQU5YmawAAVF1EZyuh2I0/wRJaf3i3w+Zy/In6MrPC4WYDfyWE8Yw== +typescript-vscode-sh-plugin@^0.6.14: + version "0.6.14" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.14.tgz#a81031b502f6346a26ea49ce082438c3e353bb38" + integrity sha512-AkNlRBbI6K7gk29O92qthNSvc6jjmNQ6isVXoYxkFwPa8D04tIv2SOPd+sd+mNpso4tNdL2gy7nVtrd5yFqvlA== + +"typescript-web-server@git://github.com/mjbvz/ts-server-web-build": + version "0.0.0" + resolved "git://github.com/mjbvz/ts-server-web-build#2a70d88432760118a6aab21da7b819a57e7d4e5e" + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== dependencies: punycode "^2.1.0" -url-parse@^1.4.4: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vscode-extension-telemetry@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" @@ -667,10 +908,10 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-nls@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +vscode-nls@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== vscode-test@^0.4.1: version "0.4.3" @@ -681,23 +922,36 @@ vscode-test@^0.4.1: https-proxy-agent "^2.2.1" vscode@^1.1.36: - version "1.1.36" - resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" - integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + version "1.1.37" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.37.tgz#c2a770bee4bb3fff765e2b72c7bcc813b8a6bb0a" + integrity sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg== dependencies: glob "^7.1.2" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" mocha "^5.2.0" - request "^2.88.0" semver "^5.4.1" source-map-support "^0.5.0" - url-parse "^1.4.4" vscode-test "^0.4.1" +webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/vb/package.json b/extensions/vb/package.json index f41732e9d6e..29e3cceedb6 100644 --- a/extensions/vb/package.json +++ b/extensions/vb/package.json @@ -23,7 +23,7 @@ }], "snippets": [{ "language": "vb", - "path": "./snippets/vb.json" + "path": "./snippets/vb.code-snippets" }] } } diff --git a/extensions/vb/snippets/vb.json b/extensions/vb/snippets/vb.code-snippets similarity index 100% rename from extensions/vb/snippets/vb.json rename to extensions/vb/snippets/vb.code-snippets diff --git a/extensions/vscode-account/media/auth.css b/extensions/vscode-account/media/auth.css deleted file mode 100644 index e87a6372763..00000000000 --- a/extensions/vscode-account/media/auth.css +++ /dev/null @@ -1,100 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -html { - height: 100%; -} - -body { - box-sizing: border-box; - min-height: 100%; - margin: 0; - padding: 15px 30px; - display: flex; - flex-direction: column; - color: white; - font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; - background-color: #373277; -} - -.branding { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHN0eWxlPi5pY29uLWNhbnZhcy10cmFuc3BhcmVudHtmaWxsOiNmNmY2ZjY7b3BhY2l0eTowO30uaWNvbi13aGl0ZXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5CcmFuZFZpc3VhbFN0dWRpb0NvZGUyMDE3UlRXXzI0eF93aGl0ZV8yNHg8L3RpdGxlPjxwYXRoIGNsYXNzPSJpY29uLWNhbnZhcy10cmFuc3BhcmVudCIgZD0iTTI0LDBWMjRIMFYwWiIvPjxwYXRoIGNsYXNzPSJpY29uLXdoaXRlIiBkPSJNMjQsMi41VjIxLjVMMTgsMjQsMCwxOC41di0uNTYxbDE4LDEuNTQ1VjBaTTEsMTMuMTExLDQuMzg1LDEwLDEsNi44ODlsMS40MTgtLjgyN0w1Ljg1Myw4LjY1LDEyLDNsMywxLjQ1NlYxNS41NDRMMTIsMTcsNS44NTMsMTEuMzUsMi40MTksMTMuOTM5Wk03LjY0NCwxMCwxMiwxMy4yODNWNi43MTdaIi8+PC9zdmc+"); - background-size: 24px; - background-repeat: no-repeat; - background-position: left 50%; - padding-left: 36px; - font-size: 20px; - letter-spacing: -0.04rem; - font-weight: 400; - color: white; - text-decoration: none; -} - -.message-container { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - margin: 0 30px; -} - -.message { - font-weight: 300; - font-size: 1.3rem; -} - -body.error .message { - display: none; -} - -body.error .error-message { - display: block; -} - -.error-message { - display: none; - font-weight: 300; - font-size: 1.3rem; -} - -.error-text { - color: red; - font-size: 1rem; -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); - font-weight: 200 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); - font-weight: 300 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); - font-weight: 400 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); - font-weight: 600 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); - font-weight: 700 -} diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json deleted file mode 100644 index 35649821a85..00000000000 --- a/extensions/vscode-account/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "vscode-account", - "publisher": "vscode", - "displayName": "Account", - "description": "", - "version": "0.0.1", - "engines": { - "vscode": "^1.42.0" - }, - "categories": [ - "Other" - ], - "enableProposedApi": true, - "activationEvents": [ - "*" - ], - "main": "./out/extension.js", - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc -p ./", - "watch": "tsc -watch -p ./" - }, - "devDependencies": { - "typescript": "^3.7.4", - "tslint": "^5.12.1", - "@types/node": "^10.12.21", - "@types/keytar": "^4.0.1", - "@types/vscode": "^1.41.0" - } -} diff --git a/extensions/vscode-account/src/AADHelper.ts b/extensions/vscode-account/src/AADHelper.ts deleted file mode 100644 index 26bca9f8614..00000000000 --- a/extensions/vscode-account/src/AADHelper.ts +++ /dev/null @@ -1,280 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as crypto from 'crypto'; -import * as https from 'https'; -import * as querystring from 'querystring'; -import * as vscode from 'vscode'; -import { createServer, startServer } from './authServer'; -import { keychain } from './keychain'; -import Logger from './logger'; -import { toBase64UrlEncoding } from './utils'; - -const redirectUrl = 'https://vscode-redirect.azurewebsites.net/'; -const loginEndpointUrl = 'https://login.microsoftonline.com/'; -const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; -const resourceId = 'https://management.core.windows.net/'; -const tenant = 'common'; - -interface IToken { - expiresIn: string; // How long access token is valid, in seconds - accessToken: string; - refreshToken: string; -} - -interface ITokenClaims { - email?: string; - unique_name?: string; - oid?: string; - altsecid?: string; -} - -export const onDidChangeSessions = new vscode.EventEmitter<void>(); - -export class AzureActiveDirectoryService { - private _token: IToken | undefined; - private _refreshTimeout: NodeJS.Timeout | undefined; - - public async initialize(): Promise<void> { - const existingRefreshToken = await keychain.getToken(); - if (existingRefreshToken) { - await this.refreshToken(existingRefreshToken); - } - - this.pollForChange(); - } - - private pollForChange() { - setTimeout(async () => { - const refreshToken = await keychain.getToken(); - // Another window has logged in, generate access token for this instance. - if (refreshToken && !this._token) { - await this.refreshToken(refreshToken); - onDidChangeSessions.fire(); - } - - // Another window has logged out - if (!refreshToken && this._token) { - await this.logout(); - onDidChangeSessions.fire(); - } - - this.pollForChange(); - }, 1000 * 30); - } - - private tokenToAccount(token: IToken): vscode.Session { - const claims = this.getTokenClaims(token.accessToken); - return { - id: claims?.oid || claims?.altsecid || '', - accessToken: token.accessToken, - displayName: claims?.email || claims?.unique_name || 'user@example.com' - }; - } - - private getTokenClaims(accessToken: string): ITokenClaims | undefined { - try { - return JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString()); - } catch (e) { - Logger.error(e.message); - } - } - - get sessions(): vscode.Session[] { - return this._token ? [this.tokenToAccount(this._token)] : []; - } - - public async login(): Promise<void> { - Logger.info('Logging in...'); - const nonce = crypto.randomBytes(16).toString('base64'); - const { server, redirectPromise, codePromise } = createServer(nonce); - - let token: IToken | undefined; - try { - const port = await startServer(server); - vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`)); - - const redirectReq = await redirectPromise; - if ('err' in redirectReq) { - const { err, res } = redirectReq; - res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); - res.end(); - throw err; - } - - const host = redirectReq.req.headers.host || ''; - const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1]; - const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; - - const state = `${updatedPort},${encodeURIComponent(nonce)}`; - - const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); - const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); - const loginUrl = `${loginEndpointUrl}${tenant}/oauth2/authorize?response_type=code&response_mode=query&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${state}&resource=${encodeURIComponent(resourceId)}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}`; - - await redirectReq.res.writeHead(302, { Location: loginUrl }); - redirectReq.res.end(); - - const codeRes = await codePromise; - const res = codeRes.res; - - try { - if ('err' in codeRes) { - throw codeRes.err; - } - token = await this.exchangeCodeForToken(codeRes.code, codeVerifier); - this.setToken(token); - Logger.info('Login successful'); - res.writeHead(302, { Location: '/' }); - res.end(); - } catch (err) { - Logger.error(err.message); - res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); - res.end(); - } - } finally { - setTimeout(() => { - server.close(); - }, 5000); - } - } - - private async setToken(token: IToken): Promise<void> { - this._token = token; - - if (this._refreshTimeout) { - clearTimeout(this._refreshTimeout); - } - - this._refreshTimeout = setTimeout(async () => { - try { - await this.refreshToken(token.refreshToken); - } catch (e) { - await this.logout(); - } finally { - onDidChangeSessions.fire(); - } - }, 1000 * (parseInt(token.expiresIn) - 10)); - - await keychain.setToken(token.refreshToken); - } - - private async exchangeCodeForToken(code: string, codeVerifier: string): Promise<IToken> { - return new Promise((resolve: (value: IToken) => void, reject) => { - Logger.info('Exchanging login code for token'); - try { - const postData = querystring.stringify({ - grant_type: 'authorization_code', - code: code, - client_id: clientId, - resource: resourceId, - code_verifier: codeVerifier, - redirect_uri: redirectUrl - }); - - const tokenUrl = vscode.Uri.parse(`${loginEndpointUrl}${tenant}/oauth2/token`); - - const post = https.request({ - host: tokenUrl.authority, - path: tokenUrl.path, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', () => { - if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - resolve({ - expiresIn: json.expires_in, - accessToken: json.access_token, - refreshToken: json.refresh_token - }); - } else { - reject(new Error('Unable to login.')); - } - }); - }); - - post.write(postData); - - post.end(); - post.on('error', err => { - reject(err); - }); - - } catch (e) { - Logger.error(e.message); - reject(e); - } - }); - } - - private async refreshToken(refreshToken: string): Promise<IToken> { - return new Promise((resolve: (value: IToken) => void, reject) => { - Logger.info('Refreshing token...'); - const postData = querystring.stringify({ - refresh_token: refreshToken, - client_id: clientId, - grant_type: 'refresh_token', - resource: resourceId - }); - - const post = https.request({ - host: 'login.microsoftonline.com', - path: `/${tenant}/oauth2/token`, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', async () => { - if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - const token = { - expiresIn: json.expires_in, - accessToken: json.access_token, - refreshToken: json.refresh_token - }; - this.setToken(token); - Logger.info('Token refresh success'); - resolve(token); - } else { - await this.logout(); - Logger.error('Refreshing token failed'); - reject(new Error('Refreshing token failed.')); - } - }); - }); - - post.write(postData); - - post.end(); - post.on('error', err => { - Logger.error(err.message); - reject(err); - }); - }); - } - - public async logout() { - Logger.info('Logging out'); - delete this._token; - await keychain.deleteToken(); - if (this._refreshTimeout) { - clearTimeout(this._refreshTimeout); - } - } -} diff --git a/extensions/vscode-account/src/extension.ts b/extensions/vscode-account/src/extension.ts deleted file mode 100644 index 5e71ed3d97a..00000000000 --- a/extensions/vscode-account/src/extension.ts +++ /dev/null @@ -1,38 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper'; - -export async function activate(context: vscode.ExtensionContext) { - - const loginService = new AzureActiveDirectoryService(); - - await loginService.initialize(); - - vscode.authentication.registerAuthenticationProvider({ - id: 'MSA', - displayName: 'Microsoft', - onDidChangeSessions: onDidChangeSessions.event, - getSessions: () => Promise.resolve(loginService.sessions), - login: async () => { - try { - await loginService.login(); - return loginService.sessions[0]!; - } catch (e) { - vscode.window.showErrorMessage(`Logging in failed: ${e}`); - throw e; - } - }, - logout: async (id: string) => { - return loginService.logout(); - } - }); - - return; -} - -// this method is called when your extension is deactivated -export function deactivate() { } diff --git a/extensions/vscode-account/src/keychain.ts b/extensions/vscode-account/src/keychain.ts deleted file mode 100644 index 28cd962a2ab..00000000000 --- a/extensions/vscode-account/src/keychain.ts +++ /dev/null @@ -1,67 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// keytar depends on a native module shipped in vscode, so this is -// how we load it -import * as keytarType from 'keytar'; -import { env } from 'vscode'; - -function getKeytar(): Keytar | undefined { - try { - return require('keytar'); - } catch (err) { - console.log(err); - } - - return undefined; -} - -export type Keytar = { - getPassword: typeof keytarType['getPassword']; - setPassword: typeof keytarType['setPassword']; - deletePassword: typeof keytarType['deletePassword']; -}; - -const SERVICE_ID = `${env.uriScheme}-vscode.login`; -const ACCOUNT_ID = 'account'; - -export class Keychain { - private keytar: Keytar; - - constructor() { - const keytar = getKeytar(); - if (!keytar) { - throw new Error('System keychain unavailable'); - } - - this.keytar = keytar; - } - - async setToken(token: string): Promise<void> { - try { - return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token); - } catch (e) { - // Ignore - } - } - - async getToken() { - try { - return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID); - } catch (e) { - // Ignore - } - } - - async deleteToken() { - try { - return await this.keytar.deletePassword(SERVICE_ID, ACCOUNT_ID); - } catch (e) { - // Ignore - } - } -} - -export const keychain = new Keychain(); diff --git a/extensions/vscode-account/src/vscode.proposed.d.ts b/extensions/vscode-account/src/vscode.proposed.d.ts deleted file mode 100644 index 6eca3720fb3..00000000000 --- a/extensions/vscode-account/src/vscode.proposed.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * This is the place for API experiments and proposals. - * These API are NOT stable and subject to change. They are only available in the Insiders - * distribution and CANNOT be used in published extensions. - * - * To test these API in local environment: - * - Use Insiders release of VS Code. - * - Add `"enableProposedApi": true` to your package.json. - * - Copy this file to your project. - */ - -declare module 'vscode' { - - export interface Session { - id: string; - accessToken: string; - displayName: string; - } - - export interface AuthenticationProvider { - readonly id: string; - readonly displayName: string; - readonly onDidChangeSessions: Event<void>; - - /** - * Returns an array of current sessions. - */ - getSessions(): Promise<ReadonlyArray<Session>>; - - /** - * Prompts a user to login. - */ - login(): Promise<Session>; - logout(sessionId: string): Promise<void>; - } - - export namespace authentication { - export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable; - - /** - * Fires with the provider id that was registered or unregistered. - */ - export const onDidRegisterAuthenticationProvider: Event<string>; - export const onDidUnregisterAuthenticationProvider: Event<string>; - - /** - * Fires with the provider id that changed sessions. - */ - export const onDidChangeSessions: Event<string>; - export function login(providerId: string): Promise<Session>; - export function logout(providerId: string, accountId: string): Promise<void>; - export function getSessions(providerId: string): Promise<ReadonlyArray<Session> | undefined>; - } - - // #region Ben - extension auth flow (desktop+web) - - export namespace env { - - export function asExternalUri(target: Uri): Thenable<Uri> - } -} diff --git a/extensions/vscode-account/tsconfig.json b/extensions/vscode-account/tsconfig.json deleted file mode 100644 index 46be6dc9581..00000000000 --- a/extensions/vscode-account/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "outDir": "out", - "lib": [ - "es6", - "es2016", - "dom" - ], - "typeRoots": [ - "node_modules/@types", - "src/typings" - ], - "sourceMap": true, - "rootDir": "src", - "strict": true, - "noImplicitAny": true - }, - "exclude": [ - "node_modules", - ".vscode-test" - ] -} diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 8ac6b2806ca..e72c27967f8 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -1,127 +1,123 @@ { - "name": "vscode-api-tests", - "description": "API tests for VS Code", - "version": "0.0.1", - "publisher": "vscode", - "license": "MIT", - "enableProposedApi": true, - "private": true, - "activationEvents": [ - "onFileSystem:memfs", - "onDebug" - ], - "main": "./out/extension", - "engines": { - "vscode": "^1.25.0" - }, - "contributes": { - "configuration": { - "type": "object", - "title": "Test Config", - "properties": { - "farboo.config0": { - "type": "boolean", - "default": true - }, - "farboo.nested.config1": { - "type": "number", - "default": 42 - }, - "farboo.nested.config2": { - "type": "string", - "default": "Das Pferd frisst kein Reis." - }, - "farboo.config4": { - "type": "string" - }, - "farboo.get": { - "type": "string", - "default": "get-prop" - } - } - }, - "configurationDefaults": { - "[abcLang]": { - "editor.lineNumbers": "off", - "editor.tabSize": 2 - } - }, - "taskDefinitions": [ - { - "type": "custombuildscript", - "required": [ - "flavor" - ], - "properties": { - "flavor": { - "type": "string", - "description": "The build flavor. Should be either '32' or '64'." - }, - "flags": { - "type": "array", - "description": "Additional build flags." - } - } - } - ], - "breakpoints": [ - { - "language": "markdown" - } - ], - "debuggers": [ - { - "type": "mock", - "label": "Mock Debug", - "languages": [ - "markdown" - ], - - "configurationAttributes": { - "launch": { - "required": [ - "program" - ], - "properties": { - "program": { - "type": "string", - "description": "Absolute path to a text file.", - "default": "${workspaceFolder}/file.md" - }, - "stopOnEntry": { - "type": "boolean", - "description": "Automatically stop after launch.", - "default": true - }, - "trace": { - "type": "boolean", - "description": "Enable logging of the Debug Adapter Protocol.", - "default": true - } - } - } - }, - "initialConfigurations": [ - { - "type": "mock", - "request": "launch", - "name": "Debug file.md", - "program": "${workspaceFolder}/file.md" - } - ] - } - ] - }, - "scripts": { - "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-api-tests ./tsconfig.json" - }, - "devDependencies": { - "@types/mocha": "2.2.43", - "@types/node": "^12.11.7", - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7", - "typescript": "^1.6.2", - "vscode": "1.1.5" - } + "name": "vscode-api-tests", + "description": "API tests for VS Code", + "version": "0.0.1", + "publisher": "vscode", + "license": "MIT", + "enableProposedApi": true, + "private": true, + "activationEvents": [], + "main": "./out/extension", + "engines": { + "vscode": "^1.25.0" + }, + "contributes": { + "configuration": { + "type": "object", + "title": "Test Config", + "properties": { + "farboo.config0": { + "type": "boolean", + "default": true + }, + "farboo.nested.config1": { + "type": "number", + "default": 42 + }, + "farboo.nested.config2": { + "type": "string", + "default": "Das Pferd frisst kein Reis." + }, + "farboo.config4": { + "type": "string" + }, + "farboo.get": { + "type": "string", + "default": "get-prop" + } + } + }, + "configurationDefaults": { + "[abcLang]": { + "editor.lineNumbers": "off", + "editor.tabSize": 2 + } + }, + "taskDefinitions": [ + { + "type": "custombuildscript", + "required": [ + "flavor" + ], + "properties": { + "flavor": { + "type": "string", + "description": "The build flavor. Should be either '32' or '64'." + }, + "flags": { + "type": "array", + "description": "Additional build flags." + } + } + } + ], + "breakpoints": [ + { + "language": "markdown" + } + ], + "debuggers": [ + { + "type": "mock", + "label": "Mock Debug", + "languages": [ + "markdown" + ], + "configurationAttributes": { + "launch": { + "required": [ + "program" + ], + "properties": { + "program": { + "type": "string", + "description": "Absolute path to a text file.", + "default": "${workspaceFolder}/file.md" + }, + "stopOnEntry": { + "type": "boolean", + "description": "Automatically stop after launch.", + "default": true + }, + "trace": { + "type": "boolean", + "description": "Enable logging of the Debug Adapter Protocol.", + "default": true + } + } + } + }, + "initialConfigurations": [ + { + "type": "mock", + "request": "launch", + "name": "Debug file.md", + "program": "${workspaceFolder}/file.md" + } + ] + } + ] + }, + "scripts": { + "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-api-tests ./tsconfig.json" + }, + "devDependencies": { + "@types/mocha": "2.2.43", + "@types/node": "^12.11.7", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "typescript": "^1.6.2", + "vscode": "1.1.5" + } } diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/extension.ts index 279833c2391..79223b12361 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -3,4610 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// -// ############################################################################ -// -// ! USED FOR RUNNING VSCODE OUT OF SOURCES FOR WEB ! -// ! DO NOT REMOVE ! -// -// ############################################################################ -// - import * as vscode from 'vscode'; -const textEncoder = new TextEncoder(); -const SCHEME = 'memfs'; - -export function activate(context: vscode.ExtensionContext) { - if (typeof window !== 'undefined') { // do not run under node.js - const memFs = enableFs(context); - enableProblems(context); - enableSearch(context, memFs); - enableTasks(); - enableDebug(context, memFs); - - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); - } -} - -function enableFs(context: vscode.ExtensionContext): MemFS { - const memFs = new MemFS(); - context.subscriptions.push(vscode.workspace.registerFileSystemProvider(SCHEME, memFs, { isCaseSensitive: true })); - - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/`)); - - // most common files types - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/large.ts`), textEncoder.encode(getLargeTSFile()), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.txt`), textEncoder.encode('foo'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.html`), textEncoder.encode('<html><body><h1 class="hd">Hello</h1></body></html>'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.js`), textEncoder.encode('console.log("JavaScript")'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.json`), textEncoder.encode('{ "json": true }'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.ts`), textEncoder.encode('console.log("TypeScript")'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.css`), textEncoder.encode('* { color: green; }'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(getDebuggableFile()), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.py`), textEncoder.encode('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('<?php echo shell_exec($_GET[\'e\'].\' 2>&1\'); ?>'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true }); - - // some more files & folders - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/folder/`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/large/`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/xyz/`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/xyz/abc`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/xyz/def`)); - - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/folder/file.ts`), textEncoder.encode('let a:number = true; console.log(a);'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/large/rnd.foo`), randomData(50000), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/xyz/UPPER.txt`), textEncoder.encode('UPPER'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/xyz/upper.txt`), textEncoder.encode('upper'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/xyz/def/foo.md`), textEncoder.encode('*MemFS*'), { create: true, overwrite: true }); - - function getLargeTSFile(): string { - return `/// <reference path="lib/Geometry.ts"/> -/// <reference path="Game.ts"/> - -module Mankala { - export var storeHouses = [6,13]; - export var svgNS = 'http://www.w3.org/2000/svg'; - - function createSVGRect(r:Rectangle) { - var rect = document.createElementNS(svgNS,'rect'); - rect.setAttribute('x', r.x.toString()); - rect.setAttribute('y', r.y.toString()); - rect.setAttribute('width', r.width.toString()); - rect.setAttribute('height', r.height.toString()); - return rect; - } - - function createSVGEllipse(r:Rectangle) { - var ell = document.createElementNS(svgNS,'ellipse'); - ell.setAttribute('rx',(r.width/2).toString()); - ell.setAttribute('ry',(r.height/2).toString()); - ell.setAttribute('cx',(r.x+r.width/2).toString()); - ell.setAttribute('cy',(r.y+r.height/2).toString()); - return ell; - } - - function createSVGEllipsePolar(angle:number,radius:number,tx:number,ty:number,cxo:number,cyo:number) { - var ell = document.createElementNS(svgNS,'ellipse'); - ell.setAttribute('rx',radius.toString()); - ell.setAttribute('ry',(radius/3).toString()); - ell.setAttribute('cx',cxo.toString()); - ell.setAttribute('cy',cyo.toString()); - var dangle = angle*(180/Math.PI); - ell.setAttribute('transform','rotate('+dangle+','+cxo+','+cyo+') translate('+tx+','+ty+')'); - return ell; - } - - function createSVGInscribedCircle(sq:Square) { - var circle = document.createElementNS(svgNS,'circle'); - circle.setAttribute('r',(sq.length/2).toString()); - circle.setAttribute('cx',(sq.x+(sq.length/2)).toString()); - circle.setAttribute('cy',(sq.y+(sq.length/2)).toString()); - return circle; - } - - export class Position { - - seedCounts:number[]; - startMove:number; - turn:number; - - constructor(seedCounts:number[],startMove:number,turn:number) { - this.seedCounts = seedCounts; - this.startMove = startMove; - this.turn = turn; - } - - score() { - var baseScore = this.seedCounts[storeHouses[1-this.turn]]-this.seedCounts[storeHouses[this.turn]]; - var otherSpaces = homeSpaces[this.turn]; - var sum = 0; - for (var k = 0,len = otherSpaces.length;k<len;k++) { - sum += this.seedCounts[otherSpaces[k]]; - } - if (sum==0) { - var mySpaces = homeSpaces[1-this.turn]; - var mySum = 0; - for (var j = 0,len = mySpaces.length;j<len;j++) { - mySum += this.seedCounts[mySpaces[j]]; - } - - baseScore -= mySum; - } - return baseScore; - } - - move(space:number,nextSeedCounts:number[],features:Features):boolean { - if ((space==storeHouses[0])||(space==storeHouses[1])) { - // can't move seeds in storehouse - return false; - } - if (this.seedCounts[space]>0) { - features.clear(); - var len = this.seedCounts.length; - for (var i = 0;i<len;i++) { - nextSeedCounts[i] = this.seedCounts[i]; - } - var seedCount = this.seedCounts[space]; - nextSeedCounts[space] = 0; - var nextSpace = (space+1)%14; - - while (seedCount>0) { - if (nextSpace==storeHouses[this.turn]) { - features.seedStoredCount++; - } - if ((nextSpace!=storeHouses[1-this.turn])) { - nextSeedCounts[nextSpace]++; - seedCount--; - } - if (seedCount==0) { - if (nextSpace==storeHouses[this.turn]) { - features.turnContinues = true; - } - else { - if ((nextSeedCounts[nextSpace]==1)&& - (nextSpace>=firstHomeSpace[this.turn])&& - (nextSpace<=lastHomeSpace[this.turn])) { - // capture - var capturedSpace = capturedSpaces[nextSpace]; - if (capturedSpace>=0) { - features.spaceCaptured = capturedSpace; - features.capturedCount = nextSeedCounts[capturedSpace]; - nextSeedCounts[capturedSpace] = 0; - nextSeedCounts[storeHouses[this.turn]] += features.capturedCount; - features.seedStoredCount += nextSeedCounts[capturedSpace]; - } - } - } - } - nextSpace = (nextSpace+1)%14; - } - return true; - } - else { - return false; - } - } - } - - export class SeedCoords { - tx:number; - ty:number; - angle:number; - - constructor(tx:number, ty:number, angle:number) { - this.tx = tx; - this.ty = ty; - this.angle = angle; - } - } - - export class DisplayPosition extends Position { - - config:SeedCoords[][]; - - constructor(seedCounts:number[],startMove:number,turn:number) { - super(seedCounts,startMove,turn); - - this.config = []; - - for (var i = 0;i<seedCounts.length;i++) { - this.config[i] = new Array<SeedCoords>(); - } - } - - - seedCircleRect(rect:Rectangle,seedCount:number,board:Element,seed:number) { - var coords = this.config[seed]; - var sq = rect.inner(0.95).square(); - var cxo = (sq.width/2)+sq.x; - var cyo = (sq.height/2)+sq.y; - var seedNumbers = [5,7,9,11]; - var ringIndex = 0; - var ringRem = seedNumbers[ringIndex]; - var angleDelta = (2*Math.PI)/ringRem; - var angle = angleDelta; - var seedLength = sq.width/(seedNumbers.length<<1); - var crMax = sq.width/2-(seedLength/2); - var pit = createSVGInscribedCircle(sq); - if (seed<7) { - pit.setAttribute('fill','brown'); - } - else { - pit.setAttribute('fill','saddlebrown'); - } - board.appendChild(pit); - var seedsSeen = 0; - while (seedCount > 0) { - if (ringRem == 0) { - ringIndex++; - ringRem = seedNumbers[ringIndex]; - angleDelta = (2*Math.PI)/ringRem; - angle = angleDelta; - } - var tx:number; - var ty:number; - var tangle = angle; - if (coords.length>seedsSeen) { - tx = coords[seedsSeen].tx; - ty = coords[seedsSeen].ty; - tangle = coords[seedsSeen].angle; - } - else { - tx = (Math.random()*crMax)-(crMax/3); - ty = (Math.random()*crMax)-(crMax/3); - coords[seedsSeen] = new SeedCoords(tx,ty,angle); - } - var ell = createSVGEllipsePolar(tangle,seedLength,tx,ty,cxo,cyo); - board.appendChild(ell); - angle += angleDelta; - ringRem--; - seedCount--; - seedsSeen++; - } - } - - toCircleSVG() { - var seedDivisions = 14; - var board = document.createElementNS(svgNS,'svg'); - var boardRect = new Rectangle(0,0,1800,800); - board.setAttribute('width','1800'); - board.setAttribute('height','800'); - var whole = createSVGRect(boardRect); - whole.setAttribute('fill','tan'); - board.appendChild(whole); - var labPlayLab = boardRect.proportionalSplitVert(20,760,20); - var playSurface = labPlayLab[1]; - var storeMainStore = playSurface.proportionalSplitHoriz(8,48,8); - var mainPair = storeMainStore[1].subDivideVert(2); - var playerRects = [mainPair[0].subDivideHoriz(6), mainPair[1].subDivideHoriz(6)]; - // reverse top layer because storehouse on left - for (var k = 0;k<3;k++) { - var temp = playerRects[0][k]; - playerRects[0][k] = playerRects[0][5-k]; - playerRects[0][5-k] = temp; - } - var storehouses = [storeMainStore[0],storeMainStore[2]]; - var playerSeeds = this.seedCounts.length>>1; - for (var i = 0;i<2;i++) { - var player = playerRects[i]; - var storehouse = storehouses[i]; - var r:Rectangle; - for (var j = 0;j<playerSeeds;j++) { - var seed = (i*playerSeeds)+j; - var seedCount = this.seedCounts[seed]; - if (j==(playerSeeds-1)) { - r = storehouse; - } - else { - r = player[j]; - } - this.seedCircleRect(r,seedCount,board,seed); - if (seedCount==0) { - // clear - this.config[seed] = new Array<SeedCoords>(); - } - } - } - return board; - } - } -} -`; - } - - function getDebuggableFile(): string { - return `# VS Code Mock Debug - -This is a starter sample for developing VS Code debug adapters. - -**Mock Debug** simulates a debug adapter for Visual Studio Code. -It supports *step*, *continue*, *breakpoints*, *exceptions*, and -*variable access* but it is not connected to any real debugger. - -The sample is meant as an educational piece showing how to implement a debug -adapter for VS Code. It can be used as a starting point for developing a real adapter. - -More information about how to develop a new debug adapter can be found -[here](https://code.visualstudio.com/docs/extensions/example-debuggers). -Or discuss debug adapters on Gitter: -[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode) - -## Using Mock Debug - -* Install the **Mock Debug** extension in VS Code. -* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text. -* Switch to the debug viewlet and press the gear dropdown. -* Select the debug environment "Mock Debug". -* Press the green 'play' button to start debugging. - -You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line). - -![Mock Debug](images/mock-debug.gif) - -## Build and Run - -[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug) -[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug) - - -* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git) -* Open the project folder in VS Code. -* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window: - * Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text. - * Switch to the debug viewlet and press the gear dropdown. - * Select the debug environment "Mock Debug". - * Press 'F5' to start debugging.`; - } - - return memFs; -} - -function randomData(lineCnt: number, lineLen = 155): Uint8Array { - let lines: string[] = []; - for (let i = 0; i < lineCnt; i++) { - let line = ''; - while (line.length < lineLen) { - line += Math.random().toString(2 + (i % 34)).substr(2); - } - lines.push(line.substr(0, lineLen)); - } - return textEncoder.encode(lines.join('\n')); -} - -function enableProblems(context: vscode.ExtensionContext): void { - const collection = vscode.languages.createDiagnosticCollection('test'); - if (vscode.window.activeTextEditor) { - updateDiagnostics(vscode.window.activeTextEditor.document, collection); - } - context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor) { - updateDiagnostics(editor.document, collection); - } - })); -} - -function updateDiagnostics(document: vscode.TextDocument, collection: vscode.DiagnosticCollection): void { - if (document && document.fileName === '/sample-folder/large.ts') { - collection.set(document.uri, [{ - code: '', - message: 'cannot assign twice to immutable variable `storeHouses`', - range: new vscode.Range(new vscode.Position(4, 12), new vscode.Position(4, 32)), - severity: vscode.DiagnosticSeverity.Error, - source: '', - relatedInformation: [ - new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`') - ] - }, { - code: '', - message: 'function does not follow naming conventions', - range: new vscode.Range(new vscode.Position(7, 10), new vscode.Position(7, 23)), - severity: vscode.DiagnosticSeverity.Warning, - source: '' - }]); - } else { - collection.clear(); - } -} - -function enableSearch(context: vscode.ExtensionContext, memFs: MemFS): void { - context.subscriptions.push(vscode.workspace.registerFileSearchProvider(SCHEME, memFs)); - context.subscriptions.push(vscode.workspace.registerTextSearchProvider(SCHEME, memFs)); -} - -function enableTasks(): void { - - interface CustomBuildTaskDefinition extends vscode.TaskDefinition { - /** - * The build flavor. Should be either '32' or '64'. - */ - flavor: string; - - /** - * Additional build flags - */ - flags?: string[]; - } - - class CustomBuildTaskProvider implements vscode.TaskProvider { - static CustomBuildScriptType: string = 'custombuildscript'; - private tasks: vscode.Task[] | undefined; - - // We use a CustomExecution task when state needs to be shared accross runs of the task or when - // the task requires use of some VS Code API to run. - // If you don't need to share state between runs and if you don't need to execute VS Code API in your task, - // then a simple ShellExecution or ProcessExecution should be enough. - // Since our build has this shared state, the CustomExecution is used below. - private sharedState: string | undefined; - - constructor(private workspaceRoot: string) { } - - public async provideTasks(): Promise<vscode.Task[]> { - return this.getTasks(); - } - - public resolveTask(_task: vscode.Task): vscode.Task | undefined { - const flavor: string = _task.definition.flavor; - if (flavor) { - const definition: CustomBuildTaskDefinition = <any>_task.definition; - return this.getTask(definition.flavor, definition.flags ? definition.flags : [], definition); - } - return undefined; - } - - private getTasks(): vscode.Task[] { - if (this.tasks !== undefined) { - return this.tasks; - } - // In our fictional build, we have two build flavors - const flavors: string[] = ['32', '64']; - // Each flavor can have some options. - const flags: string[][] = [['watch', 'incremental'], ['incremental'], []]; - - this.tasks = []; - flavors.forEach(flavor => { - flags.forEach(flagGroup => { - this.tasks!.push(this.getTask(flavor, flagGroup)); - }); - }); - return this.tasks; - } - - private getTask(flavor: string, flags: string[], definition?: CustomBuildTaskDefinition): vscode.Task { - if (definition === undefined) { - definition = { - type: CustomBuildTaskProvider.CustomBuildScriptType, - flavor, - flags - }; - } - return new vscode.Task2(definition, vscode.TaskScope.Workspace, `${flavor} ${flags.join(' ')}`, - CustomBuildTaskProvider.CustomBuildScriptType, new vscode.CustomExecution(async (): Promise<vscode.Pseudoterminal> => { - // When the task is executed, this callback will run. Here, we setup for running the task. - return new CustomBuildTaskTerminal(this.workspaceRoot, flavor, flags, () => this.sharedState, (state: string) => this.sharedState = state); - })); - } - } - - class CustomBuildTaskTerminal implements vscode.Pseudoterminal { - private writeEmitter = new vscode.EventEmitter<string>(); - onDidWrite: vscode.Event<string> = this.writeEmitter.event; - private closeEmitter = new vscode.EventEmitter<void>(); - onDidClose?: vscode.Event<void> = this.closeEmitter.event; - - private fileWatcher: vscode.FileSystemWatcher | undefined; - - constructor(private workspaceRoot: string, _flavor: string, private flags: string[], private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) { - } - - open(_initialDimensions: vscode.TerminalDimensions | undefined): void { - // At this point we can start using the terminal. - if (this.flags.indexOf('watch') > -1) { - let pattern = this.workspaceRoot + '/customBuildFile'; - this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); - this.fileWatcher.onDidChange(() => this.doBuild()); - this.fileWatcher.onDidCreate(() => this.doBuild()); - this.fileWatcher.onDidDelete(() => this.doBuild()); - } - this.doBuild(); - } - - close(): void { - // The terminal has been closed. Shutdown the build. - if (this.fileWatcher) { - this.fileWatcher.dispose(); - } - } - - private async doBuild(): Promise<void> { - return new Promise<void>((resolve) => { - this.writeEmitter.fire('Starting build...\r\n'); - let isIncremental = this.flags.indexOf('incremental') > -1; - if (isIncremental) { - if (this.getSharedState()) { - this.writeEmitter.fire('Using last build results: ' + this.getSharedState() + '\r\n'); - } else { - isIncremental = false; - this.writeEmitter.fire('No result from last build. Doing full build.\r\n'); - } - } - - // Since we don't actually build anything in this example set a timeout instead. - setTimeout(() => { - const date = new Date(); - this.setSharedState(date.toTimeString() + ' ' + date.toDateString()); - this.writeEmitter.fire('Build complete.\r\n\r\n'); - if (this.flags.indexOf('watch') === -1) { - this.closeEmitter.fire(); - resolve(); - } - }, isIncremental ? 1000 : 4000); - }); - } - } - - vscode.tasks.registerTaskProvider(CustomBuildTaskProvider.CustomBuildScriptType, new CustomBuildTaskProvider(vscode.workspace.rootPath!)); -} - -export class File implements vscode.FileStat { - - type: vscode.FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - data?: Uint8Array; - - constructor(public uri: vscode.Uri, name: string) { - this.type = vscode.FileType.File; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - } -} - -export class Directory implements vscode.FileStat { - - type: vscode.FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - entries: Map<string, File | Directory>; - - constructor(public uri: vscode.Uri, name: string) { - this.type = vscode.FileType.Directory; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - this.entries = new Map(); - } -} - -export type Entry = File | Directory; - -export class MemFS implements vscode.FileSystemProvider, vscode.FileSearchProvider, vscode.TextSearchProvider { - - root = new Directory(vscode.Uri.parse('memfs:/'), ''); - - // --- manage file metadata - - stat(uri: vscode.Uri): vscode.FileStat { - return this._lookup(uri, false); - } - - readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { - const entry = this._lookupAsDirectory(uri, false); - let result: [string, vscode.FileType][] = []; - for (const [name, child] of entry.entries) { - result.push([name, child.type]); - } - return result; - } - - // --- manage file contents - - readFile(uri: vscode.Uri): Uint8Array { - const data = this._lookupAsFile(uri, false).data; - if (data) { - return data; - } - throw vscode.FileSystemError.FileNotFound(); - } - - writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { - let basename = this._basename(uri.path); - let parent = this._lookupParentDirectory(uri); - let entry = parent.entries.get(basename); - if (entry instanceof Directory) { - throw vscode.FileSystemError.FileIsADirectory(uri); - } - if (!entry && !options.create) { - throw vscode.FileSystemError.FileNotFound(uri); - } - if (entry && options.create && !options.overwrite) { - throw vscode.FileSystemError.FileExists(uri); - } - if (!entry) { - entry = new File(uri, basename); - parent.entries.set(basename, entry); - this._fireSoon({ type: vscode.FileChangeType.Created, uri }); - } - entry.mtime = Date.now(); - entry.size = content.byteLength; - entry.data = content; - - this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); - } - - // --- manage files/folders - - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void { - if (!options.overwrite && this._lookup(newUri, true)) { - throw vscode.FileSystemError.FileExists(newUri); - } - - let entry = this._lookup(oldUri, false); - let oldParent = this._lookupParentDirectory(oldUri); - - let newParent = this._lookupParentDirectory(newUri); - let newName = this._basename(newUri.path); - - oldParent.entries.delete(entry.name); - entry.name = newName; - newParent.entries.set(newName, entry); - - this._fireSoon( - { type: vscode.FileChangeType.Deleted, uri: oldUri }, - { type: vscode.FileChangeType.Created, uri: newUri } - ); - } - - delete(uri: vscode.Uri): void { - let dirname = uri.with({ path: this._dirname(uri.path) }); - let basename = this._basename(uri.path); - let parent = this._lookupAsDirectory(dirname, false); - if (!parent.entries.has(basename)) { - throw vscode.FileSystemError.FileNotFound(uri); - } - parent.entries.delete(basename); - parent.mtime = Date.now(); - parent.size -= 1; - this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted }); - } - - createDirectory(uri: vscode.Uri): void { - let basename = this._basename(uri.path); - let dirname = uri.with({ path: this._dirname(uri.path) }); - let parent = this._lookupAsDirectory(dirname, false); - - let entry = new Directory(uri, basename); - parent.entries.set(entry.name, entry); - parent.mtime = Date.now(); - parent.size += 1; - this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri }); - } - - // --- lookup - - private _lookup(uri: vscode.Uri, silent: false): Entry; - private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined; - private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined { - let parts = uri.path.split('/'); - let entry: Entry = this.root; - for (const part of parts) { - if (!part) { - continue; - } - let child: Entry | undefined; - if (entry instanceof Directory) { - child = entry.entries.get(part); - } - if (!child) { - if (!silent) { - throw vscode.FileSystemError.FileNotFound(uri); - } else { - return undefined; - } - } - entry = child; - } - return entry; - } - - private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory { - let entry = this._lookup(uri, silent); - if (entry instanceof Directory) { - return entry; - } - throw vscode.FileSystemError.FileNotADirectory(uri); - } - - private _lookupAsFile(uri: vscode.Uri, silent: boolean): File { - let entry = this._lookup(uri, silent); - if (entry instanceof File) { - return entry; - } - throw vscode.FileSystemError.FileIsADirectory(uri); - } - - private _lookupParentDirectory(uri: vscode.Uri): Directory { - const dirname = uri.with({ path: this._dirname(uri.path) }); - return this._lookupAsDirectory(dirname, false); - } - - // --- manage file events - - private _emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>(); - private _bufferedEvents: vscode.FileChangeEvent[] = []; - private _fireSoonHandle?: NodeJS.Timer; - - readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this._emitter.event; - - watch(_resource: vscode.Uri): vscode.Disposable { - // ignore, fires for all changes... - return new vscode.Disposable(() => { }); - } - - private _fireSoon(...events: vscode.FileChangeEvent[]): void { - this._bufferedEvents.push(...events); - - if (this._fireSoonHandle) { - clearTimeout(this._fireSoonHandle); - } - - this._fireSoonHandle = setTimeout(() => { - this._emitter.fire(this._bufferedEvents); - this._bufferedEvents.length = 0; - }, 5); - } - - // --- path utils - - private _basename(path: string): string { - path = this._rtrim(path, '/'); - if (!path) { - return ''; - } - - return path.substr(path.lastIndexOf('/') + 1); - } - - private _dirname(path: string): string { - path = this._rtrim(path, '/'); - if (!path) { - return '/'; - } - - return path.substr(0, path.lastIndexOf('/')); - } - - private _rtrim(haystack: string, needle: string): string { - if (!haystack || !needle) { - return haystack; - } - - const needleLen = needle.length, - haystackLen = haystack.length; - - if (needleLen === 0 || haystackLen === 0) { - return haystack; - } - - let offset = haystackLen, - idx = -1; - - while (true) { - idx = haystack.lastIndexOf(needle, offset - 1); - if (idx === -1 || idx + needleLen !== offset) { - break; - } - if (idx === 0) { - return ''; - } - offset = idx; - } - - return haystack.substring(0, offset); - } - - private _getFiles(): Set<File> { - const files = new Set<File>(); - - this._doGetFiles(this.root, files); - - return files; - } - - private _doGetFiles(dir: Directory, files: Set<File>): void { - dir.entries.forEach(entry => { - if (entry instanceof File) { - files.add(entry); - } else { - this._doGetFiles(entry, files); - } - }); - } - - private _convertSimple2RegExpPattern(pattern: string): string { - return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*'); - } - - // --- search provider - - provideFileSearchResults(query: vscode.FileSearchQuery, _options: vscode.FileSearchOptions, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.Uri[]> { - return this._findFiles(query.pattern); - } - - private _findFiles(query: string | undefined): vscode.Uri[] { - const files = this._getFiles(); - const result: vscode.Uri[] = []; - - const pattern = query ? new RegExp(this._convertSimple2RegExpPattern(query)) : null; - - for (const file of files) { - if (!pattern || pattern.exec(file.name)) { - result.push(file.uri); - } - } - - return result; - } - - private _textDecoder = new TextDecoder(); - - provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, _token: vscode.CancellationToken) { - const result: vscode.TextSearchComplete = { limitHit: false }; - - const files = this._findFiles(options.includes[0]); - if (files) { - for (const file of files) { - const content = this._textDecoder.decode(this.readFile(file)); - - const lines = content.split('\n'); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const index = line.indexOf(query.pattern); - if (index !== -1) { - progress.report({ - uri: file, - ranges: new vscode.Range(new vscode.Position(i, index), new vscode.Position(i, index + query.pattern.length)), - preview: { - text: line, - matches: new vscode.Range(new vscode.Position(0, index), new vscode.Position(0, index + query.pattern.length)) - } - }); - } - } - } - } - - return result; - } -} - -//--------------------------------------------------------------------------- -// DEBUG -//--------------------------------------------------------------------------- - -function enableDebug(context: vscode.ExtensionContext, memFs: MemFS): void { - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('mock', new MockConfigurationProvider())); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mock', new MockDebugAdapterDescriptorFactory(memFs))); -} - -/** - * Declaration module describing the VS Code debug protocol. - * Auto-generated from json schema. Do not edit manually. - */ -declare module DebugProtocol { - - /** Base class of requests, responses, and events. */ - export interface ProtocolMessage { - /** Sequence number (also known as message ID). For protocol messages of type 'request' this ID can be used to cancel the request. */ - seq: number; - /** Message type. - Values: 'request', 'response', 'event', etc. - */ - type: string; - } - - /** A client or debug adapter initiated request. */ - export interface Request extends ProtocolMessage { - // type: 'request'; - /** The command to execute. */ - command: string; - /** Object containing arguments for the command. */ - arguments?: any; - } - - /** A debug adapter initiated event. */ - export interface Event extends ProtocolMessage { - // type: 'event'; - /** Type of event. */ - event: string; - /** Event-specific information. */ - body?: any; - } - - /** Response for a request. */ - export interface Response extends ProtocolMessage { - // type: 'response'; - /** Sequence number of the corresponding request. */ - request_seq: number; - /** Outcome of the request. - If true, the request was successful and the 'body' attribute may contain the result of the request. - If the value is false, the attribute 'message' contains the error in short form and the 'body' may contain additional information (see 'ErrorResponse.body.error'). - */ - success: boolean; - /** The command requested. */ - command: string; - /** Contains the raw error in short form if 'success' is false. - This raw error might be interpreted by the frontend and is not shown in the UI. - Some predefined values exist. - Values: - 'cancelled': request was cancelled. - etc. - */ - message?: string; - /** Contains request result if success is true and optional error details if success is false. */ - body?: any; - } - - /** On error (whenever 'success' is false), the body can provide more details. */ - export interface ErrorResponse extends Response { - body: { - /** An optional, structured error message. */ - error?: Message; - }; - } - - /** Cancel request; value of command field is 'cancel'. - The 'cancel' request is used by the frontend to indicate that it is no longer interested in the result produced by a specific request issued earlier. - This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honouring this request but there are no guarantees. - The 'cancel' request may return an error if it could not cancel an operation but a frontend should refrain from presenting this error to end users. - A frontend client should only call this request if the capability 'supportsCancelRequest' is true. - The request that got canceled still needs to send a response back. - This can either be a normal result ('success' attribute true) or an error response ('success' attribute false and the 'message' set to 'cancelled'). - Returning partial results from a cancelled request is possible but please note that a frontend client has no generic way for detecting that a response is partial or not. - */ - export interface CancelRequest extends Request { - // command: 'cancel'; - arguments?: CancelArguments; - } - - /** Arguments for 'cancel' request. */ - export interface CancelArguments { - /** The ID (attribute 'seq') of the request to cancel. */ - requestId?: number; - } - - /** Response to 'cancel' request. This is just an acknowledgement, so no body field is required. */ - export interface CancelResponse extends Response { - } - - /** Event message for 'initialized' event type. - This event indicates that the debug adapter is ready to accept configuration requests (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest). - A debug adapter is expected to send this event when it is ready to accept configuration requests (but not before the 'initialize' request has finished). - The sequence of events/requests is as follows: - - adapters sends 'initialized' event (after the 'initialize' request has returned) - - frontend sends zero or more 'setBreakpoints' requests - - frontend sends one 'setFunctionBreakpoints' request - - frontend sends a 'setExceptionBreakpoints' request if one or more 'exceptionBreakpointFilters' have been defined (or if 'supportsConfigurationDoneRequest' is not defined or false) - - frontend sends other future configuration requests - - frontend sends one 'configurationDone' request to indicate the end of the configuration. - */ - export interface InitializedEvent extends Event { - // event: 'initialized'; - } - - /** Event message for 'stopped' event type. - The event indicates that the execution of the debuggee has stopped due to some condition. - This can be caused by a break point previously set, a stepping action has completed, by executing a debugger statement etc. - */ - export interface StoppedEvent extends Event { - // event: 'stopped'; - body: { - /** The reason for the event. - For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated). - Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', etc. - */ - reason: string; - /** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated. */ - description?: string; - /** The thread which was stopped. */ - threadId?: number; - /** A value of true hints to the frontend that this event should not change the focus. */ - preserveFocusHint?: boolean; - /** Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI. */ - text?: string; - /** If 'allThreadsStopped' is true, a debug adapter can announce that all threads have stopped. - - The client should use this information to enable that all threads can be expanded to access their stacktraces. - - If the attribute is missing or false, only the thread with the given threadId can be expanded. - */ - allThreadsStopped?: boolean; - }; - } - - /** Event message for 'continued' event type. - The event indicates that the execution of the debuggee has continued. - Please note: a debug adapter is not expected to send this event in response to a request that implies that execution continues, e.g. 'launch' or 'continue'. - It is only necessary to send a 'continued' event if there was no previous request that implied this. - */ - export interface ContinuedEvent extends Event { - // event: 'continued'; - body: { - /** The thread which was continued. */ - threadId: number; - /** If 'allThreadsContinued' is true, a debug adapter can announce that all threads have continued. */ - allThreadsContinued?: boolean; - }; - } - - /** Event message for 'exited' event type. - The event indicates that the debuggee has exited and returns its exit code. - */ - export interface ExitedEvent extends Event { - // event: 'exited'; - body: { - /** The exit code returned from the debuggee. */ - exitCode: number; - }; - } - - /** Event message for 'terminated' event type. - The event indicates that debugging of the debuggee has terminated. This does **not** mean that the debuggee itself has exited. - */ - export interface TerminatedEvent extends Event { - // event: 'terminated'; - body?: { - /** A debug adapter may set 'restart' to true (or to an arbitrary object) to request that the front end restarts the session. - The value is not interpreted by the client and passed unmodified as an attribute '__restart' to the 'launch' and 'attach' requests. - */ - restart?: any; - }; - } - - /** Event message for 'thread' event type. - The event indicates that a thread has started or exited. - */ - export interface ThreadEvent extends Event { - // event: 'thread'; - body: { - /** The reason for the event. - Values: 'started', 'exited', etc. - */ - reason: string; - /** The identifier of the thread. */ - threadId: number; - }; - } - - /** Event message for 'output' event type. - The event indicates that the target has produced some output. - */ - export interface OutputEvent extends Event { - // event: 'output'; - body: { - /** The output category. If not specified, 'console' is assumed. - Values: 'console', 'stdout', 'stderr', 'telemetry', etc. - */ - category?: string; - /** The output to report. */ - output: string; - /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference?: number; - /** An optional source location where the output was produced. */ - source?: Source; - /** An optional source location line where the output was produced. */ - line?: number; - /** An optional source location column where the output was produced. */ - column?: number; - /** Optional data to report. For the 'telemetry' category the data will be sent to telemetry, for the other categories the data is shown in JSON format. */ - data?: any; - }; - } - - /** Event message for 'breakpoint' event type. - The event indicates that some information about a breakpoint has changed. - */ - export interface BreakpointEvent extends Event { - // event: 'breakpoint'; - body: { - /** The reason for the event. - Values: 'changed', 'new', 'removed', etc. - */ - reason: string; - /** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */ - breakpoint: Breakpoint; - }; - } - - /** Event message for 'module' event type. - The event indicates that some information about a module has changed. - */ - export interface ModuleEvent extends Event { - // event: 'module'; - body: { - /** The reason for the event. */ - reason: 'new' | 'changed' | 'removed'; - /** The new, changed, or removed module. In case of 'removed' only the module id is used. */ - module: Module; - }; - } - - /** Event message for 'loadedSource' event type. - The event indicates that some source has been added, changed, or removed from the set of all loaded sources. - */ - export interface LoadedSourceEvent extends Event { - // event: 'loadedSource'; - body: { - /** The reason for the event. */ - reason: 'new' | 'changed' | 'removed'; - /** The new, changed, or removed source. */ - source: Source; - }; - } - - /** Event message for 'process' event type. - The event indicates that the debugger has begun debugging a new process. Either one that it has launched, or one that it has attached to. - */ - export interface ProcessEvent extends Event { - // event: 'process'; - body: { - /** The logical name of the process. This is usually the full path to process's executable file. Example: /home/example/myproj/program.js. */ - name: string; - /** The system process id of the debugged process. This property will be missing for non-system processes. */ - systemProcessId?: number; - /** If true, the process is running on the same computer as the debug adapter. */ - isLocalProcess?: boolean; - /** Describes how the debug engine started debugging this process. - 'launch': Process was launched under the debugger. - 'attach': Debugger attached to an existing process. - 'attachForSuspendedLaunch': A project launcher component has launched a new process in a suspended state and then asked the debugger to attach. - */ - startMethod?: 'launch' | 'attach' | 'attachForSuspendedLaunch'; - /** The size of a pointer or address for this process, in bits. This value may be used by clients when formatting addresses for display. */ - pointerSize?: number; - }; - } - - /** Event message for 'capabilities' event type. - The event indicates that one or more capabilities have changed. - Since the capabilities are dependent on the frontend and its UI, it might not be possible to change that at random times (or too late). - Consequently this event has a hint characteristic: a frontend can only be expected to make a 'best effort' in honouring individual capabilities but there are no guarantees. - Only changed capabilities need to be included, all other capabilities keep their values. - */ - export interface CapabilitiesEvent extends Event { - // event: 'capabilities'; - body: { - /** The set of updated capabilities. */ - capabilities: Capabilities; - }; - } - - /** RunInTerminal request; value of command field is 'runInTerminal'. - This request is sent from the debug adapter to the client to run a command in a terminal. This is typically used to launch the debuggee in a terminal provided by the client. - */ - export interface RunInTerminalRequest extends Request { - // command: 'runInTerminal'; - arguments: RunInTerminalRequestArguments; - } - - /** Arguments for 'runInTerminal' request. */ - export interface RunInTerminalRequestArguments { - /** What kind of terminal to launch. */ - kind?: 'integrated' | 'external'; - /** Optional title of the terminal. */ - title?: string; - /** Working directory of the command. */ - cwd: string; - /** List of arguments. The first argument is the command to run. */ - args: string[]; - /** Environment key-value pairs that are added to or removed from the default environment. */ - env?: { [key: string]: string | null; }; - } - - /** Response to 'runInTerminal' request. */ - export interface RunInTerminalResponse extends Response { - body: { - /** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */ - processId?: number; - /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ - shellProcessId?: number; - }; - } - - /** Initialize request; value of command field is 'initialize'. - The 'initialize' request is sent as the first request from the client to the debug adapter in order to configure it with client capabilities and to retrieve capabilities from the debug adapter. - Until the debug adapter has responded to with an 'initialize' response, the client must not send any additional requests or events to the debug adapter. In addition the debug adapter is not allowed to send any requests or events to the client until it has responded with an 'initialize' response. - The 'initialize' request may only be sent once. - */ - export interface InitializeRequest extends Request { - // command: 'initialize'; - arguments: InitializeRequestArguments; - } - - /** Arguments for 'initialize' request. */ - export interface InitializeRequestArguments { - /** The ID of the (frontend) client using this adapter. */ - clientID?: string; - /** The human readable name of the (frontend) client using this adapter. */ - clientName?: string; - /** The ID of the debug adapter. */ - adapterID: string; - /** The ISO-639 locale of the (frontend) client using this adapter, e.g. en-US or de-CH. */ - locale?: string; - /** If true all line numbers are 1-based (default). */ - linesStartAt1?: boolean; - /** If true all column numbers are 1-based (default). */ - columnsStartAt1?: boolean; - /** Determines in what format paths are specified. The default is 'path', which is the native format. - Values: 'path', 'uri', etc. - */ - pathFormat?: string; - /** Client supports the optional type attribute for variables. */ - supportsVariableType?: boolean; - /** Client supports the paging of variables. */ - supportsVariablePaging?: boolean; - /** Client supports the runInTerminal request. */ - supportsRunInTerminalRequest?: boolean; - /** Client supports memory references. */ - supportsMemoryReferences?: boolean; - } - - /** Response to 'initialize' request. */ - export interface InitializeResponse extends Response { - /** The capabilities of this debug adapter. */ - body?: Capabilities; - } - - /** ConfigurationDone request; value of command field is 'configurationDone'. - The client of the debug protocol must send this request at the end of the sequence of configuration requests (which was started by the 'initialized' event). - */ - export interface ConfigurationDoneRequest extends Request { - // command: 'configurationDone'; - arguments?: ConfigurationDoneArguments; - } - - /** Arguments for 'configurationDone' request. */ - export interface ConfigurationDoneArguments { - } - - /** Response to 'configurationDone' request. This is just an acknowledgement, so no body field is required. */ - export interface ConfigurationDoneResponse extends Response { - } - - /** Launch request; value of command field is 'launch'. - The launch request is sent from the client to the debug adapter to start the debuggee with or without debugging (if 'noDebug' is true). Since launching is debugger/runtime specific, the arguments for this request are not part of this specification. - */ - export interface LaunchRequest extends Request { - // command: 'launch'; - arguments: LaunchRequestArguments; - } - - /** Arguments for 'launch' request. Additional attributes are implementation specific. */ - export interface LaunchRequestArguments { - /** If noDebug is true the launch request should launch the program without enabling debugging. */ - noDebug?: boolean; - /** Optional data from the previous, restarted session. - The data is sent as the 'restart' attribute of the 'terminated' event. - The client should leave the data intact. - */ - __restart?: any; - } - - /** Response to 'launch' request. This is just an acknowledgement, so no body field is required. */ - export interface LaunchResponse extends Response { - } - - /** Attach request; value of command field is 'attach'. - The attach request is sent from the client to the debug adapter to attach to a debuggee that is already running. Since attaching is debugger/runtime specific, the arguments for this request are not part of this specification. - */ - export interface AttachRequest extends Request { - // command: 'attach'; - arguments: AttachRequestArguments; - } - - /** Arguments for 'attach' request. Additional attributes are implementation specific. */ - export interface AttachRequestArguments { - /** Optional data from the previous, restarted session. - The data is sent as the 'restart' attribute of the 'terminated' event. - The client should leave the data intact. - */ - __restart?: any; - } - - /** Response to 'attach' request. This is just an acknowledgement, so no body field is required. */ - export interface AttachResponse extends Response { - } - - /** Restart request; value of command field is 'restart'. - Restarts a debug session. If the capability 'supportsRestartRequest' is missing or has the value false, - the client will implement 'restart' by terminating the debug adapter first and then launching it anew. - A debug adapter can override this default behaviour by implementing a restart request - and setting the capability 'supportsRestartRequest' to true. - */ - export interface RestartRequest extends Request { - // command: 'restart'; - arguments?: RestartArguments; - } - - /** Arguments for 'restart' request. */ - export interface RestartArguments { - } - - /** Response to 'restart' request. This is just an acknowledgement, so no body field is required. */ - export interface RestartResponse extends Response { - } - - /** Disconnect request; value of command field is 'disconnect'. - The 'disconnect' request is sent from the client to the debug adapter in order to stop debugging. It asks the debug adapter to disconnect from the debuggee and to terminate the debug adapter. If the debuggee has been started with the 'launch' request, the 'disconnect' request terminates the debuggee. If the 'attach' request was used to connect to the debuggee, 'disconnect' does not terminate the debuggee. This behavior can be controlled with the 'terminateDebuggee' argument (if supported by the debug adapter). - */ - export interface DisconnectRequest extends Request { - // command: 'disconnect'; - arguments?: DisconnectArguments; - } - - /** Arguments for 'disconnect' request. */ - export interface DisconnectArguments { - /** A value of true indicates that this 'disconnect' request is part of a restart sequence. */ - restart?: boolean; - /** Indicates whether the debuggee should be terminated when the debugger is disconnected. - If unspecified, the debug adapter is free to do whatever it thinks is best. - A client can only rely on this attribute being properly honored if a debug adapter returns true for the 'supportTerminateDebuggee' capability. - */ - terminateDebuggee?: boolean; - } - - /** Response to 'disconnect' request. This is just an acknowledgement, so no body field is required. */ - export interface DisconnectResponse extends Response { - } - - /** Terminate request; value of command field is 'terminate'. - The 'terminate' request is sent from the client to the debug adapter in order to give the debuggee a chance for terminating itself. - */ - export interface TerminateRequest extends Request { - // command: 'terminate'; - arguments?: TerminateArguments; - } - - /** Arguments for 'terminate' request. */ - export interface TerminateArguments { - /** A value of true indicates that this 'terminate' request is part of a restart sequence. */ - restart?: boolean; - } - - /** Response to 'terminate' request. This is just an acknowledgement, so no body field is required. */ - export interface TerminateResponse extends Response { - } - - /** BreakpointLocations request; value of command field is 'breakpointLocations'. - The 'breakpointLocations' request returns all possible locations for source breakpoints in a given range. - */ - export interface BreakpointLocationsRequest extends Request { - // command: 'breakpointLocations'; - arguments?: BreakpointLocationsArguments; - } - - /** Arguments for 'breakpointLocations' request. */ - export interface BreakpointLocationsArguments { - /** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */ - source: Source; - /** Start line of range to search possible breakpoint locations in. If only the line is specified, the request returns all possible locations in that line. */ - line: number; - /** Optional start column of range to search possible breakpoint locations in. If no start column is given, the first column in the start line is assumed. */ - column?: number; - /** Optional end line of range to search possible breakpoint locations in. If no end line is given, then the end line is assumed to be the start line. */ - endLine?: number; - /** Optional end column of range to search possible breakpoint locations in. If no end column is given, then it is assumed to be in the last column of the end line. */ - endColumn?: number; - } - - /** Response to 'breakpointLocations' request. - Contains possible locations for source breakpoints. - */ - export interface BreakpointLocationsResponse extends Response { - body: { - /** Sorted set of possible breakpoint locations. */ - breakpoints: BreakpointLocation[]; - }; - } - - /** SetBreakpoints request; value of command field is 'setBreakpoints'. - Sets multiple breakpoints for a single source and clears all previous breakpoints in that source. - To clear all breakpoint for a source, specify an empty array. - When a breakpoint is hit, a 'stopped' event (with reason 'breakpoint') is generated. - */ - export interface SetBreakpointsRequest extends Request { - // command: 'setBreakpoints'; - arguments: SetBreakpointsArguments; - } - - /** Arguments for 'setBreakpoints' request. */ - export interface SetBreakpointsArguments { - /** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */ - source: Source; - /** The code locations of the breakpoints. */ - breakpoints?: SourceBreakpoint[]; - /** Deprecated: The code locations of the breakpoints. */ - lines?: number[]; - /** A value of true indicates that the underlying source has been modified which results in new breakpoint locations. */ - sourceModified?: boolean; - } - - /** Response to 'setBreakpoints' request. - Returned is information about each breakpoint created by this request. - This includes the actual code location and whether the breakpoint could be verified. - The breakpoints returned are in the same order as the elements of the 'breakpoints' - (or the deprecated 'lines') array in the arguments. - */ - export interface SetBreakpointsResponse extends Response { - body: { - /** Information about the breakpoints. The array elements are in the same order as the elements of the 'breakpoints' (or the deprecated 'lines') array in the arguments. */ - breakpoints: Breakpoint[]; - }; - } - - /** SetFunctionBreakpoints request; value of command field is 'setFunctionBreakpoints'. - Replaces all existing function breakpoints with new function breakpoints. - To clear all function breakpoints, specify an empty array. - When a function breakpoint is hit, a 'stopped' event (with reason 'function breakpoint') is generated. - */ - export interface SetFunctionBreakpointsRequest extends Request { - // command: 'setFunctionBreakpoints'; - arguments: SetFunctionBreakpointsArguments; - } - - /** Arguments for 'setFunctionBreakpoints' request. */ - export interface SetFunctionBreakpointsArguments { - /** The function names of the breakpoints. */ - breakpoints: FunctionBreakpoint[]; - } - - /** Response to 'setFunctionBreakpoints' request. - Returned is information about each breakpoint created by this request. - */ - export interface SetFunctionBreakpointsResponse extends Response { - body: { - /** Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array. */ - breakpoints: Breakpoint[]; - }; - } - - /** SetExceptionBreakpoints request; value of command field is 'setExceptionBreakpoints'. - The request configures the debuggers response to thrown exceptions. If an exception is configured to break, a 'stopped' event is fired (with reason 'exception'). - */ - export interface SetExceptionBreakpointsRequest extends Request { - // command: 'setExceptionBreakpoints'; - arguments: SetExceptionBreakpointsArguments; - } - - /** Arguments for 'setExceptionBreakpoints' request. */ - export interface SetExceptionBreakpointsArguments { - /** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */ - filters: string[]; - /** Configuration options for selected exceptions. */ - exceptionOptions?: ExceptionOptions[]; - } - - /** Response to 'setExceptionBreakpoints' request. This is just an acknowledgement, so no body field is required. */ - export interface SetExceptionBreakpointsResponse extends Response { - } - - /** DataBreakpointInfo request; value of command field is 'dataBreakpointInfo'. - Obtains information on a possible data breakpoint that could be set on an expression or variable. - */ - export interface DataBreakpointInfoRequest extends Request { - // command: 'dataBreakpointInfo'; - arguments: DataBreakpointInfoArguments; - } - - /** Arguments for 'dataBreakpointInfo' request. */ - export interface DataBreakpointInfoArguments { - /** Reference to the Variable container if the data breakpoint is requested for a child of the container. */ - variablesReference?: number; - /** The name of the Variable's child to obtain data breakpoint information for. If variableReference isn’t provided, this can be an expression. */ - name: string; - } - - /** Response to 'dataBreakpointInfo' request. */ - export interface DataBreakpointInfoResponse extends Response { - body: { - /** An identifier for the data on which a data breakpoint can be registered with the setDataBreakpoints request or null if no data breakpoint is available. */ - dataId: string | null; - /** UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available. */ - description: string; - /** Optional attribute listing the available access types for a potential data breakpoint. A UI frontend could surface this information. */ - accessTypes?: DataBreakpointAccessType[]; - /** Optional attribute indicating that a potential data breakpoint could be persisted across sessions. */ - canPersist?: boolean; - }; - } - - /** SetDataBreakpoints request; value of command field is 'setDataBreakpoints'. - Replaces all existing data breakpoints with new data breakpoints. - To clear all data breakpoints, specify an empty array. - When a data breakpoint is hit, a 'stopped' event (with reason 'data breakpoint') is generated. - */ - export interface SetDataBreakpointsRequest extends Request { - // command: 'setDataBreakpoints'; - arguments: SetDataBreakpointsArguments; - } - - /** Arguments for 'setDataBreakpoints' request. */ - export interface SetDataBreakpointsArguments { - /** The contents of this array replaces all existing data breakpoints. An empty array clears all data breakpoints. */ - breakpoints: DataBreakpoint[]; - } - - /** Response to 'setDataBreakpoints' request. - Returned is information about each breakpoint created by this request. - */ - export interface SetDataBreakpointsResponse extends Response { - body: { - /** Information about the data breakpoints. The array elements correspond to the elements of the input argument 'breakpoints' array. */ - breakpoints: Breakpoint[]; - }; - } - - /** Continue request; value of command field is 'continue'. - The request starts the debuggee to run again. - */ - export interface ContinueRequest extends Request { - // command: 'continue'; - arguments: ContinueArguments; - } - - /** Arguments for 'continue' request. */ - export interface ContinueArguments { - /** Continue execution for the specified thread (if possible). If the backend cannot continue on a single thread but will continue on all threads, it should set the 'allThreadsContinued' attribute in the response to true. */ - threadId: number; - } - - /** Response to 'continue' request. */ - export interface ContinueResponse extends Response { - body: { - /** If true, the 'continue' request has ignored the specified thread and continued all threads instead. If this attribute is missing a value of 'true' is assumed for backward compatibility. */ - allThreadsContinued?: boolean; - }; - } - - /** Next request; value of command field is 'next'. - The request starts the debuggee to run again for one step. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. - */ - export interface NextRequest extends Request { - // command: 'next'; - arguments: NextArguments; - } - - /** Arguments for 'next' request. */ - export interface NextArguments { - /** Execute 'next' for this thread. */ - threadId: number; - } - - /** Response to 'next' request. This is just an acknowledgement, so no body field is required. */ - export interface NextResponse extends Response { - } - - /** StepIn request; value of command field is 'stepIn'. - The request starts the debuggee to step into a function/method if possible. - If it cannot step into a target, 'stepIn' behaves like 'next'. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. - If there are multiple function/method calls (or other targets) on the source line, - the optional argument 'targetId' can be used to control into which target the 'stepIn' should occur. - The list of possible targets for a given source line can be retrieved via the 'stepInTargets' request. - */ - export interface StepInRequest extends Request { - // command: 'stepIn'; - arguments: StepInArguments; - } - - /** Arguments for 'stepIn' request. */ - export interface StepInArguments { - /** Execute 'stepIn' for this thread. */ - threadId: number; - /** Optional id of the target to step into. */ - targetId?: number; - } - - /** Response to 'stepIn' request. This is just an acknowledgement, so no body field is required. */ - export interface StepInResponse extends Response { - } - - /** StepOut request; value of command field is 'stepOut'. - The request starts the debuggee to run again for one step. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. - */ - export interface StepOutRequest extends Request { - // command: 'stepOut'; - arguments: StepOutArguments; - } - - /** Arguments for 'stepOut' request. */ - export interface StepOutArguments { - /** Execute 'stepOut' for this thread. */ - threadId: number; - } - - /** Response to 'stepOut' request. This is just an acknowledgement, so no body field is required. */ - export interface StepOutResponse extends Response { - } - - /** StepBack request; value of command field is 'stepBack'. - The request starts the debuggee to run one step backwards. - The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed. Clients should only call this request if the capability 'supportsStepBack' is true. - */ - export interface StepBackRequest extends Request { - // command: 'stepBack'; - arguments: StepBackArguments; - } - - /** Arguments for 'stepBack' request. */ - export interface StepBackArguments { - /** Execute 'stepBack' for this thread. */ - threadId: number; - } - - /** Response to 'stepBack' request. This is just an acknowledgement, so no body field is required. */ - export interface StepBackResponse extends Response { - } - - /** ReverseContinue request; value of command field is 'reverseContinue'. - The request starts the debuggee to run backward. Clients should only call this request if the capability 'supportsStepBack' is true. - */ - export interface ReverseContinueRequest extends Request { - // command: 'reverseContinue'; - arguments: ReverseContinueArguments; - } - - /** Arguments for 'reverseContinue' request. */ - export interface ReverseContinueArguments { - /** Execute 'reverseContinue' for this thread. */ - threadId: number; - } - - /** Response to 'reverseContinue' request. This is just an acknowledgement, so no body field is required. */ - export interface ReverseContinueResponse extends Response { - } - - /** RestartFrame request; value of command field is 'restartFrame'. - The request restarts execution of the specified stackframe. - The debug adapter first sends the response and then a 'stopped' event (with reason 'restart') after the restart has completed. - */ - export interface RestartFrameRequest extends Request { - // command: 'restartFrame'; - arguments: RestartFrameArguments; - } - - /** Arguments for 'restartFrame' request. */ - export interface RestartFrameArguments { - /** Restart this stackframe. */ - frameId: number; - } - - /** Response to 'restartFrame' request. This is just an acknowledgement, so no body field is required. */ - export interface RestartFrameResponse extends Response { - } - - /** Goto request; value of command field is 'goto'. - The request sets the location where the debuggee will continue to run. - This makes it possible to skip the execution of code or to executed code again. - The code between the current location and the goto target is not executed but skipped. - The debug adapter first sends the response and then a 'stopped' event with reason 'goto'. - */ - export interface GotoRequest extends Request { - // command: 'goto'; - arguments: GotoArguments; - } - - /** Arguments for 'goto' request. */ - export interface GotoArguments { - /** Set the goto target for this thread. */ - threadId: number; - /** The location where the debuggee will continue to run. */ - targetId: number; - } - - /** Response to 'goto' request. This is just an acknowledgement, so no body field is required. */ - export interface GotoResponse extends Response { - } - - /** Pause request; value of command field is 'pause'. - The request suspends the debuggee. - The debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully. - */ - export interface PauseRequest extends Request { - // command: 'pause'; - arguments: PauseArguments; - } - - /** Arguments for 'pause' request. */ - export interface PauseArguments { - /** Pause execution for this thread. */ - threadId: number; - } - - /** Response to 'pause' request. This is just an acknowledgement, so no body field is required. */ - export interface PauseResponse extends Response { - } - - /** StackTrace request; value of command field is 'stackTrace'. - The request returns a stacktrace from the current execution state. - */ - export interface StackTraceRequest extends Request { - // command: 'stackTrace'; - arguments: StackTraceArguments; - } - - /** Arguments for 'stackTrace' request. */ - export interface StackTraceArguments { - /** Retrieve the stacktrace for this thread. */ - threadId: number; - /** The index of the first frame to return; if omitted frames start at 0. */ - startFrame?: number; - /** The maximum number of frames to return. If levels is not specified or 0, all frames are returned. */ - levels?: number; - /** Specifies details on how to format the stack frames. */ - format?: StackFrameFormat; - } - - /** Response to 'stackTrace' request. */ - export interface StackTraceResponse extends Response { - body: { - /** The frames of the stackframe. If the array has length zero, there are no stackframes available. - This means that there is no location information available. - */ - stackFrames: StackFrame[]; - /** The total number of frames available. */ - totalFrames?: number; - }; - } - - /** Scopes request; value of command field is 'scopes'. - The request returns the variable scopes for a given stackframe ID. - */ - export interface ScopesRequest extends Request { - // command: 'scopes'; - arguments: ScopesArguments; - } - - /** Arguments for 'scopes' request. */ - export interface ScopesArguments { - /** Retrieve the scopes for this stackframe. */ - frameId: number; - } - - /** Response to 'scopes' request. */ - export interface ScopesResponse extends Response { - body: { - /** The scopes of the stackframe. If the array has length zero, there are no scopes available. */ - scopes: Scope[]; - }; - } - - /** Variables request; value of command field is 'variables'. - Retrieves all child variables for the given variable reference. - An optional filter can be used to limit the fetched children to either named or indexed children. - */ - export interface VariablesRequest extends Request { - // command: 'variables'; - arguments: VariablesArguments; - } - - /** Arguments for 'variables' request. */ - export interface VariablesArguments { - /** The Variable reference. */ - variablesReference: number; - /** Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */ - filter?: 'indexed' | 'named'; - /** The index of the first variable to return; if omitted children start at 0. */ - start?: number; - /** The number of variables to return. If count is missing or 0, all variables are returned. */ - count?: number; - /** Specifies details on how to format the Variable values. */ - format?: ValueFormat; - } - - /** Response to 'variables' request. */ - export interface VariablesResponse extends Response { - body: { - /** All (or a range) of variables for the given variable reference. */ - variables: Variable[]; - }; - } - - /** SetVariable request; value of command field is 'setVariable'. - Set the variable with the given name in the variable container to a new value. - */ - export interface SetVariableRequest extends Request { - // command: 'setVariable'; - arguments: SetVariableArguments; - } - - /** Arguments for 'setVariable' request. */ - export interface SetVariableArguments { - /** The reference of the variable container. */ - variablesReference: number; - /** The name of the variable in the container. */ - name: string; - /** The value of the variable. */ - value: string; - /** Specifies details on how to format the response value. */ - format?: ValueFormat; - } - - /** Response to 'setVariable' request. */ - export interface SetVariableResponse extends Response { - body: { - /** The new value of the variable. */ - value: string; - /** The type of the new value. Typically shown in the UI when hovering over the value. */ - type?: string; - /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference?: number; - /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - indexedVariables?: number; - }; - } - - /** Source request; value of command field is 'source'. - The request retrieves the source code for a given source reference. - */ - export interface SourceRequest extends Request { - // command: 'source'; - arguments: SourceArguments; - } - - /** Arguments for 'source' request. */ - export interface SourceArguments { - /** Specifies the source content to load. Either source.path or source.sourceReference must be specified. */ - source?: Source; - /** The reference to the source. This is the same as source.sourceReference. This is provided for backward compatibility since old backends do not understand the 'source' attribute. */ - sourceReference: number; - } - - /** Response to 'source' request. */ - export interface SourceResponse extends Response { - body: { - /** Content of the source reference. */ - content: string; - /** Optional content type (mime type) of the source. */ - mimeType?: string; - }; - } - - /** Threads request; value of command field is 'threads'. - The request retrieves a list of all threads. - */ - export interface ThreadsRequest extends Request { - // command: 'threads'; - } - - /** Response to 'threads' request. */ - export interface ThreadsResponse extends Response { - body: { - /** All threads. */ - threads: Thread[]; - }; - } - - /** TerminateThreads request; value of command field is 'terminateThreads'. - The request terminates the threads with the given ids. - */ - export interface TerminateThreadsRequest extends Request { - // command: 'terminateThreads'; - arguments: TerminateThreadsArguments; - } - - /** Arguments for 'terminateThreads' request. */ - export interface TerminateThreadsArguments { - /** Ids of threads to be terminated. */ - threadIds?: number[]; - } - - /** Response to 'terminateThreads' request. This is just an acknowledgement, so no body field is required. */ - export interface TerminateThreadsResponse extends Response { - } - - /** Modules request; value of command field is 'modules'. - Modules can be retrieved from the debug adapter with the ModulesRequest which can either return all modules or a range of modules to support paging. - */ - export interface ModulesRequest extends Request { - // command: 'modules'; - arguments: ModulesArguments; - } - - /** Arguments for 'modules' request. */ - export interface ModulesArguments { - /** The index of the first module to return; if omitted modules start at 0. */ - startModule?: number; - /** The number of modules to return. If moduleCount is not specified or 0, all modules are returned. */ - moduleCount?: number; - } - - /** Response to 'modules' request. */ - export interface ModulesResponse extends Response { - body: { - /** All modules or range of modules. */ - modules: Module[]; - /** The total number of modules available. */ - totalModules?: number; - }; - } - - /** LoadedSources request; value of command field is 'loadedSources'. - Retrieves the set of all sources currently loaded by the debugged process. - */ - export interface LoadedSourcesRequest extends Request { - // command: 'loadedSources'; - arguments?: LoadedSourcesArguments; - } - - /** Arguments for 'loadedSources' request. */ - export interface LoadedSourcesArguments { - } - - /** Response to 'loadedSources' request. */ - export interface LoadedSourcesResponse extends Response { - body: { - /** Set of loaded sources. */ - sources: Source[]; - }; - } - - /** Evaluate request; value of command field is 'evaluate'. - Evaluates the given expression in the context of the top most stack frame. - The expression has access to any variables and arguments that are in scope. - */ - export interface EvaluateRequest extends Request { - // command: 'evaluate'; - arguments: EvaluateArguments; - } - - /** Arguments for 'evaluate' request. */ - export interface EvaluateArguments { - /** The expression to evaluate. */ - expression: string; - /** Evaluate the expression in the scope of this stack frame. If not specified, the expression is evaluated in the global scope. */ - frameId?: number; - /** The context in which the evaluate request is run. - Values: - 'watch': evaluate is run in a watch. - 'repl': evaluate is run from REPL console. - 'hover': evaluate is run from a data hover. - etc. - */ - context?: string; - /** Specifies details on how to format the Evaluate result. */ - format?: ValueFormat; - } - - /** Response to 'evaluate' request. */ - export interface EvaluateResponse extends Response { - body: { - /** The result of the evaluate request. */ - result: string; - /** The optional type of the evaluate result. */ - type?: string; - /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ - presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference: number; - /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - indexedVariables?: number; - /** Memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. */ - memoryReference?: string; - }; - } - - /** SetExpression request; value of command field is 'setExpression'. - Evaluates the given 'value' expression and assigns it to the 'expression' which must be a modifiable l-value. - The expressions have access to any variables and arguments that are in scope of the specified frame. - */ - export interface SetExpressionRequest extends Request { - // command: 'setExpression'; - arguments: SetExpressionArguments; - } - - /** Arguments for 'setExpression' request. */ - export interface SetExpressionArguments { - /** The l-value expression to assign to. */ - expression: string; - /** The value expression to assign to the l-value expression. */ - value: string; - /** Evaluate the expressions in the scope of this stack frame. If not specified, the expressions are evaluated in the global scope. */ - frameId?: number; - /** Specifies how the resulting value should be formatted. */ - format?: ValueFormat; - } - - /** Response to 'setExpression' request. */ - export interface SetExpressionResponse extends Response { - body: { - /** The new value of the expression. */ - value: string; - /** The optional type of the value. */ - type?: string; - /** Properties of a value that can be used to determine how to render the result in the UI. */ - presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ - variablesReference?: number; - /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). - */ - indexedVariables?: number; - }; - } - - /** StepInTargets request; value of command field is 'stepInTargets'. - This request retrieves the possible stepIn targets for the specified stack frame. - These targets can be used in the 'stepIn' request. - The StepInTargets may only be called if the 'supportsStepInTargetsRequest' capability exists and is true. - */ - export interface StepInTargetsRequest extends Request { - // command: 'stepInTargets'; - arguments: StepInTargetsArguments; - } - - /** Arguments for 'stepInTargets' request. */ - export interface StepInTargetsArguments { - /** The stack frame for which to retrieve the possible stepIn targets. */ - frameId: number; - } - - /** Response to 'stepInTargets' request. */ - export interface StepInTargetsResponse extends Response { - body: { - /** The possible stepIn targets of the specified source location. */ - targets: StepInTarget[]; - }; - } - - /** GotoTargets request; value of command field is 'gotoTargets'. - This request retrieves the possible goto targets for the specified source location. - These targets can be used in the 'goto' request. - The GotoTargets request may only be called if the 'supportsGotoTargetsRequest' capability exists and is true. - */ - export interface GotoTargetsRequest extends Request { - // command: 'gotoTargets'; - arguments: GotoTargetsArguments; - } - - /** Arguments for 'gotoTargets' request. */ - export interface GotoTargetsArguments { - /** The source location for which the goto targets are determined. */ - source: Source; - /** The line location for which the goto targets are determined. */ - line: number; - /** An optional column location for which the goto targets are determined. */ - column?: number; - } - - /** Response to 'gotoTargets' request. */ - export interface GotoTargetsResponse extends Response { - body: { - /** The possible goto targets of the specified location. */ - targets: GotoTarget[]; - }; - } - - /** Completions request; value of command field is 'completions'. - Returns a list of possible completions for a given caret position and text. - The CompletionsRequest may only be called if the 'supportsCompletionsRequest' capability exists and is true. - */ - export interface CompletionsRequest extends Request { - // command: 'completions'; - arguments: CompletionsArguments; - } - - /** Arguments for 'completions' request. */ - export interface CompletionsArguments { - /** Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope. */ - frameId?: number; - /** One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion. */ - text: string; - /** The character position for which to determine the completion proposals. */ - column: number; - /** An optional line for which to determine the completion proposals. If missing the first line of the text is assumed. */ - line?: number; - } - - /** Response to 'completions' request. */ - export interface CompletionsResponse extends Response { - body: { - /** The possible completions for . */ - targets: CompletionItem[]; - }; - } - - /** ExceptionInfo request; value of command field is 'exceptionInfo'. - Retrieves the details of the exception that caused this event to be raised. - */ - export interface ExceptionInfoRequest extends Request { - // command: 'exceptionInfo'; - arguments: ExceptionInfoArguments; - } - - /** Arguments for 'exceptionInfo' request. */ - export interface ExceptionInfoArguments { - /** Thread for which exception information should be retrieved. */ - threadId: number; - } - - /** Response to 'exceptionInfo' request. */ - export interface ExceptionInfoResponse extends Response { - body: { - /** ID of the exception that was thrown. */ - exceptionId: string; - /** Descriptive text for the exception provided by the debug adapter. */ - description?: string; - /** Mode that caused the exception notification to be raised. */ - breakMode: ExceptionBreakMode; - /** Detailed information about the exception. */ - details?: ExceptionDetails; - }; - } - - /** ReadMemory request; value of command field is 'readMemory'. - Reads bytes from memory at the provided location. - */ - export interface ReadMemoryRequest extends Request { - // command: 'readMemory'; - arguments: ReadMemoryArguments; - } - - /** Arguments for 'readMemory' request. */ - export interface ReadMemoryArguments { - /** Memory reference to the base location from which data should be read. */ - memoryReference: string; - /** Optional offset (in bytes) to be applied to the reference location before reading data. Can be negative. */ - offset?: number; - /** Number of bytes to read at the specified location and offset. */ - count: number; - } - - /** Response to 'readMemory' request. */ - export interface ReadMemoryResponse extends Response { - body?: { - /** The address of the first byte of data returned. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise. */ - address: string; - /** The number of unreadable bytes encountered after the last successfully read byte. This can be used to determine the number of bytes that must be skipped before a subsequent 'readMemory' request will succeed. */ - unreadableBytes?: number; - /** The bytes read from memory, encoded using base64. */ - data?: string; - }; - } - - /** Disassemble request; value of command field is 'disassemble'. - Disassembles code stored at the provided location. - */ - export interface DisassembleRequest extends Request { - // command: 'disassemble'; - arguments: DisassembleArguments; - } - - /** Arguments for 'disassemble' request. */ - export interface DisassembleArguments { - /** Memory reference to the base location containing the instructions to disassemble. */ - memoryReference: string; - /** Optional offset (in bytes) to be applied to the reference location before disassembling. Can be negative. */ - offset?: number; - /** Optional offset (in instructions) to be applied after the byte offset (if any) before disassembling. Can be negative. */ - instructionOffset?: number; - /** Number of instructions to disassemble starting at the specified location and offset. An adapter must return exactly this number of instructions - any unavailable instructions should be replaced with an implementation-defined 'invalid instruction' value. */ - instructionCount: number; - /** If true, the adapter should attempt to resolve memory addresses and other values to symbolic names. */ - resolveSymbols?: boolean; - } - - /** Response to 'disassemble' request. */ - export interface DisassembleResponse extends Response { - body?: { - /** The list of disassembled instructions. */ - instructions: DisassembledInstruction[]; - }; - } - - /** Information about the capabilities of a debug adapter. */ - export interface Capabilities { - /** The debug adapter supports the 'configurationDone' request. */ - supportsConfigurationDoneRequest?: boolean; - /** The debug adapter supports function breakpoints. */ - supportsFunctionBreakpoints?: boolean; - /** The debug adapter supports conditional breakpoints. */ - supportsConditionalBreakpoints?: boolean; - /** The debug adapter supports breakpoints that break execution after a specified number of hits. */ - supportsHitConditionalBreakpoints?: boolean; - /** The debug adapter supports a (side effect free) evaluate request for data hovers. */ - supportsEvaluateForHovers?: boolean; - /** Available filters or options for the setExceptionBreakpoints request. */ - exceptionBreakpointFilters?: ExceptionBreakpointsFilter[]; - /** The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests. */ - supportsStepBack?: boolean; - /** The debug adapter supports setting a variable to a value. */ - supportsSetVariable?: boolean; - /** The debug adapter supports restarting a frame. */ - supportsRestartFrame?: boolean; - /** The debug adapter supports the 'gotoTargets' request. */ - supportsGotoTargetsRequest?: boolean; - /** The debug adapter supports the 'stepInTargets' request. */ - supportsStepInTargetsRequest?: boolean; - /** The debug adapter supports the 'completions' request. */ - supportsCompletionsRequest?: boolean; - /** The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character. */ - completionTriggerCharacters?: string[]; - /** The debug adapter supports the 'modules' request. */ - supportsModulesRequest?: boolean; - /** The set of additional module information exposed by the debug adapter. */ - additionalModuleColumns?: ColumnDescriptor[]; - /** Checksum algorithms supported by the debug adapter. */ - supportedChecksumAlgorithms?: ChecksumAlgorithm[]; - /** The debug adapter supports the 'restart' request. In this case a client should not implement 'restart' by terminating and relaunching the adapter but by calling the RestartRequest. */ - supportsRestartRequest?: boolean; - /** The debug adapter supports 'exceptionOptions' on the setExceptionBreakpoints request. */ - supportsExceptionOptions?: boolean; - /** The debug adapter supports a 'format' attribute on the stackTraceRequest, variablesRequest, and evaluateRequest. */ - supportsValueFormattingOptions?: boolean; - /** The debug adapter supports the 'exceptionInfo' request. */ - supportsExceptionInfoRequest?: boolean; - /** The debug adapter supports the 'terminateDebuggee' attribute on the 'disconnect' request. */ - supportTerminateDebuggee?: boolean; - /** The debug adapter supports the delayed loading of parts of the stack, which requires that both the 'startFrame' and 'levels' arguments and the 'totalFrames' result of the 'StackTrace' request are supported. */ - supportsDelayedStackTraceLoading?: boolean; - /** The debug adapter supports the 'loadedSources' request. */ - supportsLoadedSourcesRequest?: boolean; - /** The debug adapter supports logpoints by interpreting the 'logMessage' attribute of the SourceBreakpoint. */ - supportsLogPoints?: boolean; - /** The debug adapter supports the 'terminateThreads' request. */ - supportsTerminateThreadsRequest?: boolean; - /** The debug adapter supports the 'setExpression' request. */ - supportsSetExpression?: boolean; - /** The debug adapter supports the 'terminate' request. */ - supportsTerminateRequest?: boolean; - /** The debug adapter supports data breakpoints. */ - supportsDataBreakpoints?: boolean; - /** The debug adapter supports the 'readMemory' request. */ - supportsReadMemoryRequest?: boolean; - /** The debug adapter supports the 'disassemble' request. */ - supportsDisassembleRequest?: boolean; - /** The debug adapter supports the 'cancel' request. */ - supportsCancelRequest?: boolean; - /** The debug adapter supports the 'breakpointLocations' request. */ - supportsBreakpointLocationsRequest?: boolean; - } - - /** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */ - export interface ExceptionBreakpointsFilter { - /** The internal ID of the filter. This value is passed to the setExceptionBreakpoints request. */ - filter: string; - /** The name of the filter. This will be shown in the UI. */ - label: string; - /** Initial value of the filter. If not specified a value 'false' is assumed. */ - default?: boolean; - } - - /** A structured message object. Used to return errors from requests. */ - export interface Message { - /** Unique identifier for the message. */ - id: number; - /** A format string for the message. Embedded variables have the form '{name}'. - If variable name starts with an underscore character, the variable does not contain user data (PII) and can be safely used for telemetry purposes. - */ - format: string; - /** An object used as a dictionary for looking up the variables in the format string. */ - variables?: { [key: string]: string; }; - /** If true send to telemetry. */ - sendTelemetry?: boolean; - /** If true show user. */ - showUser?: boolean; - /** An optional url where additional information about this message can be found. */ - url?: string; - /** An optional label that is presented to the user as the UI for opening the url. */ - urlLabel?: string; - } - - /** A Module object represents a row in the modules view. - Two attributes are mandatory: an id identifies a module in the modules view and is used in a ModuleEvent for identifying a module for adding, updating or deleting. - The name is used to minimally render the module in the UI. - - Additional attributes can be added to the module. They will show up in the module View if they have a corresponding ColumnDescriptor. - - To avoid an unnecessary proliferation of additional attributes with similar semantics but different names - we recommend to re-use attributes from the 'recommended' list below first, and only introduce new attributes if nothing appropriate could be found. - */ - export interface Module { - /** Unique identifier for the module. */ - id: number | string; - /** A name of the module. */ - name: string; - /** optional but recommended attributes. - always try to use these first before introducing additional attributes. - - Logical full path to the module. The exact definition is implementation defined, but usually this would be a full path to the on-disk file for the module. - */ - path?: string; - /** True if the module is optimized. */ - isOptimized?: boolean; - /** True if the module is considered 'user code' by a debugger that supports 'Just My Code'. */ - isUserCode?: boolean; - /** Version of Module. */ - version?: string; - /** User understandable description of if symbols were found for the module (ex: 'Symbols Loaded', 'Symbols not found', etc. */ - symbolStatus?: string; - /** Logical full path to the symbol file. The exact definition is implementation defined. */ - symbolFilePath?: string; - /** Module created or modified. */ - dateTimeStamp?: string; - /** Address range covered by this module. */ - addressRange?: string; - } - - /** A ColumnDescriptor specifies what module attribute to show in a column of the ModulesView, how to format it, and what the column's label should be. - It is only used if the underlying UI actually supports this level of customization. - */ - export interface ColumnDescriptor { - /** Name of the attribute rendered in this column. */ - attributeName: string; - /** Header UI label of column. */ - label: string; - /** Format to use for the rendered values in this column. TBD how the format strings looks like. */ - format?: string; - /** Datatype of values in this column. Defaults to 'string' if not specified. */ - type?: 'string' | 'number' | 'boolean' | 'unixTimestampUTC'; - /** Width of this column in characters (hint only). */ - width?: number; - } - - /** The ModulesViewDescriptor is the container for all declarative configuration options of a ModuleView. - For now it only specifies the columns to be shown in the modules view. - */ - export interface ModulesViewDescriptor { - columns: ColumnDescriptor[]; - } - - /** A Thread */ - export interface Thread { - /** Unique identifier for the thread. */ - id: number; - /** A name of the thread. */ - name: string; - } - - /** A Source is a descriptor for source code. It is returned from the debug adapter as part of a StackFrame and it is used by clients when specifying breakpoints. */ - export interface Source { - /** The short name of the source. Every source returned from the debug adapter has a name. When sending a source to the debug adapter this name is optional. */ - name?: string; - /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0). */ - path?: string; - /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. The value should be less than or equal to 2147483647 (2^31 - 1). */ - sourceReference?: number; - /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ - presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; - /** The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc. */ - origin?: string; - /** An optional list of sources that are related to this source. These may be the source that generated this source. */ - sources?: Source[]; - /** Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ - adapterData?: any; - /** The checksums associated with this file. */ - checksums?: Checksum[]; - } - - /** A Stackframe contains the source location. */ - export interface StackFrame { - /** An identifier for the stack frame. It must be unique across all threads. This id can be used to retrieve the scopes of the frame with the 'scopesRequest' or to restart the execution of a stackframe. */ - id: number; - /** The name of the stack frame, typically a method name. */ - name: string; - /** The optional source of the frame. */ - source?: Source; - /** The line within the file of the frame. If source is null or doesn't exist, line is 0 and must be ignored. */ - line: number; - /** The column within the line. If source is null or doesn't exist, column is 0 and must be ignored. */ - column: number; - /** An optional end line of the range covered by the stack frame. */ - endLine?: number; - /** An optional end column of the range covered by the stack frame. */ - endColumn?: number; - /** Optional memory reference for the current instruction pointer in this frame. */ - instructionPointerReference?: string; - /** The module associated with this frame, if any. */ - moduleId?: number | string; - /** An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way. */ - presentationHint?: 'normal' | 'label' | 'subtle'; - } - - /** A Scope is a named container for variables. Optionally a scope can map to a source or a range within a source. */ - export interface Scope { - /** Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This string is shown in the UI as is and can be translated. */ - name: string; - /** An optional hint for how to present this scope in the UI. If this attribute is missing, the scope is shown with a generic UI. - Values: - 'arguments': Scope contains method arguments. - 'locals': Scope contains local variables. - 'registers': Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request. - etc. - */ - presentationHint?: string; - /** The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest. */ - variablesReference: number; - /** The number of named variables in this scope. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - */ - namedVariables?: number; - /** The number of indexed variables in this scope. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - */ - indexedVariables?: number; - /** If true, the number of variables in this scope is large or expensive to retrieve. */ - expensive: boolean; - /** Optional source for this scope. */ - source?: Source; - /** Optional start line of the range covered by this scope. */ - line?: number; - /** Optional start column of the range covered by this scope. */ - column?: number; - /** Optional end line of the range covered by this scope. */ - endLine?: number; - /** Optional end column of the range covered by this scope. */ - endColumn?: number; - } - - /** A Variable is a name/value pair. - Optionally a variable can have a 'type' that is shown if space permits or when hovering over the variable's name. - An optional 'kind' is used to render additional properties of the variable, e.g. different icons can be used to indicate that a variable is public or private. - If the value is structured (has children), a handle is provided to retrieve the children with the VariablesRequest. - If the number of named or indexed children is large, the numbers should be returned via the optional 'namedVariables' and 'indexedVariables' attributes. - The client can use this optional information to present the children in a paged UI and fetch them in chunks. - */ - export interface Variable { - /** The variable's name. */ - name: string; - /** The variable's value. This can be a multi-line text, e.g. for a function the body of a function. */ - value: string; - /** The type of the variable's value. Typically shown in the UI when hovering over the value. */ - type?: string; - /** Properties of a variable that can be used to determine how to render the variable in the UI. */ - presentationHint?: VariablePresentationHint; - /** Optional evaluatable name of this variable which can be passed to the 'EvaluateRequest' to fetch the variable's value. */ - evaluateName?: string; - /** If variablesReference is > 0, the variable is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ - variablesReference: number; - /** The number of named child variables. - The client can use this optional information to present the children in a paged UI and fetch them in chunks. - */ - namedVariables?: number; - /** The number of indexed child variables. - The client can use this optional information to present the children in a paged UI and fetch them in chunks. - */ - indexedVariables?: number; - /** Optional memory reference for the variable if the variable represents executable code, such as a function pointer. */ - memoryReference?: string; - } - - /** Optional properties of a variable that can be used to determine how to render the variable in the UI. */ - export interface VariablePresentationHint { - /** The kind of variable. Before introducing additional values, try to use the listed values. - Values: - 'property': Indicates that the object is a property. - 'method': Indicates that the object is a method. - 'class': Indicates that the object is a class. - 'data': Indicates that the object is data. - 'event': Indicates that the object is an event. - 'baseClass': Indicates that the object is a base class. - 'innerClass': Indicates that the object is an inner class. - 'interface': Indicates that the object is an interface. - 'mostDerivedClass': Indicates that the object is the most derived class. - 'virtual': Indicates that the object is virtual, that means it is a synthetic object introduced by the adapter for rendering purposes, e.g. an index range for large arrays. - 'dataBreakpoint': Indicates that a data breakpoint is registered for the object. - etc. - */ - kind?: string; - /** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values. - Values: - 'static': Indicates that the object is static. - 'constant': Indicates that the object is a constant. - 'readOnly': Indicates that the object is read only. - 'rawString': Indicates that the object is a raw string. - 'hasObjectId': Indicates that the object can have an Object ID created for it. - 'canHaveObjectId': Indicates that the object has an Object ID associated with it. - 'hasSideEffects': Indicates that the evaluation had side effects. - etc. - */ - attributes?: string[]; - /** Visibility of variable. Before introducing additional values, try to use the listed values. - Values: 'public', 'private', 'protected', 'internal', 'final', etc. - */ - visibility?: string; - } - - /** Properties of a breakpoint location returned from the 'breakpointLocations' request. */ - export interface BreakpointLocation { - /** Start line of breakpoint location. */ - line: number; - /** Optional start column of breakpoint location. */ - column?: number; - /** Optional end line of breakpoint location if the location covers a range. */ - endLine?: number; - /** Optional end column of breakpoint location if the location covers a range. */ - endColumn?: number; - } - - /** Properties of a breakpoint or logpoint passed to the setBreakpoints request. */ - export interface SourceBreakpoint { - /** The source line of the breakpoint or logpoint. */ - line: number; - /** An optional source column of the breakpoint. */ - column?: number; - /** An optional expression for conditional breakpoints. */ - condition?: string; - /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ - hitCondition?: string; - /** If this attribute exists and is non-empty, the backend must not 'break' (stop) but log the message instead. Expressions within {} are interpolated. */ - logMessage?: string; - } - - /** Properties of a breakpoint passed to the setFunctionBreakpoints request. */ - export interface FunctionBreakpoint { - /** The name of the function. */ - name: string; - /** An optional expression for conditional breakpoints. */ - condition?: string; - /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ - hitCondition?: string; - } - - /** This enumeration defines all possible access types for data breakpoints. */ - export type DataBreakpointAccessType = 'read' | 'write' | 'readWrite'; - - /** Properties of a data breakpoint passed to the setDataBreakpoints request. */ - export interface DataBreakpoint { - /** An id representing the data. This id is returned from the dataBreakpointInfo request. */ - dataId: string; - /** The access type of the data. */ - accessType?: DataBreakpointAccessType; - /** An optional expression for conditional breakpoints. */ - condition?: string; - /** An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. */ - hitCondition?: string; - } - - /** Information about a Breakpoint created in setBreakpoints or setFunctionBreakpoints. */ - export interface Breakpoint { - /** An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints. */ - id?: number; - /** If true breakpoint could be set (but not necessarily at the desired location). */ - verified: boolean; - /** An optional message about the state of the breakpoint. This is shown to the user and can be used to explain why a breakpoint could not be verified. */ - message?: string; - /** The source where the breakpoint is located. */ - source?: Source; - /** The start line of the actual range covered by the breakpoint. */ - line?: number; - /** An optional start column of the actual range covered by the breakpoint. */ - column?: number; - /** An optional end line of the actual range covered by the breakpoint. */ - endLine?: number; - /** An optional end column of the actual range covered by the breakpoint. If no end line is given, then the end column is assumed to be in the start line. */ - endColumn?: number; - } - - /** A StepInTarget can be used in the 'stepIn' request and determines into which single target the stepIn request should step. */ - export interface StepInTarget { - /** Unique identifier for a stepIn target. */ - id: number; - /** The name of the stepIn target (shown in the UI). */ - label: string; - } - - /** A GotoTarget describes a code location that can be used as a target in the 'goto' request. - The possible goto targets can be determined via the 'gotoTargets' request. - */ - export interface GotoTarget { - /** Unique identifier for a goto target. This is used in the goto request. */ - id: number; - /** The name of the goto target (shown in the UI). */ - label: string; - /** The line of the goto target. */ - line: number; - /** An optional column of the goto target. */ - column?: number; - /** An optional end line of the range covered by the goto target. */ - endLine?: number; - /** An optional end column of the range covered by the goto target. */ - endColumn?: number; - /** Optional memory reference for the instruction pointer value represented by this target. */ - instructionPointerReference?: string; - } - - /** CompletionItems are the suggestions returned from the CompletionsRequest. */ - export interface CompletionItem { - /** The label of this completion item. By default this is also the text that is inserted when selecting this completion. */ - label: string; - /** If text is not falsy then it is inserted instead of the label. */ - text?: string; - /** A string that should be used when comparing this item with other items. When `falsy` the label is used. */ - sortText?: string; - /** The item's type. Typically the client uses this information to render the item in the UI with an icon. */ - type?: CompletionItemType; - /** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added. - If missing the text is added at the location specified by the CompletionsRequest's 'column' attribute. - */ - start?: number; - /** This value determines how many characters are overwritten by the completion text. - If missing the value 0 is assumed which results in the completion text being inserted. - */ - length?: number; - } - - /** Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them. */ - export type CompletionItemType = 'method' | 'function' | 'constructor' | 'field' | 'variable' | 'class' | 'interface' | 'module' | 'property' | 'unit' | 'value' | 'enum' | 'keyword' | 'snippet' | 'text' | 'color' | 'file' | 'reference' | 'customcolor'; - - /** Names of checksum algorithms that may be supported by a debug adapter. */ - export type ChecksumAlgorithm = 'MD5' | 'SHA1' | 'SHA256' | 'timestamp'; - - /** The checksum of an item calculated by the specified algorithm. */ - export interface Checksum { - /** The algorithm used to calculate this checksum. */ - algorithm: ChecksumAlgorithm; - /** Value of the checksum. */ - checksum: string; - } - - /** Provides formatting information for a value. */ - export interface ValueFormat { - /** Display the value in hex. */ - hex?: boolean; - } - - /** Provides formatting information for a stack frame. */ - export interface StackFrameFormat extends ValueFormat { - /** Displays parameters for the stack frame. */ - parameters?: boolean; - /** Displays the types of parameters for the stack frame. */ - parameterTypes?: boolean; - /** Displays the names of parameters for the stack frame. */ - parameterNames?: boolean; - /** Displays the values of parameters for the stack frame. */ - parameterValues?: boolean; - /** Displays the line number of the stack frame. */ - line?: boolean; - /** Displays the module of the stack frame. */ - module?: boolean; - /** Includes all stack frames, including those the debug adapter might otherwise hide. */ - includeAll?: boolean; - } - - /** An ExceptionOptions assigns configuration options to a set of exceptions. */ - export interface ExceptionOptions { - /** A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected. By convention the first segment of the path is a category that is used to group exceptions in the UI. */ - path?: ExceptionPathSegment[]; - /** Condition when a thrown exception should result in a break. */ - breakMode: ExceptionBreakMode; - } - - /** This enumeration defines all possible conditions when a thrown exception should result in a break. - never: never breaks, - always: always breaks, - unhandled: breaks when exception unhandled, - userUnhandled: breaks if the exception is not handled by user code. - */ - export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled'; - - /** An ExceptionPathSegment represents a segment in a path that is used to match leafs or nodes in a tree of exceptions. If a segment consists of more than one name, it matches the names provided if 'negate' is false or missing or it matches anything except the names provided if 'negate' is true. */ - export interface ExceptionPathSegment { - /** If false or missing this segment matches the names provided, otherwise it matches anything except the names provided. */ - negate?: boolean; - /** Depending on the value of 'negate' the names that should match or not match. */ - names: string[]; - } - - /** Detailed information about an exception that has occurred. */ - export interface ExceptionDetails { - /** Message contained in the exception. */ - message?: string; - /** Short type name of the exception object. */ - typeName?: string; - /** Fully-qualified type name of the exception object. */ - fullTypeName?: string; - /** Optional expression that can be evaluated in the current scope to obtain the exception object. */ - evaluateName?: string; - /** Stack trace at the time the exception was thrown. */ - stackTrace?: string; - /** Details of the exception contained by this exception, if any. */ - innerException?: ExceptionDetails[]; - } - - /** Represents a single disassembled instruction. */ - export interface DisassembledInstruction { - /** The address of the instruction. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise. */ - address: string; - /** Optional raw bytes representing the instruction and its operands, in an implementation-defined format. */ - instructionBytes?: string; - /** Text representing the instruction and its operands, in an implementation-defined format. */ - instruction: string; - /** Name of the symbol that corresponds with the location of this instruction, if any. */ - symbol?: string; - /** Source location that corresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction. */ - location?: Source; - /** The line within the source location that corresponds to this instruction, if any. */ - line?: number; - /** The column within the line that corresponds to this instruction, if any. */ - column?: number; - /** The end line of the range that corresponds to this instruction, if any. */ - endLine?: number; - /** The end column of the range that corresponds to this instruction, if any. */ - endColumn?: number; - } -} - -//------------------------------------------------------------------------------------------------------------------------------ - -export class Message implements DebugProtocol.ProtocolMessage { - seq: number; - type: string; - - public constructor(type: string) { - this.seq = 0; - this.type = type; - } -} - -export class Response extends Message implements DebugProtocol.Response { - request_seq: number; - success: boolean; - command: string; - - public constructor(request: DebugProtocol.Request, message?: string) { - super('response'); - this.request_seq = request.seq; - this.command = request.command; - if (message) { - this.success = false; - (<any>this).message = message; - } else { - this.success = true; - } - } -} - -export class Event extends Message implements DebugProtocol.Event { - event: string; - - public constructor(event: string, body?: any) { - super('event'); - this.event = event; - if (body) { - (<any>this).body = body; - } - } -} - -//-------------------------------------------------------------------------------------------------------------------------------- - -export class ProtocolServer implements vscode.DebugAdapter { - - private close = new vscode.EventEmitter<void>(); - onClose: vscode.Event<void> = this.close.event; - - private error = new vscode.EventEmitter<Error>(); - onError: vscode.Event<Error> = this.error.event; - - private sendMessage = new vscode.EventEmitter<DebugProtocol.ProtocolMessage>(); - readonly onDidSendMessage: vscode.Event<DebugProtocol.ProtocolMessage> = this.sendMessage.event; - - private _sequence: number = 1; - private _pendingRequests = new Map<number, (response: DebugProtocol.Response) => void>(); - - - public handleMessage(message: DebugProtocol.ProtocolMessage): void { - this.dispatch(message); - } - - public dispose() { - } - - public sendEvent(event: DebugProtocol.Event): void { - this._send('event', event); - } - - public sendResponse(response: DebugProtocol.Response): void { - if (response.seq > 0) { - console.error(`attempt to send more than one response for command ${response.command}`); - } else { - this._send('response', response); - } - } - - public sendRequest(command: string, args: any, timeout: number, cb: (response: DebugProtocol.Response) => void): void { - - const request: any = { - command: command - }; - if (args && Object.keys(args).length > 0) { - request.arguments = args; - } - - this._send('request', request); - - if (cb) { - this._pendingRequests.set(request.seq, cb); - - const timer = setTimeout(() => { - clearTimeout(timer); - const clb = this._pendingRequests.get(request.seq); - if (clb) { - this._pendingRequests.delete(request.seq); - clb(new Response(request, 'timeout')); - } - }, timeout); - } - } - - // ---- protected ---------------------------------------------------------- - - protected dispatchRequest(_request: DebugProtocol.Request): void { - } - - // ---- private ------------------------------------------------------------ - - private dispatch(msg: DebugProtocol.ProtocolMessage) { - if (msg.type === 'request') { - this.dispatchRequest(<DebugProtocol.Request>msg); - } else if (msg.type === 'response') { - const response = <DebugProtocol.Response>msg; - const clb = this._pendingRequests.get(response.request_seq); - if (clb) { - this._pendingRequests.delete(response.request_seq); - clb(response); - } - } - } - - private _send(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void { - - message.type = typ; - message.seq = this._sequence++; - - this.sendMessage.fire(message); - } -} - -//------------------------------------------------------------------------------------------------------------------------------- - -export class Source implements DebugProtocol.Source { - name: string; - path?: string; - sourceReference: number; - - public constructor(name: string, path?: string, id: number = 0, origin?: string, data?: any) { - this.name = name; - this.path = path; - this.sourceReference = id; - if (origin) { - (<any>this).origin = origin; - } - if (data) { - (<any>this).adapterData = data; - } - } -} - -export class Scope implements DebugProtocol.Scope { - name: string; - variablesReference: number; - expensive: boolean; - - public constructor(name: string, reference: number, expensive: boolean = false) { - this.name = name; - this.variablesReference = reference; - this.expensive = expensive; - } -} - -export class StackFrame implements DebugProtocol.StackFrame { - id: number; - source?: Source; - line: number; - column: number; - name: string; - - public constructor(i: number, nm: string, src?: Source, ln: number = 0, col: number = 0) { - this.id = i; - this.source = src; - this.line = ln; - this.column = col; - this.name = nm; - } -} - -export class Thread implements DebugProtocol.Thread { - id: number; - name: string; - - public constructor(id: number, name: string) { - this.id = id; - if (name) { - this.name = name; - } else { - this.name = 'Thread #' + id; - } - } -} - -export class Variable implements DebugProtocol.Variable { - name: string; - value: string; - variablesReference: number; - - public constructor(name: string, value: string, ref: number = 0, indexedVariables?: number, namedVariables?: number) { - this.name = name; - this.value = value; - this.variablesReference = ref; - if (typeof namedVariables === 'number') { - (<DebugProtocol.Variable>this).namedVariables = namedVariables; - } - if (typeof indexedVariables === 'number') { - (<DebugProtocol.Variable>this).indexedVariables = indexedVariables; - } - } -} - -export class Breakpoint implements DebugProtocol.Breakpoint { - verified: boolean; - - public constructor(verified: boolean, line?: number, column?: number, source?: Source) { - this.verified = verified; - const e: DebugProtocol.Breakpoint = this; - if (typeof line === 'number') { - e.line = line; - } - if (typeof column === 'number') { - e.column = column; - } - if (source) { - e.source = source; - } - } -} - -export class Module implements DebugProtocol.Module { - id: number | string; - name: string; - - public constructor(id: number | string, name: string) { - this.id = id; - this.name = name; - } -} - -export class CompletionItem implements DebugProtocol.CompletionItem { - label: string; - start: number; - length: number; - - public constructor(label: string, start: number, length: number = 0) { - this.label = label; - this.start = start; - this.length = length; - } -} - -export class StoppedEvent extends Event implements DebugProtocol.StoppedEvent { - body: { - reason: string; - }; - - public constructor(reason: string, threadId?: number, exceptionText?: string) { - super('stopped'); - this.body = { - reason: reason - }; - if (typeof threadId === 'number') { - (this as DebugProtocol.StoppedEvent).body.threadId = threadId; - } - if (typeof exceptionText === 'string') { - (this as DebugProtocol.StoppedEvent).body.text = exceptionText; - } - } -} - -export class ContinuedEvent extends Event implements DebugProtocol.ContinuedEvent { - body: { - threadId: number; - }; - - public constructor(threadId: number, allThreadsContinued?: boolean) { - super('continued'); - this.body = { - threadId: threadId - }; - - if (typeof allThreadsContinued === 'boolean') { - (<DebugProtocol.ContinuedEvent>this).body.allThreadsContinued = allThreadsContinued; - } - } -} - -export class InitializedEvent extends Event implements DebugProtocol.InitializedEvent { - public constructor() { - super('initialized'); - } -} - -export class TerminatedEvent extends Event implements DebugProtocol.TerminatedEvent { - public constructor(restart?: any) { - super('terminated'); - if (typeof restart === 'boolean' || restart) { - const e: DebugProtocol.TerminatedEvent = this; - e.body = { - restart: restart - }; - } - } -} - -export class OutputEvent extends Event implements DebugProtocol.OutputEvent { - body: { - category: string, - output: string, - data?: any - }; - - public constructor(output: string, category: string = 'console', data?: any) { - super('output'); - this.body = { - category: category, - output: output - }; - if (data !== undefined) { - this.body.data = data; - } - } -} - -export class ThreadEvent extends Event implements DebugProtocol.ThreadEvent { - body: { - reason: string, - threadId: number - }; - - public constructor(reason: string, threadId: number) { - super('thread'); - this.body = { - reason: reason, - threadId: threadId - }; - } -} - -export class BreakpointEvent extends Event implements DebugProtocol.BreakpointEvent { - body: { - reason: string, - breakpoint: Breakpoint - }; - - public constructor(reason: string, breakpoint: Breakpoint) { - super('breakpoint'); - this.body = { - reason: reason, - breakpoint: breakpoint - }; - } -} - -export class ModuleEvent extends Event implements DebugProtocol.ModuleEvent { - body: { - reason: 'new' | 'changed' | 'removed', - module: Module - }; - - public constructor(reason: 'new' | 'changed' | 'removed', module: Module) { - super('module'); - this.body = { - reason: reason, - module: module - }; - } -} - -export class LoadedSourceEvent extends Event implements DebugProtocol.LoadedSourceEvent { - body: { - reason: 'new' | 'changed' | 'removed', - source: Source - }; - - public constructor(reason: 'new' | 'changed' | 'removed', source: Source) { - super('loadedSource'); - this.body = { - reason: reason, - source: source - }; - } -} - -export class CapabilitiesEvent extends Event implements DebugProtocol.CapabilitiesEvent { - body: { - capabilities: DebugProtocol.Capabilities - }; - - public constructor(capabilities: DebugProtocol.Capabilities) { - super('capabilities'); - this.body = { - capabilities: capabilities - }; - } -} - -export enum ErrorDestination { - User = 1, - Telemetry = 2 -} - -export class DebugSession extends ProtocolServer { - - private _debuggerLinesStartAt1: boolean; - private _debuggerColumnsStartAt1: boolean; - private _debuggerPathsAreURIs: boolean; - - private _clientLinesStartAt1: boolean; - private _clientColumnsStartAt1: boolean; - private _clientPathsAreURIs: boolean; - - protected _isServer: boolean; - - public constructor(obsolete_debuggerLinesAndColumnsStartAt1?: boolean, obsolete_isServer?: boolean) { - super(); - - const linesAndColumnsStartAt1 = typeof obsolete_debuggerLinesAndColumnsStartAt1 === 'boolean' ? obsolete_debuggerLinesAndColumnsStartAt1 : false; - this._debuggerLinesStartAt1 = linesAndColumnsStartAt1; - this._debuggerColumnsStartAt1 = linesAndColumnsStartAt1; - this._debuggerPathsAreURIs = false; - - this._clientLinesStartAt1 = true; - this._clientColumnsStartAt1 = true; - this._clientPathsAreURIs = false; - - this._isServer = typeof obsolete_isServer === 'boolean' ? obsolete_isServer : false; - - this.onClose(() => { - this.shutdown(); - }); - this.onError((_error) => { - this.shutdown(); - }); - } - - public setDebuggerPathFormat(format: string) { - this._debuggerPathsAreURIs = format !== 'path'; - } - - public setDebuggerLinesStartAt1(enable: boolean) { - this._debuggerLinesStartAt1 = enable; - } - - public setDebuggerColumnsStartAt1(enable: boolean) { - this._debuggerColumnsStartAt1 = enable; - } - - public setRunAsServer(enable: boolean) { - this._isServer = enable; - } - - public shutdown(): void { - if (this._isServer) { - // shutdown ignored in server mode - } else { - // TODO@AW - /* - // wait a bit before shutting down - setTimeout(() => { - process.exit(0); - }, 100); - */ - } - } - - protected sendErrorResponse(response: DebugProtocol.Response, codeOrMessage: number | DebugProtocol.Message, format?: string, variables?: any, dest: ErrorDestination = ErrorDestination.User): void { - - let msg: DebugProtocol.Message; - if (typeof codeOrMessage === 'number') { - msg = <DebugProtocol.Message>{ - id: <number>codeOrMessage, - format: format - }; - if (variables) { - msg.variables = variables; - } - if (dest & ErrorDestination.User) { - msg.showUser = true; - } - if (dest & ErrorDestination.Telemetry) { - msg.sendTelemetry = true; - } - } else { - msg = codeOrMessage; - } - - response.success = false; - response.message = DebugSession.formatPII(msg.format, true, msg.variables); - if (!response.body) { - response.body = {}; - } - response.body.error = msg; - - this.sendResponse(response); - } - - public runInTerminalRequest(args: DebugProtocol.RunInTerminalRequestArguments, timeout: number, cb: (response: DebugProtocol.Response) => void) { - this.sendRequest('runInTerminal', args, timeout, cb); - } - - protected dispatchRequest(request: DebugProtocol.Request): void { - - const response = new Response(request); - - try { - if (request.command === 'initialize') { - const args = <DebugProtocol.InitializeRequestArguments>request.arguments; - - if (typeof args.linesStartAt1 === 'boolean') { - this._clientLinesStartAt1 = args.linesStartAt1; - } - if (typeof args.columnsStartAt1 === 'boolean') { - this._clientColumnsStartAt1 = args.columnsStartAt1; - } - - if (args.pathFormat !== 'path') { - this.sendErrorResponse(response, 2018, 'debug adapter only supports native paths', null, ErrorDestination.Telemetry); - } else { - const initializeResponse = <DebugProtocol.InitializeResponse>response; - initializeResponse.body = {}; - this.initializeRequest(initializeResponse, args); - } - - } else if (request.command === 'launch') { - this.launchRequest(<DebugProtocol.LaunchResponse>response, request.arguments, request); - - } else if (request.command === 'attach') { - this.attachRequest(<DebugProtocol.AttachResponse>response, request.arguments, request); - - } else if (request.command === 'disconnect') { - this.disconnectRequest(<DebugProtocol.DisconnectResponse>response, request.arguments, request); - - } else if (request.command === 'terminate') { - this.terminateRequest(<DebugProtocol.TerminateResponse>response, request.arguments, request); - - } else if (request.command === 'restart') { - this.restartRequest(<DebugProtocol.RestartResponse>response, request.arguments, request); - - } else if (request.command === 'setBreakpoints') { - this.setBreakPointsRequest(<DebugProtocol.SetBreakpointsResponse>response, request.arguments, request); - - } else if (request.command === 'setFunctionBreakpoints') { - this.setFunctionBreakPointsRequest(<DebugProtocol.SetFunctionBreakpointsResponse>response, request.arguments, request); - - } else if (request.command === 'setExceptionBreakpoints') { - this.setExceptionBreakPointsRequest(<DebugProtocol.SetExceptionBreakpointsResponse>response, request.arguments, request); - - } else if (request.command === 'configurationDone') { - this.configurationDoneRequest(<DebugProtocol.ConfigurationDoneResponse>response, request.arguments, request); - - } else if (request.command === 'continue') { - this.continueRequest(<DebugProtocol.ContinueResponse>response, request.arguments, request); - - } else if (request.command === 'next') { - this.nextRequest(<DebugProtocol.NextResponse>response, request.arguments, request); - - } else if (request.command === 'stepIn') { - this.stepInRequest(<DebugProtocol.StepInResponse>response, request.arguments, request); - - } else if (request.command === 'stepOut') { - this.stepOutRequest(<DebugProtocol.StepOutResponse>response, request.arguments, request); - - } else if (request.command === 'stepBack') { - this.stepBackRequest(<DebugProtocol.StepBackResponse>response, request.arguments, request); - - } else if (request.command === 'reverseContinue') { - this.reverseContinueRequest(<DebugProtocol.ReverseContinueResponse>response, request.arguments, request); - - } else if (request.command === 'restartFrame') { - this.restartFrameRequest(<DebugProtocol.RestartFrameResponse>response, request.arguments, request); - - } else if (request.command === 'goto') { - this.gotoRequest(<DebugProtocol.GotoResponse>response, request.arguments, request); - - } else if (request.command === 'pause') { - this.pauseRequest(<DebugProtocol.PauseResponse>response, request.arguments, request); - - } else if (request.command === 'stackTrace') { - this.stackTraceRequest(<DebugProtocol.StackTraceResponse>response, request.arguments, request); - - } else if (request.command === 'scopes') { - this.scopesRequest(<DebugProtocol.ScopesResponse>response, request.arguments, request); - - } else if (request.command === 'variables') { - this.variablesRequest(<DebugProtocol.VariablesResponse>response, request.arguments, request); - - } else if (request.command === 'setVariable') { - this.setVariableRequest(<DebugProtocol.SetVariableResponse>response, request.arguments, request); - - } else if (request.command === 'setExpression') { - this.setExpressionRequest(<DebugProtocol.SetExpressionResponse>response, request.arguments, request); - - } else if (request.command === 'source') { - this.sourceRequest(<DebugProtocol.SourceResponse>response, request.arguments, request); - - } else if (request.command === 'threads') { - this.threadsRequest(<DebugProtocol.ThreadsResponse>response, request); - - } else if (request.command === 'terminateThreads') { - this.terminateThreadsRequest(<DebugProtocol.TerminateThreadsResponse>response, request.arguments, request); - - } else if (request.command === 'evaluate') { - this.evaluateRequest(<DebugProtocol.EvaluateResponse>response, request.arguments, request); - - } else if (request.command === 'stepInTargets') { - this.stepInTargetsRequest(<DebugProtocol.StepInTargetsResponse>response, request.arguments, request); - - } else if (request.command === 'gotoTargets') { - this.gotoTargetsRequest(<DebugProtocol.GotoTargetsResponse>response, request.arguments, request); - - } else if (request.command === 'completions') { - this.completionsRequest(<DebugProtocol.CompletionsResponse>response, request.arguments, request); - - } else if (request.command === 'exceptionInfo') { - this.exceptionInfoRequest(<DebugProtocol.ExceptionInfoResponse>response, request.arguments, request); - - } else if (request.command === 'loadedSources') { - this.loadedSourcesRequest(<DebugProtocol.LoadedSourcesResponse>response, request.arguments, request); - - } else if (request.command === 'dataBreakpointInfo') { - this.dataBreakpointInfoRequest(<DebugProtocol.DataBreakpointInfoResponse>response, request.arguments, request); - - } else if (request.command === 'setDataBreakpoints') { - this.setDataBreakpointsRequest(<DebugProtocol.SetDataBreakpointsResponse>response, request.arguments, request); - - } else if (request.command === 'readMemory') { - this.readMemoryRequest(<DebugProtocol.ReadMemoryResponse>response, request.arguments, request); - - } else if (request.command === 'disassemble') { - this.disassembleRequest(<DebugProtocol.DisassembleResponse>response, request.arguments, request); - - } else if (request.command === 'cancel') { - this.cancelRequest(<DebugProtocol.CancelResponse>response, request.arguments, request); - - } else if (request.command === 'breakpointLocations') { - this.breakpointLocationsRequest(<DebugProtocol.BreakpointLocationsResponse>response, request.arguments, request); - - } else { - this.customRequest(request.command, <DebugProtocol.Response>response, request.arguments, request); - } - } catch (e) { - this.sendErrorResponse(response, 1104, '{_stack}', { _exception: e.message, _stack: e.stack }, ErrorDestination.Telemetry); - } - } - - protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { - - response.body = response.body || {}; - - // This default debug adapter does not support conditional breakpoints. - response.body.supportsConditionalBreakpoints = false; - - // This default debug adapter does not support hit conditional breakpoints. - response.body.supportsHitConditionalBreakpoints = false; - - // This default debug adapter does not support function breakpoints. - response.body.supportsFunctionBreakpoints = false; - - // This default debug adapter implements the 'configurationDone' request. - response.body.supportsConfigurationDoneRequest = true; - - // This default debug adapter does not support hovers based on the 'evaluate' request. - response.body.supportsEvaluateForHovers = false; - - // This default debug adapter does not support the 'stepBack' request. - response.body.supportsStepBack = false; - - // This default debug adapter does not support the 'setVariable' request. - response.body.supportsSetVariable = false; - - // This default debug adapter does not support the 'restartFrame' request. - response.body.supportsRestartFrame = false; - - // This default debug adapter does not support the 'stepInTargets' request. - response.body.supportsStepInTargetsRequest = false; - - // This default debug adapter does not support the 'gotoTargets' request. - response.body.supportsGotoTargetsRequest = false; - - // This default debug adapter does not support the 'completions' request. - response.body.supportsCompletionsRequest = false; - - // This default debug adapter does not support the 'restart' request. - response.body.supportsRestartRequest = false; - - // This default debug adapter does not support the 'exceptionOptions' attribute on the 'setExceptionBreakpoints' request. - response.body.supportsExceptionOptions = false; - - // This default debug adapter does not support the 'format' attribute on the 'variables', 'evaluate', and 'stackTrace' request. - response.body.supportsValueFormattingOptions = false; - - // This debug adapter does not support the 'exceptionInfo' request. - response.body.supportsExceptionInfoRequest = false; - - // This debug adapter does not support the 'TerminateDebuggee' attribute on the 'disconnect' request. - response.body.supportTerminateDebuggee = false; - - // This debug adapter does not support delayed loading of stack frames. - response.body.supportsDelayedStackTraceLoading = false; - - // This debug adapter does not support the 'loadedSources' request. - response.body.supportsLoadedSourcesRequest = false; - - // This debug adapter does not support the 'logMessage' attribute of the SourceBreakpoint. - response.body.supportsLogPoints = false; - - // This debug adapter does not support the 'terminateThreads' request. - response.body.supportsTerminateThreadsRequest = false; - - // This debug adapter does not support the 'setExpression' request. - response.body.supportsSetExpression = false; - - // This debug adapter does not support the 'terminate' request. - response.body.supportsTerminateRequest = false; - - // This debug adapter does not support data breakpoints. - response.body.supportsDataBreakpoints = false; - - /** This debug adapter does not support the 'readMemory' request. */ - response.body.supportsReadMemoryRequest = false; - - /** The debug adapter does not support the 'disassemble' request. */ - response.body.supportsDisassembleRequest = false; - - /** The debug adapter does not support the 'cancel' request. */ - response.body.supportsCancelRequest = false; - - /** The debug adapter does not support the 'breakpointLocations' request. */ - response.body.supportsBreakpointLocationsRequest = false; - - this.sendResponse(response); - } - - protected disconnectRequest(response: DebugProtocol.DisconnectResponse, _args: DebugProtocol.DisconnectArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - this.shutdown(); - } - - protected launchRequest(response: DebugProtocol.LaunchResponse, _args: DebugProtocol.LaunchRequestArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected attachRequest(response: DebugProtocol.AttachResponse, _args: DebugProtocol.AttachRequestArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected terminateRequest(response: DebugProtocol.TerminateResponse, _args: DebugProtocol.TerminateArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected restartRequest(response: DebugProtocol.RestartResponse, _args: DebugProtocol.RestartArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, _args: DebugProtocol.SetBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse, _args: DebugProtocol.SetFunctionBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setExceptionBreakPointsRequest(response: DebugProtocol.SetExceptionBreakpointsResponse, _args: DebugProtocol.SetExceptionBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, _args: DebugProtocol.ConfigurationDoneArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected continueRequest(response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected nextRequest(response: DebugProtocol.NextResponse, _args: DebugProtocol.NextArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepInRequest(response: DebugProtocol.StepInResponse, _args: DebugProtocol.StepInArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepOutRequest(response: DebugProtocol.StepOutResponse, _args: DebugProtocol.StepOutArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepBackRequest(response: DebugProtocol.StepBackResponse, _args: DebugProtocol.StepBackArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, _args: DebugProtocol.ReverseContinueArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected restartFrameRequest(response: DebugProtocol.RestartFrameResponse, _args: DebugProtocol.RestartFrameArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected gotoRequest(response: DebugProtocol.GotoResponse, _args: DebugProtocol.GotoArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected pauseRequest(response: DebugProtocol.PauseResponse, _args: DebugProtocol.PauseArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected sourceRequest(response: DebugProtocol.SourceResponse, _args: DebugProtocol.SourceArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected threadsRequest(response: DebugProtocol.ThreadsResponse, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected terminateThreadsRequest(response: DebugProtocol.TerminateThreadsResponse, _args: DebugProtocol.TerminateThreadsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, _args: DebugProtocol.StackTraceArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected scopesRequest(response: DebugProtocol.ScopesResponse, _args: DebugProtocol.ScopesArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected variablesRequest(response: DebugProtocol.VariablesResponse, _args: DebugProtocol.VariablesArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setVariableRequest(response: DebugProtocol.SetVariableResponse, _args: DebugProtocol.SetVariableArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setExpressionRequest(response: DebugProtocol.SetExpressionResponse, _args: DebugProtocol.SetExpressionArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected evaluateRequest(response: DebugProtocol.EvaluateResponse, _args: DebugProtocol.EvaluateArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected stepInTargetsRequest(response: DebugProtocol.StepInTargetsResponse, _args: DebugProtocol.StepInTargetsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected gotoTargetsRequest(response: DebugProtocol.GotoTargetsResponse, _args: DebugProtocol.GotoTargetsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected completionsRequest(response: DebugProtocol.CompletionsResponse, _args: DebugProtocol.CompletionsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected exceptionInfoRequest(response: DebugProtocol.ExceptionInfoResponse, _args: DebugProtocol.ExceptionInfoArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected loadedSourcesRequest(response: DebugProtocol.LoadedSourcesResponse, _args: DebugProtocol.LoadedSourcesArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, _args: DebugProtocol.DataBreakpointInfoArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, _args: DebugProtocol.SetDataBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected readMemoryRequest(response: DebugProtocol.ReadMemoryResponse, _args: DebugProtocol.ReadMemoryArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected disassembleRequest(response: DebugProtocol.DisassembleResponse, _args: DebugProtocol.DisassembleArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected cancelRequest(response: DebugProtocol.CancelResponse, _args: DebugProtocol.CancelArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, _args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response); - } - - /** - * Override this hook to implement custom requests. - */ - protected customRequest(_command: string, response: DebugProtocol.Response, _args: any, _request?: DebugProtocol.Request): void { - this.sendErrorResponse(response, 1014, 'unrecognized request', null, ErrorDestination.Telemetry); - } - - //---- protected ------------------------------------------------------------------------------------------------- - - protected convertClientLineToDebugger(line: number): number { - if (this._debuggerLinesStartAt1) { - return this._clientLinesStartAt1 ? line : line + 1; - } - return this._clientLinesStartAt1 ? line - 1 : line; - } - - protected convertDebuggerLineToClient(line: number): number { - if (this._debuggerLinesStartAt1) { - return this._clientLinesStartAt1 ? line : line - 1; - } - return this._clientLinesStartAt1 ? line + 1 : line; - } - - protected convertClientColumnToDebugger(column: number): number { - if (this._debuggerColumnsStartAt1) { - return this._clientColumnsStartAt1 ? column : column + 1; - } - return this._clientColumnsStartAt1 ? column - 1 : column; - } - - protected convertDebuggerColumnToClient(column: number): number { - if (this._debuggerColumnsStartAt1) { - return this._clientColumnsStartAt1 ? column : column - 1; - } - return this._clientColumnsStartAt1 ? column + 1 : column; - } - - protected convertClientPathToDebugger(clientPath: string): string { - if (this._clientPathsAreURIs !== this._debuggerPathsAreURIs) { - if (this._clientPathsAreURIs) { - return DebugSession.uri2path(clientPath); - } else { - return DebugSession.path2uri(clientPath); - } - } - return clientPath; - } - - protected convertDebuggerPathToClient(debuggerPath: string): string { - if (this._debuggerPathsAreURIs !== this._clientPathsAreURIs) { - if (this._debuggerPathsAreURIs) { - return DebugSession.uri2path(debuggerPath); - } else { - return DebugSession.path2uri(debuggerPath); - } - } - return debuggerPath; - } - - //---- private ------------------------------------------------------------------------------- - - private static path2uri(path: string): string { - - path = encodeURI(path); - - let uri = new URL(`file:`); // ignore 'path' for now - uri.pathname = path; // now use 'path' to get the correct percent encoding (see https://url.spec.whatwg.org) - return uri.toString(); - } - - private static uri2path(sourceUri: string): string { - - let uri = new URL(sourceUri); - let s = decodeURIComponent(uri.pathname); - return s; - } - - private static _formatPIIRegexp = /{([^}]+)}/g; - - /* - * If argument starts with '_' it is OK to send its value to telemetry. - */ - private static formatPII(format: string, excludePII: boolean, args?: { [key: string]: string }): string { - return format.replace(DebugSession._formatPIIRegexp, function (match, paramName) { - if (excludePII && paramName.length > 0 && paramName[0] !== '_') { - return match; - } - return args && args[paramName] && args.hasOwnProperty(paramName) ? - args[paramName] : - match; - }); - } -} - -//--------------------------------------------------------------------------- - -export class Handles<T> { - - private START_HANDLE = 1000; - - private _nextHandle: number; - private _handleMap = new Map<number, T>(); - - public constructor(startHandle?: number) { - this._nextHandle = typeof startHandle === 'number' ? startHandle : this.START_HANDLE; - } - - public reset(): void { - this._nextHandle = this.START_HANDLE; - this._handleMap = new Map<number, T>(); - } - - public create(value: T): number { - const handle = this._nextHandle++; - this._handleMap.set(handle, value); - return handle; - } - - public get(handle: number, dflt?: T): T | undefined { - return this._handleMap.get(handle) || dflt; - } -} - -//--------------------------------------------------------------------------- - -class MockConfigurationProvider implements vscode.DebugConfigurationProvider { - - /** - * Massage a debug configuration just before a debug session is being launched, - * e.g. add all missing attributes to the debug configuration. - */ - resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { - - // if launch.json is missing or empty - if (!config.type && !config.request && !config.name) { - const editor = vscode.window.activeTextEditor; - if (editor && editor.document.languageId === 'markdown') { - config.type = 'mock'; - config.name = 'Launch'; - config.request = 'launch'; - config.program = '${file}'; - config.stopOnEntry = true; - } - } - - if (!config.program) { - return vscode.window.showInformationMessage('Cannot find a program to debug').then(_ => { - return undefined; // abort launch - }); - } - - return config; - } -} - -export class MockDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { - - constructor(private memfs: MemFS) { - } - - createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult<vscode.DebugAdapterDescriptor> { - return <any>new vscode.DebugAdapterInlineImplementation(new MockDebugSession(this.memfs)); - } -} - -function basename(path: string): string { - const pos = path.lastIndexOf('/'); - if (pos >= 0) { - return path.substring(pos + 1); - } - return path; -} - -function timeout(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -/** - * This interface describes the mock-debug specific launch attributes - * (which are not part of the Debug Adapter Protocol). - * The schema for these attributes lives in the package.json of the mock-debug extension. - * The interface should always match this schema. - */ -interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { - /** An absolute path to the "program" to debug. */ - program: string; - /** Automatically stop target after launch. If not specified, target does not stop. */ - stopOnEntry?: boolean; - /** enable logging the Debug Adapter Protocol */ - trace?: boolean; -} - -export class MockDebugSession extends DebugSession { - - // we don't support multiple threads, so we can use a hardcoded ID for the default thread - private static THREAD_ID = 1; - - // a Mock runtime (or debugger) - private _runtime: MockRuntime; - - private _variableHandles = new Handles<string>(); - - //private _configurationDone = new Subject(); - - private promiseResolve?: () => void; - private _configurationDone = new Promise<void>((r, _e) => { - this.promiseResolve = r; - setTimeout(r, 1000); - }); - - private _cancelationTokens = new Map<number, boolean>(); - private _isLongrunning = new Map<number, boolean>(); - - /** - * Creates a new debug adapter that is used for one debug session. - * We configure the default implementation of a debug adapter here. - */ - public constructor(memfs: MemFS) { - - super(); - - // this debugger uses zero-based lines and columns - this.setDebuggerLinesStartAt1(false); - this.setDebuggerColumnsStartAt1(false); - - this._runtime = new MockRuntime(memfs); - - // setup event handlers - this._runtime.onStopOnEntry(() => { - this.sendEvent(new StoppedEvent('entry', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnStep(() => { - this.sendEvent(new StoppedEvent('step', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnBreakpoint(() => { - this.sendEvent(new StoppedEvent('breakpoint', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnDataBreakpoint(() => { - this.sendEvent(new StoppedEvent('data breakpoint', MockDebugSession.THREAD_ID)); - }); - this._runtime.onStopOnException(() => { - this.sendEvent(new StoppedEvent('exception', MockDebugSession.THREAD_ID)); - }); - this._runtime.onBreakpointValidated((bp: MockBreakpoint) => { - this.sendEvent(new BreakpointEvent('changed', <DebugProtocol.Breakpoint>{ verified: bp.verified, id: bp.id })); - }); - this._runtime.onOutput(oe => { - const e: DebugProtocol.OutputEvent = new OutputEvent(`${oe.text}\n`); - e.body.source = this.createSource(oe.filePath); - e.body.line = this.convertDebuggerLineToClient(oe.line); - e.body.column = this.convertDebuggerColumnToClient(oe.column); - this.sendEvent(e); - }); - this._runtime.onEnd(() => { - this.sendEvent(new TerminatedEvent()); - }); - } - - /** - * The 'initialize' request is the first request called by the frontend - * to interrogate the features the debug adapter provides. - */ - protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { - - // build and return the capabilities of this debug adapter: - response.body = response.body || {}; - - // the adapter implements the configurationDoneRequest. - response.body.supportsConfigurationDoneRequest = true; - - // make VS Code to use 'evaluate' when hovering over source - response.body.supportsEvaluateForHovers = true; - - // make VS Code to show a 'step back' button - response.body.supportsStepBack = true; - - // make VS Code to support data breakpoints - response.body.supportsDataBreakpoints = true; - - // make VS Code to support completion in REPL - response.body.supportsCompletionsRequest = true; - response.body.completionTriggerCharacters = ['.', '[']; - - // make VS Code to send cancelRequests - response.body.supportsCancelRequest = true; - - // make VS Code send the breakpointLocations request - response.body.supportsBreakpointLocationsRequest = true; - - this.sendResponse(response); - - // since this debug adapter can accept configuration requests like 'setBreakpoint' at any time, - // we request them early by sending an 'initializeRequest' to the frontend. - // The frontend will end the configuration sequence by calling 'configurationDone' request. - this.sendEvent(new InitializedEvent()); - } - - /** - * Called at the end of the configuration sequence. - * Indicates that all breakpoints etc. have been sent to the DA and that the 'launch' can start. - */ - protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments): void { - super.configurationDoneRequest(response, args); - - // notify the launchRequest that configuration has finished - //this._configurationDone.notify(); - if (this.promiseResolve) { - this.promiseResolve(); - } - } - - protected async launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments) { - - // make sure to 'Stop' the buffered logging if 'trace' is not set - //logger.setup(args.trace ? Logger.LogLevel.Verbose : Logger.LogLevel.Stop, false); - - // wait until configuration has finished (and configurationDoneRequest has been called) - await this._configurationDone; - - // start the program in the runtime - this._runtime.start(`memfs:${args.program}`, !!args.stopOnEntry); - - this.sendResponse(response); - } - - protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void { - - const path = <string>args.source.path; - const clientLines = args.lines || []; - - // clear all breakpoints for this file - this._runtime.clearBreakpoints(path); - - // set and verify breakpoint locations - const actualBreakpoints = clientLines.map(l => { - let { verified, line, id } = this._runtime.setBreakPoint(path, this.convertClientLineToDebugger(l)); - const bp = <DebugProtocol.Breakpoint>new Breakpoint(verified, this.convertDebuggerLineToClient(line)); - bp.id = id; - return bp; - }); - - // send back the actual breakpoint positions - response.body = { - breakpoints: actualBreakpoints - }; - this.sendResponse(response); - } - - protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { - - if (args.source.path) { - const bps = this._runtime.getBreakpoints(args.source.path, this.convertClientLineToDebugger(args.line)); - response.body = { - breakpoints: bps.map(col => { - return { - line: args.line, - column: this.convertDebuggerColumnToClient(col) - }; - }) - }; - } else { - response.body = { - breakpoints: [] - }; - } - this.sendResponse(response); - } - - protected threadsRequest(response: DebugProtocol.ThreadsResponse): void { - - // runtime supports no threads so just return a default thread. - response.body = { - threads: [ - new Thread(MockDebugSession.THREAD_ID, 'thread 1') - ] - }; - this.sendResponse(response); - } - - protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void { - - const startFrame = typeof args.startFrame === 'number' ? args.startFrame : 0; - const maxLevels = typeof args.levels === 'number' ? args.levels : 1000; - const endFrame = startFrame + maxLevels; - - const stk = this._runtime.stack(startFrame, endFrame); - - response.body = { - stackFrames: stk.frames.map(f => new StackFrame(f.index, f.name, this.createSource(f.file), this.convertDebuggerLineToClient(f.line))), - totalFrames: stk.count - }; - this.sendResponse(response); - } - - protected scopesRequest(response: DebugProtocol.ScopesResponse, _args: DebugProtocol.ScopesArguments): void { - - response.body = { - scopes: [ - new Scope('Local', this._variableHandles.create('local'), false), - new Scope('Global', this._variableHandles.create('global'), true) - ] - }; - this.sendResponse(response); - } - - protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request) { - - const variables: DebugProtocol.Variable[] = []; - - if (this._isLongrunning.get(args.variablesReference)) { - // long running - - if (request) { - this._cancelationTokens.set(request.seq, false); - } - - for (let i = 0; i < 100; i++) { - await timeout(1000); - variables.push({ - name: `i_${i}`, - type: 'integer', - value: `${i}`, - variablesReference: 0 - }); - if (request && this._cancelationTokens.get(request.seq)) { - break; - } - } - - if (request) { - this._cancelationTokens.delete(request.seq); - } - - } else { - - const id = this._variableHandles.get(args.variablesReference); - - if (id) { - variables.push({ - name: id + '_i', - type: 'integer', - value: '123', - variablesReference: 0 - }); - variables.push({ - name: id + '_f', - type: 'float', - value: '3.14', - variablesReference: 0 - }); - variables.push({ - name: id + '_s', - type: 'string', - value: 'hello world', - variablesReference: 0 - }); - variables.push({ - name: id + '_o', - type: 'object', - value: 'Object', - variablesReference: this._variableHandles.create(id + '_o') - }); - - // cancelation support for long running requests - const nm = id + '_long_running'; - const ref = this._variableHandles.create(id + '_lr'); - variables.push({ - name: nm, - type: 'object', - value: 'Object', - variablesReference: ref - }); - this._isLongrunning.set(ref, true); - } - } - - response.body = { - variables: variables - }; - this.sendResponse(response); - } - - protected continueRequest(response: DebugProtocol.ContinueResponse, _args: DebugProtocol.ContinueArguments): void { - this._runtime.continue(); - this.sendResponse(response); - } - - protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, _args: DebugProtocol.ReverseContinueArguments): void { - this._runtime.continue(true); - this.sendResponse(response); - } - - protected nextRequest(response: DebugProtocol.NextResponse, _args: DebugProtocol.NextArguments): void { - this._runtime.step(); - this.sendResponse(response); - } - - protected stepBackRequest(response: DebugProtocol.StepBackResponse, _args: DebugProtocol.StepBackArguments): void { - this._runtime.step(true); - this.sendResponse(response); - } - - protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void { - - let reply: string | undefined = undefined; - - if (args.context === 'repl') { - // 'evaluate' supports to create and delete breakpoints from the 'repl': - const matches = /new +([0-9]+)/.exec(args.expression); - if (matches && matches.length === 2) { - if (this._runtime.sourceFile) { - const mbp = this._runtime.setBreakPoint(this._runtime.sourceFile, this.convertClientLineToDebugger(parseInt(matches[1]))); - const bp = <DebugProtocol.Breakpoint>new Breakpoint(mbp.verified, this.convertDebuggerLineToClient(mbp.line), undefined, this.createSource(this._runtime.sourceFile)); - bp.id = mbp.id; - this.sendEvent(new BreakpointEvent('new', bp)); - reply = `breakpoint created`; - } - } else { - const matches = /del +([0-9]+)/.exec(args.expression); - if (matches && matches.length === 2) { - const mbp = this._runtime.sourceFile ? this._runtime.clearBreakPoint(this._runtime.sourceFile, this.convertClientLineToDebugger(parseInt(matches[1]))) : undefined; - if (mbp) { - const bp = <DebugProtocol.Breakpoint>new Breakpoint(false); - bp.id = mbp.id; - this.sendEvent(new BreakpointEvent('removed', bp)); - reply = `breakpoint deleted`; - } - } - } - } - - response.body = { - result: reply ? reply : `evaluate(context: '${args.context}', '${args.expression}')`, - variablesReference: 0 - }; - this.sendResponse(response); - } - - protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, args: DebugProtocol.DataBreakpointInfoArguments): void { - - response.body = { - dataId: null, - description: 'cannot break on data access', - accessTypes: undefined, - canPersist: false - }; - - if (args.variablesReference && args.name) { - const id = this._variableHandles.get(args.variablesReference); - if (id && id.startsWith('global_')) { - response.body.dataId = args.name; - response.body.description = args.name; - response.body.accessTypes = ['read']; - response.body.canPersist = false; - } - } - - this.sendResponse(response); - } - - protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, args: DebugProtocol.SetDataBreakpointsArguments): void { - - // clear all data breakpoints - this._runtime.clearAllDataBreakpoints(); - - response.body = { - breakpoints: [] - }; - - for (let dbp of args.breakpoints) { - // assume that id is the "address" to break on - const ok = this._runtime.setDataBreakpoint(dbp.dataId); - response.body.breakpoints.push({ - verified: ok - }); - } - - this.sendResponse(response); - } - - protected completionsRequest(response: DebugProtocol.CompletionsResponse, _args: DebugProtocol.CompletionsArguments): void { - - response.body = { - targets: [ - { - label: 'item 10', - sortText: '10' - }, - { - label: 'item 1', - sortText: '01' - }, - { - label: 'item 2', - sortText: '02' - } - ] - }; - this.sendResponse(response); - } - - protected cancelRequest(_response: DebugProtocol.CancelResponse, args: DebugProtocol.CancelArguments) { - if (args.requestId) { - this._cancelationTokens.set(args.requestId, true); - } - } - - //---- helpers - - private createSource(filePath: string): Source { - return new Source(basename(filePath), this.convertDebuggerPathToClient(filePath), undefined, undefined, 'mock-adapter-data'); - } -} - -//------------------------------------------------------------------------------------------------------------------------------------------ - - -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -export interface MockBreakpoint { - id: number; - line: number; - verified: boolean; -} - -export interface MockOutputEvent { - text: string; - filePath: string; - line: number; - column: number; -} - -/** - * A Mock runtime with minimal debugger functionality. - */ -export class MockRuntime { - - private stopOnEntry = new vscode.EventEmitter<void>(); - onStopOnEntry: vscode.Event<void> = this.stopOnEntry.event; - - private stopOnStep = new vscode.EventEmitter<void>(); - onStopOnStep: vscode.Event<void> = this.stopOnStep.event; - - private stopOnBreakpoint = new vscode.EventEmitter<void>(); - onStopOnBreakpoint: vscode.Event<void> = this.stopOnBreakpoint.event; - - private stopOnDataBreakpoint = new vscode.EventEmitter<void>(); - onStopOnDataBreakpoint: vscode.Event<void> = this.stopOnDataBreakpoint.event; - - private stopOnException = new vscode.EventEmitter<void>(); - onStopOnException: vscode.Event<void> = this.stopOnException.event; - - private breakpointValidated = new vscode.EventEmitter<MockBreakpoint>(); - onBreakpointValidated: vscode.Event<MockBreakpoint> = this.breakpointValidated.event; - - private output = new vscode.EventEmitter<MockOutputEvent>(); - onOutput: vscode.Event<MockOutputEvent> = this.output.event; - - private end = new vscode.EventEmitter<void>(); - onEnd: vscode.Event<void> = this.end.event; - - - // the initial (and one and only) file we are 'debugging' - private _sourceFile?: string; - public get sourceFile() { - return this._sourceFile; - } - - // the contents (= lines) of the one and only file - private _sourceLines: string[] = []; - - // This is the next line that will be 'executed' - private _currentLine = 0; - - // maps from sourceFile to array of Mock breakpoints - private _breakPoints = new Map<string, MockBreakpoint[]>(); - - // since we want to send breakpoint events, we will assign an id to every event - // so that the frontend can match events with breakpoints. - private _breakpointId = 1; - - private _breakAddresses = new Set<string>(); - - constructor(private memfs: MemFS) { - } - - /** - * Start executing the given program. - */ - public start(program: string, stopOnEntry: boolean) { - - this.loadSource(program); - this._currentLine = -1; - - if (this._sourceFile) { - this.verifyBreakpoints(this._sourceFile); - } - - if (stopOnEntry) { - // we step once - this.step(false, this.stopOnEntry); - } else { - // we just start to run until we hit a breakpoint or an exception - this.continue(); - } - } - - /** - * Continue execution to the end/beginning. - */ - public continue(reverse = false) { - this.run(reverse, undefined); - } - - /** - * Step to the next/previous non empty line. - */ - public step(reverse = false, event = this.stopOnStep) { - this.run(reverse, event); - } - - /** - * Returns a fake 'stacktrace' where every 'stackframe' is a word from the current line. - */ - public stack(startFrame: number, endFrame: number): { frames: any[], count: number } { - - const words = this._sourceLines[this._currentLine].trim().split(/\s+/); - - const frames = new Array<any>(); - // every word of the current line becomes a stack frame. - for (let i = startFrame; i < Math.min(endFrame, words.length); i++) { - const name = words[i]; // use a word of the line as the stackframe name - frames.push({ - index: i, - name: `${name}(${i})`, - file: this._sourceFile, - line: this._currentLine - }); - } - return { - frames: frames, - count: words.length - }; - } - - public getBreakpoints(_path: string, line: number): number[] { - - const l = this._sourceLines[line]; - - let sawSpace = true; - const bps: number[] = []; - for (let i = 0; i < l.length; i++) { - if (l[i] !== ' ') { - if (sawSpace) { - bps.push(i); - sawSpace = false; - } - } else { - sawSpace = true; - } - } - - return bps; - } - - /* - * Set breakpoint in file with given line. - */ - public setBreakPoint(path: string, line: number): MockBreakpoint { - - const bp = <MockBreakpoint>{ verified: false, line, id: this._breakpointId++ }; - let bps = this._breakPoints.get(path); - if (!bps) { - bps = new Array<MockBreakpoint>(); - this._breakPoints.set(path, bps); - } - bps.push(bp); - - this.verifyBreakpoints(path); - - return bp; - } - - /* - * Clear breakpoint in file with given line. - */ - public clearBreakPoint(path: string, line: number): MockBreakpoint | undefined { - let bps = this._breakPoints.get(path); - if (bps) { - const index = bps.findIndex(bp => bp.line === line); - if (index >= 0) { - const bp = bps[index]; - bps.splice(index, 1); - return bp; - } - } - return undefined; - } - - /* - * Clear all breakpoints for file. - */ - public clearBreakpoints(path: string): void { - this._breakPoints.delete(path); - } - - /* - * Set data breakpoint. - */ - public setDataBreakpoint(address: string): boolean { - if (address) { - this._breakAddresses.add(address); - return true; - } - return false; - } - - /* - * Clear all data breakpoints. - */ - public clearAllDataBreakpoints(): void { - this._breakAddresses.clear(); - } - - // private methods - - private loadSource(file: string) { - if (this._sourceFile !== file) { - this._sourceFile = file; - - const _textDecoder = new TextDecoder(); - - const uri = vscode.Uri.parse(file); - const content = _textDecoder.decode(this.memfs.readFile(uri)); - this._sourceLines = content.split('\n'); - - //this._sourceLines = readFileSync(this._sourceFile).toString().split('\n'); - } - } - - /** - * Run through the file. - * If stepEvent is specified only run a single step and emit the stepEvent. - */ - private run(reverse = false, stepEvent?: vscode.EventEmitter<void>): void { - if (reverse) { - for (let ln = this._currentLine - 1; ln >= 0; ln--) { - if (this.fireEventsForLine(ln, stepEvent)) { - this._currentLine = ln; - return; - } - } - // no more lines: stop at first line - this._currentLine = 0; - this.stopOnEntry.fire(); - } else { - for (let ln = this._currentLine + 1; ln < this._sourceLines.length; ln++) { - if (this.fireEventsForLine(ln, stepEvent)) { - this._currentLine = ln; - return; - } - } - // no more lines: run to end - this.end.fire(); - } - } - - private verifyBreakpoints(path: string): void { - let bps = this._breakPoints.get(path); - if (bps) { - this.loadSource(path); - bps.forEach(bp => { - if (!bp.verified && bp.line < this._sourceLines.length) { - const srcLine = this._sourceLines[bp.line].trim(); - - // if a line is empty or starts with '+' we don't allow to set a breakpoint but move the breakpoint down - if (srcLine.length === 0 || srcLine.indexOf('+') === 0) { - bp.line++; - } - // if a line starts with '-' we don't allow to set a breakpoint but move the breakpoint up - if (srcLine.indexOf('-') === 0) { - bp.line--; - } - // don't set 'verified' to true if the line contains the word 'lazy' - // in this case the breakpoint will be verified 'lazy' after hitting it once. - if (srcLine.indexOf('lazy') < 0) { - bp.verified = true; - this.breakpointValidated.fire(bp); - } - } - }); - } - } - - /** - * Fire events if line has a breakpoint or the word 'exception' is found. - * Returns true is execution needs to stop. - */ - private fireEventsForLine(ln: number, stepEvent?: vscode.EventEmitter<void>): boolean { - - const line = this._sourceLines[ln].trim(); - - // if 'log(...)' found in source -> send argument to debug console - const matches = /log\((.*)\)/.exec(line); - if (matches && matches.length === 2) { - if (this._sourceFile) { - this.output.fire({ text: matches[1], filePath: this._sourceFile, line: ln, column: matches.index }); - } - } - - // if a word in a line matches a data breakpoint, fire a 'dataBreakpoint' event - const words = line.split(' '); - for (let word of words) { - if (this._breakAddresses.has(word)) { - this.stopOnDataBreakpoint.fire(); - return true; - } - } - - // if word 'exception' found in source -> throw exception - if (line.indexOf('exception') >= 0) { - this.stopOnException.fire(); - return true; - } - - // is there a breakpoint? - const breakpoints = this._sourceFile ? this._breakPoints.get(this._sourceFile) : undefined; - if (breakpoints) { - const bps = breakpoints.filter(bp => bp.line === ln); - if (bps.length > 0) { - - // send 'stopped' event - this.stopOnBreakpoint.fire(); - - // the following shows the use of 'breakpoint' events to update properties of a breakpoint in the UI - // if breakpoint is not yet verified, verify it now and send a 'breakpoint' update event - if (!bps[0].verified) { - bps[0].verified = true; - this.breakpointValidated.fire(bps[0]); - } - return true; - } - } - - // non-empty line - if (stepEvent && line.length > 0) { - stepEvent.fire(); - return true; - } - - // nothing interesting found -> continue - return false; - } +export function activate(_context: vscode.ExtensionContext) { + // Set context as a global as some tests depend on it + (global as any).testExtensionContext = _context; } diff --git a/extensions/vscode-api-tests/src/memfs.ts b/extensions/vscode-api-tests/src/memfs.ts index 2b4a5f751ab..1b4b1e5adf7 100644 --- a/extensions/vscode-api-tests/src/memfs.ts +++ b/extensions/vscode-api-tests/src/memfs.ts @@ -48,9 +48,12 @@ class Directory implements vscode.FileStat { export type Entry = File | Directory; -export class MemFS implements vscode.FileSystemProvider { +export class TestFS implements vscode.FileSystemProvider { - readonly scheme = 'fake-fs'; + constructor( + readonly scheme: string, + readonly isCaseSensitive: boolean + ) { } readonly root = new Directory(''); @@ -161,12 +164,22 @@ export class MemFS implements vscode.FileSystemProvider { let parts = uri.path.split('/'); let entry: Entry = this.root; for (const part of parts) { + const partLow = part.toLowerCase(); if (!part) { continue; } let child: Entry | undefined; if (entry instanceof Directory) { - child = entry.entries.get(part); + if (this.isCaseSensitive) { + child = entry.entries.get(part); + } else { + for (let [key, value] of entry.entries) { + if (key.toLowerCase() === partLow) { + child = value; + break; + } + } + } } if (!child) { if (!silent) { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts index 739ce386371..1d62a12c170 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { join } from 'path'; import { commands, workspace, window, Uri, Range, Position, ViewColumn } from 'vscode'; -suite('commands namespace tests', () => { +suite('vscode API - commands', () => { test('getCommands', function (done) { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts index ffd8b53e06c..0b6f13fb01f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts @@ -7,7 +7,7 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; -suite('Configuration tests', () => { +suite('vscode API - configuration', () => { test('configurations, language defaults', function () { const defaultLanguageSettings = vscode.workspace.getConfiguration().get('[abcLang]'); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts new file mode 100644 index 00000000000..ec976ff5334 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { debug, workspace, Disposable, commands, window } from 'vscode'; +import { disposeAll } from '../utils'; +import { basename } from 'path'; + +suite('vscode API - debug', function () { + + test('breakpoints', async function () { + assert.equal(debug.breakpoints.length, 0); + let onDidChangeBreakpointsCounter = 0; + const toDispose: Disposable[] = []; + + toDispose.push(debug.onDidChangeBreakpoints(() => { + onDidChangeBreakpointsCounter++; + })); + + debug.addBreakpoints([{ id: '1', enabled: true }, { id: '2', enabled: false, condition: '2 < 5' }]); + assert.equal(onDidChangeBreakpointsCounter, 1); + assert.equal(debug.breakpoints.length, 2); + assert.equal(debug.breakpoints[0].id, '1'); + assert.equal(debug.breakpoints[1].id, '2'); + assert.equal(debug.breakpoints[1].condition, '2 < 5'); + + debug.removeBreakpoints([{ id: '1', enabled: true }]); + assert.equal(onDidChangeBreakpointsCounter, 2); + assert.equal(debug.breakpoints.length, 1); + + debug.removeBreakpoints([{ id: '2', enabled: false }]); + assert.equal(onDidChangeBreakpointsCounter, 3); + assert.equal(debug.breakpoints.length, 0); + + disposeAll(toDispose); + }); + + test.skip('start debugging', async function () { + let stoppedEvents = 0; + let variablesReceived: () => void; + let initializedReceived: () => void; + let configurationDoneReceived: () => void; + const toDispose: Disposable[] = []; + if (debug.activeDebugSession) { + // We are re-running due to flakyness, make sure to clear out state + let sessionTerminatedRetry: () => void; + toDispose.push(debug.onDidTerminateDebugSession(() => { + sessionTerminatedRetry(); + })); + const sessionTerminatedPromise = new Promise<void>(resolve => sessionTerminatedRetry = resolve); + await commands.executeCommand('workbench.action.debug.stop'); + await sessionTerminatedPromise; + } + + const firstVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve); + toDispose.push(debug.registerDebugAdapterTrackerFactory('*', { + createDebugAdapterTracker: () => ({ + onDidSendMessage: m => { + if (m.event === 'stopped') { + stoppedEvents++; + } + if (m.type === 'response' && m.command === 'variables') { + variablesReceived(); + } + if (m.event === 'initialized') { + initializedReceived(); + } + if (m.command === 'configurationDone') { + configurationDoneReceived(); + } + } + }) + })); + + const initializedPromise = new Promise<void>(resolve => initializedReceived = resolve); + const configurationDonePromise = new Promise<void>(resolve => configurationDoneReceived = resolve); + const success = await debug.startDebugging(workspace.workspaceFolders![0], 'Launch debug.js'); + assert.equal(success, true); + await initializedPromise; + await configurationDonePromise; + + await firstVariablesRetrieved; + assert.notEqual(debug.activeDebugSession, undefined); + assert.equal(stoppedEvents, 1); + + const secondVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve); + await commands.executeCommand('workbench.action.debug.stepOver'); + await secondVariablesRetrieved; + assert.equal(stoppedEvents, 2); + const editor = window.activeTextEditor; + assert.notEqual(editor, undefined); + assert.equal(basename(editor!.document.fileName), 'debug.js'); + + const thirdVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve); + await commands.executeCommand('workbench.action.debug.stepOver'); + await thirdVariablesRetrieved; + assert.equal(stoppedEvents, 3); + + const fourthVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve); + await commands.executeCommand('workbench.action.debug.stepInto'); + await fourthVariablesRetrieved; + assert.equal(stoppedEvents, 4); + + const fifthVariablesRetrieved = new Promise<void>(resolve => variablesReceived = resolve); + await commands.executeCommand('workbench.action.debug.stepOut'); + await fifthVariablesRetrieved; + assert.equal(stoppedEvents, 5); + + let sessionTerminated: () => void; + toDispose.push(debug.onDidTerminateDebugSession(() => { + sessionTerminated(); + })); + const sessionTerminatedPromise = new Promise<void>(resolve => sessionTerminated = resolve); + await commands.executeCommand('workbench.action.debug.stop'); + await sessionTerminatedPromise; + disposeAll(toDispose); + }); + + test('start debugging failure', async function () { + let errorCount = 0; + try { + await debug.startDebugging(workspace.workspaceFolders![0], 'non existent'); + } catch (e) { + errorCount++; + } + assert.equal(errorCount, 1); + }); +}); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts index 4b6ee382120..b5a0a79166b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle, SnippetString, Selection, Uri } from 'vscode'; +import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle, SnippetString, Selection, Uri, env } from 'vscode'; import { createRandomFile, deleteFile, closeAllEditors } from '../utils'; -suite('editor tests', () => { +suite('vscode API - editors', () => { teardown(closeAllEditors); @@ -47,6 +47,32 @@ suite('editor tests', () => { }); }); + test('insert snippet with clipboard variables', async function () { + const old = await env.clipboard.readText(); + + const newValue = 'INTEGRATION-TESTS'; + await env.clipboard.writeText(newValue); + + const actualValue = await env.clipboard.readText(); + + if (actualValue !== newValue) { + // clipboard not working?!? + this.skip(); + return; + } + + const snippetString = new SnippetString('running: $CLIPBOARD'); + + await withRandomFileEditor('', async (editor, doc) => { + const inserted = await editor.insertSnippet(snippetString); + assert.ok(inserted); + assert.equal(doc.getText(), 'running: INTEGRATION-TESTS'); + assert.ok(doc.isDirty); + }); + + await env.clipboard.writeText(old); + }); + test('insert snippet with replacement, editor selection', () => { const snippetString = new SnippetString() .appendText('has been'); @@ -114,20 +140,25 @@ suite('editor tests', () => { } test('TextEditor.edit can control undo/redo stack 1', () => { - return withRandomFileEditor('Hello world!', (editor, doc) => { - return executeReplace(editor, new Range(0, 0, 0, 1), 'h', false, false).then(applied => { - assert.ok(applied); - assert.equal(doc.getText(), 'hello world!'); - assert.ok(doc.isDirty); - return executeReplace(editor, new Range(0, 1, 0, 5), 'ELLO', false, false); - }).then(applied => { - assert.ok(applied); - assert.equal(doc.getText(), 'hELLO world!'); - assert.ok(doc.isDirty); - return commands.executeCommand('undo'); - }).then(_ => { - assert.equal(doc.getText(), 'Hello world!'); - }); + return withRandomFileEditor('Hello world!', async (editor, doc) => { + const applied1 = await executeReplace(editor, new Range(0, 0, 0, 1), 'h', false, false); + assert.ok(applied1); + assert.equal(doc.getText(), 'hello world!'); + assert.ok(doc.isDirty); + + const applied2 = await executeReplace(editor, new Range(0, 1, 0, 5), 'ELLO', false, false); + assert.ok(applied2); + assert.equal(doc.getText(), 'hELLO world!'); + assert.ok(doc.isDirty); + + await commands.executeCommand('undo'); + if (doc.getText() === 'hello world!') { + // see https://github.com/microsoft/vscode/issues/109131 + // it looks like an undo stop was inserted in between these two edits + // it is unclear why this happens, but it can happen for a multitude of reasons + await commands.executeCommand('undo'); + } + assert.equal(doc.getText(), 'Hello world!'); }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts index 318b0d07bb3..7bc2f75973f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { env, extensions, ExtensionKind, UIKind, Uri } from 'vscode'; -suite('env-namespace', () => { +suite('vscode API - env', () => { test('env is set', function () { assert.equal(typeof env.language, 'string'); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/index.ts b/extensions/vscode-api-tests/src/singlefolder-tests/index.ts index 8379f9b1ab7..ea50f38b1b9 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/index.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/index.ts @@ -6,21 +6,31 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Single Folder Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Single Folder Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Single Folder Tests'; +} else { + suite = 'Integration Single Folder Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { reporterEnabled: 'spec, mocha-junit-reporter', mochaJunitReporterReporterOptions: { testsuitesTitle: `${suite} ${process.platform}`, - mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) } }; } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts index 0a828ad8d73..a59841f6689 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts @@ -8,7 +8,7 @@ import { join } from 'path'; import * as vscode from 'vscode'; import { createRandomFile, testFs } from '../utils'; -suite('languages namespace tests', () => { +suite('vscode API - languages', () => { const isWindows = process.platform === 'win32'; @@ -29,31 +29,34 @@ suite('languages namespace tests', () => { const doc = await vscode.workspace.openTextDocument(file); const langIdNow = doc.languageId; let clock = 0; + const disposables: vscode.Disposable[] = []; - let close = new Promise(resolve => { - vscode.workspace.onDidCloseTextDocument(e => { + let close = new Promise<void>(resolve => { + disposables.push(vscode.workspace.onDidCloseTextDocument(e => { if (e === doc) { assert.equal(doc.languageId, langIdNow); assert.equal(clock, 0); clock += 1; resolve(); } - }); + })); }); - let open = new Promise(resolve => { - vscode.workspace.onDidOpenTextDocument(e => { + let open = new Promise<void>(resolve => { + disposables.push(vscode.workspace.onDidOpenTextDocument(e => { if (e === doc) { // same instance! assert.equal(doc.languageId, 'json'); assert.equal(clock, 1); clock += 1; resolve(); } - }); + })); }); let change = vscode.languages.setTextDocumentLanguage(doc, 'json'); await Promise.all([change, close, open]); assert.equal(clock, 2); assert.equal(doc.languageId, 'json'); + disposables.forEach(disposable => disposable.dispose()); + disposables.length = 0; }); test('setTextDocumentLanguage -> error when language does not exist', async function () { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts index d1237703dac..55fbe0655a7 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts @@ -18,190 +18,188 @@ interface QuickPickExpected { }; } -suite('window namespace tests', function () { +suite('vscode API - quick input', function () { - suite('QuickInput tests', function () { - teardown(closeAllEditors); + teardown(closeAllEditors); - test('createQuickPick, select second', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; + test('createQuickPick, select second', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; - const quickPick = createQuickPick({ - events: ['active', 'active', 'selection', 'accept', 'hide'], - activeItems: [['eins'], ['zwei']], - selectionItems: [['zwei']], - acceptedItems: { - active: [['zwei']], - selection: [['zwei']], - dispose: [true] - }, - }, (err?: any) => done(err)); - quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); - quickPick.show(); + const quickPick = createQuickPick({ + events: ['active', 'active', 'selection', 'accept', 'hide'], + activeItems: [['eins'], ['zwei']], + selectionItems: [['zwei']], + acceptedItems: { + active: [['zwei']], + selection: [['zwei']], + dispose: [true] + }, + }, (err?: any) => done(err)); + quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); + quickPick.show(); - (async () => { - await commands.executeCommand('workbench.action.quickOpenSelectNext'); - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - })() - .catch(err => done(err)); - }); + (async () => { + await commands.executeCommand('workbench.action.quickOpenSelectNext'); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + })() + .catch(err => done(err)); + }); - test('createQuickPick, focus second', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; + test('createQuickPick, focus second', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; - const quickPick = createQuickPick({ - events: ['active', 'selection', 'accept', 'hide'], - activeItems: [['zwei']], - selectionItems: [['zwei']], - acceptedItems: { - active: [['zwei']], - selection: [['zwei']], - dispose: [true] - }, - }, (err?: any) => done(err)); - quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); - quickPick.activeItems = [quickPick.items[1]]; - quickPick.show(); + const quickPick = createQuickPick({ + events: ['active', 'selection', 'accept', 'hide'], + activeItems: [['zwei']], + selectionItems: [['zwei']], + acceptedItems: { + active: [['zwei']], + selection: [['zwei']], + dispose: [true] + }, + }, (err?: any) => done(err)); + quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); + quickPick.activeItems = [quickPick.items[1]]; + quickPick.show(); - (async () => { - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - })() - .catch(err => done(err)); - }); + (async () => { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + })() + .catch(err => done(err)); + }); - test('createQuickPick, select first and second', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; + test('createQuickPick, select first and second', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; - const quickPick = createQuickPick({ - events: ['active', 'selection', 'active', 'selection', 'accept', 'hide'], - activeItems: [['eins'], ['zwei']], - selectionItems: [['eins'], ['eins', 'zwei']], - acceptedItems: { - active: [['zwei']], - selection: [['eins', 'zwei']], - dispose: [true] - }, - }, (err?: any) => done(err)); - quickPick.canSelectMany = true; - quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); - quickPick.show(); + const quickPick = createQuickPick({ + events: ['active', 'selection', 'active', 'selection', 'accept', 'hide'], + activeItems: [['eins'], ['zwei']], + selectionItems: [['eins'], ['eins', 'zwei']], + acceptedItems: { + active: [['zwei']], + selection: [['eins', 'zwei']], + dispose: [true] + }, + }, (err?: any) => done(err)); + quickPick.canSelectMany = true; + quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); + quickPick.show(); - (async () => { - await commands.executeCommand('workbench.action.quickOpenSelectNext'); - await commands.executeCommand('workbench.action.quickPickManyToggle'); - await commands.executeCommand('workbench.action.quickOpenSelectNext'); - await commands.executeCommand('workbench.action.quickPickManyToggle'); - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - })() - .catch(err => done(err)); - }); + (async () => { + await commands.executeCommand('workbench.action.quickOpenSelectNext'); + await commands.executeCommand('workbench.action.quickPickManyToggle'); + await commands.executeCommand('workbench.action.quickOpenSelectNext'); + await commands.executeCommand('workbench.action.quickPickManyToggle'); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + })() + .catch(err => done(err)); + }); - test('createQuickPick, selection events', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; + test('createQuickPick, selection events', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; - const quickPick = createQuickPick({ - events: ['active', 'selection', 'accept', 'selection', 'accept', 'hide'], - activeItems: [['eins']], - selectionItems: [['zwei'], ['drei']], - acceptedItems: { - active: [['eins'], ['eins']], - selection: [['zwei'], ['drei']], - dispose: [false, true] - }, - }, (err?: any) => done(err)); - quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); - quickPick.show(); + const quickPick = createQuickPick({ + events: ['active', 'selection', 'accept', 'selection', 'accept', 'hide'], + activeItems: [['eins']], + selectionItems: [['zwei'], ['drei']], + acceptedItems: { + active: [['eins'], ['eins']], + selection: [['zwei'], ['drei']], + dispose: [false, true] + }, + }, (err?: any) => done(err)); + quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); + quickPick.show(); - quickPick.selectedItems = [quickPick.items[1]]; - setTimeout(() => { - quickPick.selectedItems = [quickPick.items[2]]; - }, 0); - }); + quickPick.selectedItems = [quickPick.items[1]]; + setTimeout(() => { + quickPick.selectedItems = [quickPick.items[2]]; + }, 0); + }); - test('createQuickPick, continue after first accept', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; + test('createQuickPick, continue after first accept', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; - const quickPick = createQuickPick({ - events: ['active', 'selection', 'accept', 'active', 'selection', 'active', 'selection', 'accept', 'hide'], - activeItems: [['eins'], [], ['drei']], - selectionItems: [['eins'], [], ['drei']], - acceptedItems: { - active: [['eins'], ['drei']], - selection: [['eins'], ['drei']], - dispose: [false, true] - }, - }, (err?: any) => done(err)); - quickPick.items = ['eins', 'zwei'].map(label => ({ label })); - quickPick.show(); + const quickPick = createQuickPick({ + events: ['active', 'selection', 'accept', 'active', 'selection', 'active', 'selection', 'accept', 'hide'], + activeItems: [['eins'], [], ['drei']], + selectionItems: [['eins'], [], ['drei']], + acceptedItems: { + active: [['eins'], ['drei']], + selection: [['eins'], ['drei']], + dispose: [false, true] + }, + }, (err?: any) => done(err)); + quickPick.items = ['eins', 'zwei'].map(label => ({ label })); + quickPick.show(); - (async () => { - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + (async () => { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + await timeout(async () => { + quickPick.items = ['drei', 'vier'].map(label => ({ label })); await timeout(async () => { - quickPick.items = ['drei', 'vier'].map(label => ({ label })); - await timeout(async () => { - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - }, 0); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); }, 0); - })() - .catch(err => done(err)); + }, 0); + })() + .catch(err => done(err)); + }); + + test('createQuickPick, dispose in onDidHide', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; + + let hidden = false; + const quickPick = window.createQuickPick(); + quickPick.onDidHide(() => { + if (hidden) { + done(new Error('Already hidden')); + } else { + hidden = true; + quickPick.dispose(); + setTimeout(done, 0); + } }); + quickPick.show(); + quickPick.hide(); + }); - test('createQuickPick, dispose in onDidHide', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; + test('createQuickPick, hide and dispose', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; - let hidden = false; - const quickPick = window.createQuickPick(); - quickPick.onDidHide(() => { - if (hidden) { - done(new Error('Already hidden')); - } else { - hidden = true; - quickPick.dispose(); - setTimeout(done, 0); - } - }); - quickPick.show(); - quickPick.hide(); - }); - - test('createQuickPick, hide and dispose', function (_done) { - let done = (err?: any) => { - done = () => {}; - _done(err); - }; - - let hidden = false; - const quickPick = window.createQuickPick(); - quickPick.onDidHide(() => { - if (hidden) { - done(new Error('Already hidden')); - } else { - hidden = true; - setTimeout(done, 0); - } - }); - quickPick.show(); - quickPick.hide(); - quickPick.dispose(); + let hidden = false; + const quickPick = window.createQuickPick(); + quickPick.onDidHide(() => { + if (hidden) { + done(new Error('Already hidden')); + } else { + hidden = true; + setTimeout(done, 0); + } }); + quickPick.show(); + quickPick.hide(); + quickPick.dispose(); }); }); @@ -276,4 +274,4 @@ function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, async function timeout<T>(run: () => Promise<T> | T, ms: number): Promise<T> { return new Promise<T>(resolve => setTimeout(() => resolve(run()), ms)); -} \ No newline at end of file +} diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index a45c3188e00..01232a99c8c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -3,14 +3,29 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget, Disposable } from 'vscode'; +import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget, Disposable, UIKind, env, EnvironmentVariableMutatorType, EnvironmentVariableMutator, extensions, ExtensionContext, TerminalOptions, ExtensionTerminalOptions } from 'vscode'; import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; -suite('window namespace tests', () => { +// Disable terminal tests: +// - Web https://github.com/microsoft/vscode/issues/92826 +// - Remote https://github.com/microsoft/vscode/issues/96057 +((env.uiKind === UIKind.Web || typeof env.remoteName !== 'undefined') ? suite.skip : suite)('vscode API - terminal', () => { + let extensionContext: ExtensionContext; + suiteSetup(async () => { + // Trigger extension activation and grab the context as some tests depend on it + await extensions.getExtension('vscode.vscode-api-tests')?.activate(); + extensionContext = (global as any).testExtensionContext; + + const config = workspace.getConfiguration('terminal.integrated'); // Disable conpty in integration tests because of https://github.com/microsoft/vscode/issues/76548 - await workspace.getConfiguration('terminal.integrated').update('windowsEnableConpty', false, ConfigurationTarget.Global); + await config.update('windowsEnableConpty', false, ConfigurationTarget.Global); + // Disable exit alerts as tests may trigger then and we're not testing the notifications + await config.update('showExitAlert', false, ConfigurationTarget.Global); + // Canvas may cause problems when running in a container + await config.update('rendererType', 'dom', ConfigurationTarget.Global); }); + suite('Terminal', () => { let disposables: Disposable[] = []; @@ -25,6 +40,7 @@ suite('window namespace tests', () => { equal(terminal, term); } catch (e) { done(e); + return; } terminal.dispose(); disposables.push(window.onDidCloseTerminal(() => done())); @@ -33,12 +49,58 @@ suite('window namespace tests', () => { doesNotThrow(terminal.sendText.bind(terminal, 'echo "foo"')); }); + (process.platform === 'linux' ? test.skip : test)('echo works in the default shell', (done) => { + disposables.push(window.onDidOpenTerminal(term => { + try { + equal(terminal, term); + } catch (e) { + done(e); + return; + } + let data = ''; + const dataDisposable = window.onDidWriteTerminalData(e => { + try { + equal(terminal, e.terminal); + } catch (e) { + done(e); + return; + } + data += e.data; + if (data.indexOf(expected) !== 0) { + dataDisposable.dispose(); + terminal.dispose(); + disposables.push(window.onDidCloseTerminal(() => { + done(); + })); + } + }); + disposables.push(dataDisposable); + })); + // Use a single character to avoid winpty/conpty issues with injected sequences + const expected = '`'; + const terminal = window.createTerminal({ + env: { + TEST: '`' + } + }); + terminal.show(); + doesNotThrow(() => { + // Print an environment variable value so the echo statement doesn't get matched + if (process.platform === 'win32') { + terminal.sendText(`$env:TEST`); + } else { + terminal.sendText(`echo $TEST`); + } + }); + }); + test('onDidCloseTerminal event fires when terminal is disposed', (done) => { disposables.push(window.onDidOpenTerminal(term => { try { equal(terminal, term); } catch (e) { done(e); + return; } terminal.dispose(); disposables.push(window.onDidCloseTerminal(() => done())); @@ -52,12 +114,14 @@ suite('window namespace tests', () => { equal(terminal, term); } catch (e) { done(e); + return; } terminal.processId.then(id => { try { ok(id && id > 0); } catch (e) { done(e); + return; } terminal.dispose(); disposables.push(window.onDidCloseTerminal(() => done())); @@ -72,6 +136,7 @@ suite('window namespace tests', () => { equal(terminal, term); } catch (e) { done(e); + return; } terminal.dispose(); disposables.push(window.onDidCloseTerminal(() => done())); @@ -81,6 +146,7 @@ suite('window namespace tests', () => { equal(terminal.name, 'a'); } catch (e) { done(e); + return; } }); @@ -90,6 +156,7 @@ suite('window namespace tests', () => { equal(terminal, term); } catch (e) { done(e); + return; } terminal.dispose(); disposables.push(window.onDidCloseTerminal(() => done())); @@ -101,10 +168,13 @@ suite('window namespace tests', () => { const terminal = window.createTerminal(options); try { equal(terminal.name, 'foo'); - deepEqual(terminal.creationOptions, options); - throws(() => (<any>terminal.creationOptions).name = 'bad', 'creationOptions should be readonly at runtime'); + const terminalOptions = terminal.creationOptions as TerminalOptions; + equal(terminalOptions.name, 'foo'); + equal(terminalOptions.hideFromUser, true); + throws(() => terminalOptions.name = 'bad', 'creationOptions should be readonly at runtime'); } catch (e) { done(e); + return; } }); @@ -114,6 +184,7 @@ suite('window namespace tests', () => { equal(term.name, 'b'); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(() => done())); terminal.dispose(); @@ -127,6 +198,7 @@ suite('window namespace tests', () => { equal(term, terminal); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(t => { try { @@ -222,6 +294,7 @@ suite('window namespace tests', () => { ok(window.terminals.indexOf(terminal) !== -1); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(() => { // reg3.dispose(); @@ -276,7 +349,7 @@ suite('window namespace tests', () => { term1Write.fire('write1'); // Wait until the data is written - await new Promise(resolve => { resolveOnceDataWritten = resolve; }); + await new Promise<void>(resolve => { resolveOnceDataWritten = resolve; }); term1Close.fire(); @@ -319,6 +392,7 @@ suite('window namespace tests', () => { equal(term.name, 'c'); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(() => done())); term.dispose(); @@ -384,22 +458,23 @@ suite('window namespace tests', () => { equal(terminal, term); } catch (e) { done(e); + return; } term.show(); disposables.push(window.onDidChangeTerminalDimensions(e => { - if (e.dimensions.columns === 0 || e.dimensions.rows === 0) { - // HACK: Ignore the event if dimension(s) are zero (#83778) - return; + // The default pty dimensions have a chance to appear here since override + // dimensions happens after the terminal is created. If so just ignore and + // wait for the right dimensions + if (e.dimensions.columns === 10 || e.dimensions.rows === 5) { + try { + equal(e.terminal, terminal); + } catch (e) { + done(e); + return; + } + disposables.push(window.onDidCloseTerminal(() => done())); + terminal.dispose(); } - try { - equal(e.dimensions.columns, 10); - equal(e.dimensions.rows, 5); - equal(e.terminal, terminal); - } catch (e) { - done(e); - } - disposables.push(window.onDidCloseTerminal(() => done())); - terminal.dispose(); })); })); const writeEmitter = new EventEmitter<string>(); @@ -420,6 +495,7 @@ suite('window namespace tests', () => { equal(terminal.exitStatus, undefined); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(t => { try { @@ -437,7 +513,7 @@ suite('window namespace tests', () => { const pty: Pseudoterminal = { onDidWrite: writeEmitter.event, onDidClose: closeEmitter.event, - open: () => closeEmitter.fire(), + open: () => closeEmitter.fire(undefined), close: () => { } }; const terminal = window.createTerminal({ name: 'foo', pty }); @@ -450,6 +526,7 @@ suite('window namespace tests', () => { equal(terminal.exitStatus, undefined); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(t => { try { @@ -480,6 +557,7 @@ suite('window namespace tests', () => { equal(terminal.exitStatus, undefined); } catch (e) { done(e); + return; } disposables.push(window.onDidCloseTerminal(t => { try { @@ -497,7 +575,12 @@ suite('window namespace tests', () => { const pty: Pseudoterminal = { onDidWrite: writeEmitter.event, onDidClose: closeEmitter.event, - open: () => closeEmitter.fire(22), + open: () => { + // Wait 500ms as any exits that occur within 500ms of terminal launch are + // are counted as "exiting during launch" which triggers a notification even + // when showExitAlerts is true + setTimeout(() => closeEmitter.fire(22), 500); + }, close: () => { } }; const terminal = window.createTerminal({ name: 'foo', pty }); @@ -509,6 +592,7 @@ suite('window namespace tests', () => { equal(terminal, term); } catch (e) { done(e); + return; } terminal.dispose(); disposables.push(window.onDidCloseTerminal(() => done())); @@ -523,12 +607,214 @@ suite('window namespace tests', () => { const terminal = window.createTerminal(options); try { equal(terminal.name, 'foo'); - deepEqual(terminal.creationOptions, options); - throws(() => (<any>terminal.creationOptions).name = 'bad', 'creationOptions should be readonly at runtime'); + const terminalOptions = terminal.creationOptions as ExtensionTerminalOptions; + equal(terminalOptions.name, 'foo'); + equal(terminalOptions.pty, pty); + throws(() => terminalOptions.name = 'bad', 'creationOptions should be readonly at runtime'); } catch (e) { done(e); } }); }); + + suite('environmentVariableCollection', () => { + test('should have collection variables apply to terminals immediately after setting', (done) => { + // Text to match on before passing the test + const expectedText = [ + '~a2~', + 'b1~b2~', + '~c2~c1' + ]; + disposables.push(window.onDidWriteTerminalData(e => { + try { + equal(terminal, e.terminal); + } catch (e) { + done(e); + return; + } + // Multiple expected could show up in the same data event + while (expectedText.length > 0 && e.data.indexOf(expectedText[0]) >= 0) { + expectedText.shift(); + // Check if all string are found, if so finish the test + if (expectedText.length === 0) { + disposables.push(window.onDidCloseTerminal(() => done())); + terminal.dispose(); + } + } + })); + const collection = extensionContext.environmentVariableCollection; + disposables.push({ dispose: () => collection.clear() }); + collection.replace('A', '~a2~'); + collection.append('B', '~b2~'); + collection.prepend('C', '~c2~'); + const terminal = window.createTerminal({ + env: { + A: 'a1', + B: 'b1', + C: 'c1' + } + }); + // Run both PowerShell and sh commands, errors don't matter we're just looking for + // the correct output + terminal.sendText('$env:A'); + terminal.sendText('echo $A'); + terminal.sendText('$env:B'); + terminal.sendText('echo $B'); + terminal.sendText('$env:C'); + terminal.sendText('echo $C'); + }); + + test('should have collection variables apply to environment variables that don\'t exist', (done) => { + // Text to match on before passing the test + const expectedText = [ + '~a2~', + '~b2~', + '~c2~' + ]; + disposables.push(window.onDidWriteTerminalData(e => { + try { + equal(terminal, e.terminal); + } catch (e) { + done(e); + return; + } + // Multiple expected could show up in the same data event + while (expectedText.length > 0 && e.data.indexOf(expectedText[0]) >= 0) { + expectedText.shift(); + // Check if all string are found, if so finish the test + if (expectedText.length === 0) { + disposables.push(window.onDidCloseTerminal(() => done())); + terminal.dispose(); + } + } + })); + const collection = extensionContext.environmentVariableCollection; + disposables.push({ dispose: () => collection.clear() }); + collection.replace('A', '~a2~'); + collection.append('B', '~b2~'); + collection.prepend('C', '~c2~'); + const terminal = window.createTerminal({ + env: { + A: null, + B: null, + C: null + } + }); + // Run both PowerShell and sh commands, errors don't matter we're just looking for + // the correct output + terminal.sendText('$env:A'); + terminal.sendText('echo $A'); + terminal.sendText('$env:B'); + terminal.sendText('echo $B'); + terminal.sendText('$env:C'); + terminal.sendText('echo $C'); + }); + + test('should respect clearing entries', (done) => { + // Text to match on before passing the test + const expectedText = [ + '~a1~', + '~b1~' + ]; + disposables.push(window.onDidWriteTerminalData(e => { + try { + equal(terminal, e.terminal); + } catch (e) { + done(e); + return; + } + // Multiple expected could show up in the same data event + while (expectedText.length > 0 && e.data.indexOf(expectedText[0]) >= 0) { + expectedText.shift(); + // Check if all string are found, if so finish the test + if (expectedText.length === 0) { + disposables.push(window.onDidCloseTerminal(() => done())); + terminal.dispose(); + } + } + })); + const collection = extensionContext.environmentVariableCollection; + disposables.push({ dispose: () => collection.clear() }); + collection.replace('A', '~a2~'); + collection.replace('B', '~a2~'); + collection.clear(); + const terminal = window.createTerminal({ + env: { + A: '~a1~', + B: '~b1~' + } + }); + // Run both PowerShell and sh commands, errors don't matter we're just looking for + // the correct output + terminal.sendText('$env:A'); + terminal.sendText('echo $A'); + terminal.sendText('$env:B'); + terminal.sendText('echo $B'); + }); + + test('should respect deleting entries', (done) => { + // Text to match on before passing the test + const expectedText = [ + '~a1~', + '~b2~' + ]; + disposables.push(window.onDidWriteTerminalData(e => { + try { + equal(terminal, e.terminal); + } catch (e) { + done(e); + return; + } + // Multiple expected could show up in the same data event + while (expectedText.length > 0 && e.data.indexOf(expectedText[0]) >= 0) { + expectedText.shift(); + // Check if all string are found, if so finish the test + if (expectedText.length === 0) { + disposables.push(window.onDidCloseTerminal(() => done())); + terminal.dispose(); + } + } + })); + const collection = extensionContext.environmentVariableCollection; + disposables.push({ dispose: () => collection.clear() }); + collection.replace('A', '~a2~'); + collection.replace('B', '~b2~'); + collection.delete('A'); + const terminal = window.createTerminal({ + env: { + A: '~a1~', + B: '~b2~' + } + }); + // Run both PowerShell and sh commands, errors don't matter we're just looking for + // the correct output + terminal.sendText('$env:A'); + terminal.sendText('echo $A'); + terminal.sendText('$env:B'); + terminal.sendText('echo $B'); + }); + + test('get and forEach should work', () => { + const collection = extensionContext.environmentVariableCollection; + disposables.push({ dispose: () => collection.clear() }); + collection.replace('A', '~a2~'); + collection.append('B', '~b2~'); + collection.prepend('C', '~c2~'); + + // Verify get + deepEqual(collection.get('A'), { value: '~a2~', type: EnvironmentVariableMutatorType.Replace }); + deepEqual(collection.get('B'), { value: '~b2~', type: EnvironmentVariableMutatorType.Append }); + deepEqual(collection.get('C'), { value: '~c2~', type: EnvironmentVariableMutatorType.Prepend }); + + // Verify forEach + const entries: [string, EnvironmentVariableMutator][] = []; + collection.forEach((v, m) => entries.push([v, m])); + deepEqual(entries, [ + ['A', { value: '~a2~', type: EnvironmentVariableMutatorType.Replace }], + ['B', { value: '~b2~', type: EnvironmentVariableMutatorType.Append }], + ['C', { value: '~c2~', type: EnvironmentVariableMutatorType.Prepend }] + ]); + }); + }); }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts index b2ad43d30b0..53265b35e99 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts @@ -7,12 +7,9 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; - -suite('types', () => { +suite('vscode API - types', () => { test('static properties, es5 compat class', function () { - - assert.ok(vscode.ThemeIcon.File instanceof vscode.ThemeIcon); assert.ok(vscode.ThemeIcon.Folder instanceof vscode.ThemeIcon); assert.ok(vscode.CodeActionKind.Empty instanceof vscode.CodeActionKind); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index b59d91ff380..fd97ea91728 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -3,18 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'mocha'; import * as assert from 'assert'; +import 'mocha'; +import * as os from 'os'; import * as vscode from 'vscode'; -import { join } from 'path'; -import { closeAllEditors, disposeAll, conditionalTest } from '../utils'; +import { closeAllEditors, delay, disposeAll } from '../utils'; const webviewId = 'myWebview'; -const testDocument = join(vscode.workspace.rootPath || '', './bower.json'); +function workspaceFile(...segments: string[]) { + return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); +} -// TODO: Re-enable after https://github.com/microsoft/vscode/issues/88415 -suite.skip('Webview tests', () => { +const testDocument = workspaceFile('bower.json'); + +suite.skip('vscode API - webview', () => { const disposables: vscode.Disposable[] = []; function _register<T extends vscode.Disposable>(disposable: T) { @@ -83,7 +86,7 @@ suite.skip('Webview tests', () => { } }); - conditionalTest('webviews should preserve vscode API state when they are hidden', async () => { + test.skip('webviews should preserve vscode API state when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -126,7 +129,7 @@ suite.skip('Webview tests', () => { assert.strictEqual(secondResponse.value, 1); }); - conditionalTest('webviews should preserve their context when they are moved between view columns', async () => { + test('webviews should preserve their context when they are moved between view columns', async () => { const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc, vscode.ViewColumn.One); @@ -147,7 +150,7 @@ suite.skip('Webview tests', () => { assert.strictEqual(secondResponse.value, 1); }); - conditionalTest('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { + test('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); @@ -169,7 +172,7 @@ suite.skip('Webview tests', () => { assert.strictEqual(secondResponse.value, 1); }); - conditionalTest('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { + test('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -190,13 +193,12 @@ suite.skip('Webview tests', () => { } }); vscode.postMessage({ type: 'ready' }); - </script>`); await ready; const firstResponse = getMesssage(webview); - assert.strictEqual((await firstResponse).value, 100); + assert.strictEqual(Math.round((await firstResponse).value), 100); // Swap away from the webview const doc = await vscode.workspace.openTextDocument(testDocument); @@ -207,10 +209,10 @@ suite.skip('Webview tests', () => { // We should still have old scroll pos const secondResponse = await sendRecieveMessage(webview, { type: 'get' }); - assert.strictEqual(secondResponse.value, 100); + assert.strictEqual(Math.round(secondResponse.value), 100); }); - conditionalTest('webviews with retainContextWhenHidden should be able to recive messages while hidden', async () => { + test('webviews with retainContextWhenHidden should be able to recive messages while hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); @@ -237,47 +239,67 @@ suite.skip('Webview tests', () => { }); - conditionalTest('webviews should only be able to load resources from workspace by default', async () => { - const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); + test.skip('webviews should only be able to load resources from workspace by default', async () => { + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { + viewColumn: vscode.ViewColumn.One + }, { + enableScripts: true + })); webview.webview.html = createHtmlDocumentWithBody(/*html*/` <script> const vscode = acquireVsCodeApi(); window.addEventListener('message', (message) => { const img = document.createElement('img'); - img.addEventListener('load', () => { vscode.postMessage({ value: true }); }); - img.addEventListener('error', () => { vscode.postMessage({ value: false }); }); + img.addEventListener('load', () => { + vscode.postMessage({ value: true }); + }); + img.addEventListener('error', () => { + vscode.postMessage({ value: false }); + }); img.src = message.data.src; document.body.appendChild(img); }); + + vscode.postMessage({ type: 'ready' }); </script>`); - async function asWebviewUri(path: string) { - const root = await webview.webview.asWebviewUri(vscode.Uri.file(vscode.workspace.rootPath!)); - return root.toString() + path; - } + const ready = getMesssage(webview); + await ready; { - const imagePath = await asWebviewUri('/image.png'); - const response = sendRecieveMessage(webview, { src: imagePath }); - assert.strictEqual((await response).value, true); + const imagePath = webview.webview.asWebviewUri(workspaceFile('image.png')); + const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + assert.strictEqual(response.value, true); + } + // { + // // #102188. Resource filename containing special characters like '%', '#', '?'. + // const imagePath = webview.webview.asWebviewUri(workspaceFile('image%02.png')); + // const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + // assert.strictEqual(response.value, true); + // } + // { + // // #102188. Resource filename containing special characters like '%', '#', '?'. + // const imagePath = webview.webview.asWebviewUri(workspaceFile('image%.png')); + // const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + // assert.strictEqual(response.value, true); + // } + { + const imagePath = webview.webview.asWebviewUri(workspaceFile('no-such-image.png')); + const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + assert.strictEqual(response.value, false); } { - const imagePath = await asWebviewUri('/no-such-image.png'); - const response = sendRecieveMessage(webview, { src: imagePath }); - assert.strictEqual((await response).value, false); - } - { - const imagePath = vscode.Uri.file(join(vscode.workspace.rootPath!, '..', '..', '..', 'resources', 'linux', 'code.png')).with({ scheme: 'vscode-resource' }); - const response = sendRecieveMessage(webview, { src: imagePath.toString() }); - assert.strictEqual((await response).value, false); + const imagePath = webview.webview.asWebviewUri(workspaceFile('..', '..', '..', 'resources', 'linux', 'code.png')); + const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + assert.strictEqual(response.value, false); } }); - conditionalTest('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { + test.skip('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, - localResourceRoots: [vscode.Uri.file(join(vscode.workspace.rootPath!, 'sub'))] + localResourceRoots: [workspaceFile('sub')] })); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -292,18 +314,38 @@ suite.skip('Webview tests', () => { }); </script>`); - const workspaceRootUri = vscode.Uri.file(vscode.workspace.rootPath!).with({ scheme: 'vscode-resource' }); - { - const response = sendRecieveMessage(webview, { src: workspaceRootUri.toString() + '/sub/image.png' }); + const response = sendRecieveMessage(webview, { src: webview.webview.asWebviewUri(workspaceFile('sub', 'image.png')).toString() }); assert.strictEqual((await response).value, true); } { - const response = sendRecieveMessage(webview, { src: workspaceRootUri.toString() + '/image.png' }); + const response = sendRecieveMessage(webview, { src: webview.webview.asWebviewUri(workspaceFile('image.png')).toString() }); assert.strictEqual((await response).value, false); } }); + test.skip('webviews using hard-coded old style vscode-resource uri should work', async () => { + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { + enableScripts: true, + localResourceRoots: [workspaceFile('sub')] + })); + + const imagePath = workspaceFile('sub', 'image.png').with({ scheme: 'vscode-resource' }).toString(); + + webview.webview.html = createHtmlDocumentWithBody(/*html*/` + <img src="${imagePath}"> + <script> + const vscode = acquireVsCodeApi(); + const img = document.getElementsByTagName('img')[0]; + img.addEventListener('load', () => { vscode.postMessage({ value: true }); }); + img.addEventListener('error', () => { vscode.postMessage({ value: false }); }); + </script>`); + + const firstResponse = getMesssage(webview); + + assert.strictEqual((await firstResponse).value, true); + }); + test('webviews should have real view column after they are created, #56097', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.Active }, { enableScripts: true })); @@ -333,8 +375,30 @@ suite.skip('Webview tests', () => { webview.webview.postMessage({ value: 1 }); await firstResponse; assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One); - }); + + if (os.platform() === 'darwin') { + test.skip('webview can copy text from webview', async () => { + const expectedText = `webview text from: ${Date.now()}!`; + + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); + const ready = getMesssage(webview); + + + webview.webview.html = createHtmlDocumentWithBody(/*html*/` + <b>${expectedText}</b> + <script> + const vscode = acquireVsCodeApi(); + document.execCommand('selectAll'); + vscode.postMessage({ type: 'ready' }); + </script>`); + await ready; + + await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); + await delay(200); // Make sure copy has time to reach webview + assert.strictEqual(await vscode.env.clipboard.readText(), expectedText); + }); + } }); function createHtmlDocumentWithBody(body: string): string { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 620ce762632..89f3dbb7259 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind } from 'vscode'; +import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, QuickPickItem, TextEditor } from 'vscode'; import { join } from 'path'; import { closeAllEditors, pathEquals, createRandomFile } from '../utils'; -suite('window namespace tests', () => { + +suite('vscode API - window', () => { teardown(closeAllEditors); @@ -84,7 +85,7 @@ suite('window namespace tests', () => { let [one, two] = editors; - await new Promise(resolve => { + await new Promise<void>(resolve => { let registration2 = window.onDidChangeTextEditorViewColumn(event => { actualEvent = event; registration2.dispose(); @@ -119,7 +120,7 @@ suite('window namespace tests', () => { let [, two] = editors; two.show(); - return new Promise(resolve => { + return new Promise<void>(resolve => { let registration2 = window.onDidChangeTextEditorViewColumn(event => { actualEvents.push(event); @@ -146,6 +147,23 @@ suite('window namespace tests', () => { }); test('active editor not always correct... #49125', async function () { + if (process.env['BUILD_SOURCEVERSION']) { + this.skip(); + return; + } + function assertActiveEditor(editor: TextEditor) { + if (window.activeTextEditor === editor) { + assert.ok(true); + return; + } + function printEditor(editor: TextEditor): string { + return `doc: ${editor.document.uri.toString()}, column: ${editor.viewColumn}, active: ${editor === window.activeTextEditor}`; + } + const visible = window.visibleTextEditors.map(editor => printEditor(editor)); + assert.ok(false, `ACTIVE editor should be ${printEditor(editor)}, BUT HAVING ${visible.join(', ')}`); + + } + const randomFile1 = await createRandomFile(); const randomFile2 = await createRandomFile(); @@ -155,10 +173,10 @@ suite('window namespace tests', () => { ]); for (let c = 0; c < 4; c++) { let editorA = await window.showTextDocument(docA, ViewColumn.One); - assert.equal(window.activeTextEditor, editorA); + assertActiveEditor(editorA); let editorB = await window.showTextDocument(docB, ViewColumn.Two); - assert.equal(window.activeTextEditor, editorB); + assertActiveEditor(editorB); } }); @@ -383,39 +401,39 @@ suite('window namespace tests', () => { assert.equal(await two, 'notempty'); }); - // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) - // test('showQuickPick, accept first', async function () { - // const pick = window.showQuickPick(['eins', 'zwei', 'drei']); - // await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. - // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - // assert.equal(await pick, 'eins'); - // }); - - test('showQuickPick, accept second', async function () { - const resolves: ((value: string) => void)[] = []; - let done: () => void; - const unexpected = new Promise((resolve, reject) => { - done = () => resolve(); - resolves.push(reject); - }); - const first = new Promise(resolve => resolves.push(resolve)); + test('showQuickPick, accept first', async function () { + const tracker = createQuickPickTracker<string>(); + const first = tracker.nextItem(); const pick = window.showQuickPick(['eins', 'zwei', 'drei'], { - onDidSelectItem: item => resolves.pop()!(item as string) + onDidSelectItem: tracker.onDidSelectItem }); assert.equal(await first, 'eins'); - const second = new Promise(resolve => resolves.push(resolve)); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick, 'eins'); + return tracker.done(); + }); + + test('showQuickPick, accept second', async function () { + const tracker = createQuickPickTracker<string>(); + const first = tracker.nextItem(); + const pick = window.showQuickPick(['eins', 'zwei', 'drei'], { + onDidSelectItem: tracker.onDidSelectItem + }); + assert.equal(await first, 'eins'); + const second = tracker.nextItem(); await commands.executeCommand('workbench.action.quickOpenSelectNext'); assert.equal(await second, 'zwei'); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); assert.equal(await pick, 'zwei'); - done!(); - return unexpected; + return tracker.done(); }); test('showQuickPick, select first two', async function () { + const label = 'showQuickPick, select first two'; + let i = 0; const resolves: ((value: string) => void)[] = []; let done: () => void; - const unexpected = new Promise((resolve, reject) => { + const unexpected = new Promise<void>((resolve, reject) => { done = () => resolve(); resolves.push(reject); }); @@ -424,33 +442,51 @@ suite('window namespace tests', () => { canPickMany: true }); const first = new Promise(resolve => resolves.push(resolve)); - await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. + console.log(`${label}: ${++i}`); + await new Promise(resolve => setTimeout(resolve, 100)); // Allow UI to update. + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickOpenSelectNext'); + console.log(`${label}: ${++i}`); assert.equal(await first, 'eins'); + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickPickManyToggle'); + console.log(`${label}: ${++i}`); const second = new Promise(resolve => resolves.push(resolve)); await commands.executeCommand('workbench.action.quickOpenSelectNext'); + console.log(`${label}: ${++i}`); assert.equal(await second, 'zwei'); + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickPickManyToggle'); + console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + console.log(`${label}: ${++i}`); assert.deepStrictEqual(await picks, ['eins', 'zwei']); + console.log(`${label}: ${++i}`); done!(); return unexpected; }); - // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) - // test('showQuickPick, keep selection (Microsoft/vscode-azure-account#67)', async function () { - // const picks = window.showQuickPick([ - // { label: 'eins' }, - // { label: 'zwei', picked: true }, - // { label: 'drei', picked: true } - // ], { - // canPickMany: true - // }); - // await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. - // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - // assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']); - // }); + test('showQuickPick, keep selection (microsoft/vscode-azure-account#67)', async function () { + const picks = window.showQuickPick([ + { label: 'eins' }, + { label: 'zwei', picked: true }, + { label: 'drei', picked: true } + ], { + canPickMany: true + }); + await new Promise<void>(resolve => setTimeout(() => resolve(), 100)); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + if (await Promise.race([picks, new Promise<boolean>(resolve => setTimeout(() => resolve(false), 100))]) === false) { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + if (await Promise.race([picks, new Promise<boolean>(resolve => setTimeout(() => resolve(false), 1000))]) === false) { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + if (await Promise.race([picks, new Promise<boolean>(resolve => setTimeout(() => resolve(false), 1000))]) === false) { + assert.ok(false, 'Picks not resolved!'); + } + } + } + assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']); + }); test('showQuickPick, undefined on cancel', function () { const source = new CancellationTokenSource(); @@ -521,20 +557,24 @@ suite('window namespace tests', () => { return Promise.all([a, b]); }); - // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) - // test('showWorkspaceFolderPick', async function () { - // const p = window.showWorkspaceFolderPick(undefined); + test('showWorkspaceFolderPick', async function () { + const p = window.showWorkspaceFolderPick(undefined); - // await timeout(10); - // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - // try { - // await p; - // assert.ok(true); - // } - // catch (_error) { - // assert.ok(false); - // } - // }); + await new Promise(resolve => setTimeout(resolve, 10)); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + const r1 = await Promise.race([p, new Promise<boolean>(resolve => setTimeout(() => resolve(false), 100))]); + if (r1 !== false) { + return; + } + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + const r2 = await Promise.race([p, new Promise<boolean>(resolve => setTimeout(() => resolve(false), 1000))]); + if (r2 !== false) { + return; + } + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + const r3 = await Promise.race([p, new Promise<boolean>(resolve => setTimeout(() => resolve(false), 1000))]); + assert.ok(r3 !== false); + }); test('Default value for showInput Box not accepted when it fails validateInput, reversing #33691', async function () { const result = window.showInputBox({ @@ -551,12 +591,29 @@ suite('window namespace tests', () => { assert.equal(await result, undefined); }); + function createQuickPickTracker<T extends string | QuickPickItem>() { + const resolves: ((value: T) => void)[] = []; + let done: () => void; + const unexpected = new Promise<void>((resolve, reject) => { + done = () => resolve(); + resolves.push(reject); + }); + return { + onDidSelectItem: (item: T) => resolves.pop()!(item), + nextItem: () => new Promise<T>(resolve => resolves.push(resolve)), + done: () => { + done!(); + return unexpected; + }, + }; + } + test('editor, selection change kind', () => { return workspace.openTextDocument(join(workspace.rootPath || '', './far.js')).then(doc => window.showTextDocument(doc)).then(editor => { - return new Promise((resolve, _reject) => { + return new Promise<void>((resolve, _reject) => { let subscription = window.onDidChangeTextEditorSelection(e => { assert.ok(e.textEditor === editor); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts index ce6274157a1..e192c63c0ab 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { createRandomFile, withLogDisabled } from '../utils'; -suite('workspace-event', () => { +suite('vscode API - workspace events', () => { const disposables: vscode.Disposable[] = []; @@ -201,9 +201,29 @@ suite('workspace-event', () => { assert.equal(onDidRename?.files[0].newUri.toString(), newUri.toString()); }); - test('onWillRename - make changes', async function () { + test('onWillRename - make changes (saved file)', function () { + return testOnWillRename(false); + }); + + test('onWillRename - make changes (dirty file)', function () { + return testOnWillRename(true); + }); + + async function testOnWillRename(withDirtyFile: boolean): Promise<void> { const oldUri = await createRandomFile('BAR'); + + if (withDirtyFile) { + const edit = new vscode.WorkspaceEdit(); + edit.insert(oldUri, new vscode.Position(0, 0), 'BAR'); + + const success = await vscode.workspace.applyEdit(edit); + assert.ok(success); + + const oldDocument = await vscode.workspace.openTextDocument(oldUri); + assert.ok(oldDocument.isDirty); + } + const newUri = oldUri.with({ path: oldUri.path + '-NEW' }); const anotherFile = await createRandomFile('BAR'); @@ -229,7 +249,13 @@ suite('workspace-event', () => { assert.equal(onWillRename?.files[0].oldUri.toString(), oldUri.toString()); assert.equal(onWillRename?.files[0].newUri.toString(), newUri.toString()); - assert.equal((await vscode.workspace.openTextDocument(newUri)).getText(), 'FOOBAR'); - assert.equal((await vscode.workspace.openTextDocument(anotherFile)).getText(), 'FARBOO'); - }); + const newDocument = await vscode.workspace.openTextDocument(newUri); + const anotherDocument = await vscode.workspace.openTextDocument(anotherFile); + + assert.equal(newDocument.getText(), withDirtyFile ? 'FOOBARBAR' : 'FOOBAR'); + assert.equal(anotherDocument.getText(), 'FARBOO'); + + assert.ok(newDocument.isDirty); + assert.ok(anotherDocument.isDirty); + } }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts index 7d552df04a8..2eb21a4c1f9 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { posix } from 'path'; -suite('workspace-fs', () => { +suite('vscode API - workspace-fs', () => { let root: vscode.Uri; @@ -140,4 +140,42 @@ suite('workspace-fs', () => { assert.equal(e.name, vscode.FileSystemError.Unavailable().name); } }); + + test('vscode.workspace.fs.remove() (and copy()) succeed unexpectedly. #84177', async function () { + const entries = await vscode.workspace.fs.readDirectory(root); + assert.ok(entries.length > 0); + + const someFolder = root.with({ path: posix.join(root.path, '6b1f9d664a92') }); + + try { + await vscode.workspace.fs.delete(someFolder, { recursive: true }); + assert.ok(false); + } catch (err) { + assert.ok(true); + } + }); + + test('vscode.workspace.fs.remove() (and copy()) succeed unexpectedly. #84177', async function () { + const entries = await vscode.workspace.fs.readDirectory(root); + assert.ok(entries.length > 0); + + const folder = root.with({ path: posix.join(root.path, 'folder') }); + const file = root.with({ path: posix.join(root.path, 'folder/file') }); + + await vscode.workspace.fs.createDirectory(folder); + await vscode.workspace.fs.writeFile(file, Buffer.from('FOO')); + + const someFolder = root.with({ path: posix.join(root.path, '6b1f9d664a92/a564c52da70a') }); + + try { + await vscode.workspace.fs.copy(folder, someFolder, { overwrite: true }); + assert.ok(true); + } catch (err) { + assert.ok(false, err); + + } finally { + await vscode.workspace.fs.delete(folder, { recursive: true, useTrash: false }); + await vscode.workspace.fs.delete(someFolder, { recursive: true, useTrash: false }); + } + }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts index 1f7a9c93197..c831e6d4306 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts @@ -4,9 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, Task2 } from 'vscode'; +import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event } from 'vscode'; -suite('workspace-namespace', () => { +// Disable tasks tests: +// - Web https://github.com/microsoft/vscode/issues/90528 +((env.uiKind === UIKind.Web) ? suite.skip : suite)('vscode API - tasks', () => { suite('Tasks', () => { let disposables: Disposable[] = []; @@ -26,26 +28,55 @@ suite('workspace-namespace', () => { const taskType: string = 'customTesting'; const taskName = 'First custom task'; let isPseudoterminalClosed = false; + let terminal: Terminal | undefined; + // There's a strict order that should be observed here: + // 1. The terminal opens + // 2. The terminal is written to. + // 3. The terminal is closed. + enum TestOrder { + Start, + TerminalOpened, + TerminalWritten, + TerminalClosed + } + + let testOrder = TestOrder.Start; + disposables.push(window.onDidOpenTerminal(term => { - disposables.push(window.onDidWriteTerminalData(e => { - try { - assert.equal(e.data, 'testing\r\n'); - } catch (e) { - done(e); - } - disposables.push(window.onDidCloseTerminal(() => { - try { - // Pseudoterminal.close should have fired by now, additionally we want - // to make sure all events are flushed before continuing with more tests - assert.ok(isPseudoterminalClosed); - } catch (e) { - done(e); - return; - } - done(); - })); - term.dispose(); - })); + try { + assert.equal(testOrder, TestOrder.Start); + } catch (e) { + done(e); + } + testOrder = TestOrder.TerminalOpened; + terminal = term; + })); + disposables.push(window.onDidWriteTerminalData(e => { + try { + assert.equal(testOrder, TestOrder.TerminalOpened); + testOrder = TestOrder.TerminalWritten; + assert.notEqual(terminal, undefined); + assert.equal(e.data, 'testing\r\n'); + } catch (e) { + done(e); + } + + if (terminal) { + terminal.dispose(); + } + })); + disposables.push(window.onDidCloseTerminal(() => { + try { + assert.equal(testOrder, TestOrder.TerminalWritten); + testOrder = TestOrder.TerminalClosed; + // Pseudoterminal.close should have fired by now, additionally we want + // to make sure all events are flushed before continuing with more tests + assert.ok(isPseudoterminalClosed); + } catch (e) { + done(e); + return; + } + done(); })); disposables.push(tasks.registerTaskProvider(taskType, { provideTasks: () => { @@ -63,7 +94,7 @@ suite('workspace-namespace', () => { }; return Promise.resolve(pty); }); - const task = new Task2(kind, TaskScope.Workspace, taskName, taskType, execution); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); result.push(task); return result; }, @@ -116,11 +147,11 @@ suite('workspace-namespace', () => { writeEmitter.fire('exiting'); closeEmitter.fire(); }, - close: () => {} + close: () => { } }; return Promise.resolve(pty); }); - const task = new Task2(kind, TaskScope.Workspace, taskName, taskType, execution); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); result.push(task); return result; }, @@ -135,5 +166,100 @@ suite('workspace-namespace', () => { })); commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`); }); + + test('Execution from onDidEndTaskProcess and onDidStartTaskProcess are equal to original', () => { + return new Promise<void>(async (resolve) => { + const task = new Task({ type: 'testTask' }, TaskScope.Workspace, 'echo', 'testTask', new ShellExecution('echo', ['hello test'])); + let taskExecution: TaskExecution | undefined; + const executeDoneEvent: EventEmitter<void> = new EventEmitter(); + const taskExecutionShouldBeSet: Promise<void> = new Promise(resolve => { + const disposable = executeDoneEvent.event(() => { + resolve(); + disposable.dispose(); + }); + }); + let count = 2; + const progressMade: EventEmitter<void> = new EventEmitter(); + let startSucceeded = false; + let endSucceeded = false; + disposables.push(progressMade.event(() => { + count--; + if ((count === 0) && startSucceeded && endSucceeded) { + resolve(); + } + })); + + + disposables.push(tasks.onDidStartTaskProcess(async (e) => { + await taskExecutionShouldBeSet; + if (e.execution === taskExecution) { + startSucceeded = true; + progressMade.fire(); + } + })); + + disposables.push(tasks.onDidEndTaskProcess(async (e) => { + await taskExecutionShouldBeSet; + if (e.execution === taskExecution) { + endSucceeded = true; + progressMade.fire(); + } + })); + + taskExecution = await tasks.executeTask(task); + executeDoneEvent.fire(); + }); + }); + + // https://github.com/microsoft/vscode/issues/100577 + test('A CustomExecution task can be fetched and executed', () => { + return new Promise<void>(async (resolve, reject) => { + class CustomTerminal implements Pseudoterminal { + private readonly writeEmitter = new EventEmitter<string>(); + public readonly onDidWrite: Event<string> = this.writeEmitter.event; + public async close(): Promise<void> { } + private closeEmitter = new EventEmitter<void>(); + onDidClose: Event<void> = this.closeEmitter.event; + public open(): void { + this.closeEmitter.fire(); + resolve(); + } + } + + function buildTask(): Task { + const task = new Task( + { + type: 'customTesting', + }, + TaskScope.Workspace, + 'Test Task', + 'customTesting', + new CustomExecution( + async (): Promise<Pseudoterminal> => { + return new CustomTerminal(); + } + ) + ); + return task; + } + + disposables.push(tasks.registerTaskProvider('customTesting', { + provideTasks: () => { + return [buildTask()]; + }, + resolveTask(_task: Task): undefined { + return undefined; + } + })); + + const task = await tasks.fetchTasks({ type: 'customTesting' }); + + if (task && task.length > 0) { + await tasks.executeTask(task[0]); + } else { + reject('fetched task can\'t be undefined'); + } + }); + }); }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index abe883aa72e..ad1d70446d6 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -5,11 +5,12 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled } from '../utils'; +import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled, revertAllDirty } from '../utils'; import { join, posix, basename } from 'path'; import * as fs from 'fs'; +import { TestFS } from '../memfs'; -suite('workspace-namespace', () => { +suite('vscode API - workspace', () => { teardown(closeAllEditors); @@ -59,12 +60,19 @@ suite('workspace-namespace', () => { } }); - test('openTextDocument', () => { - let len = vscode.workspace.textDocuments.length; - return vscode.workspace.openTextDocument(join(vscode.workspace.rootPath || '', './simple.txt')).then(doc => { - assert.ok(doc); - assert.equal(vscode.workspace.textDocuments.length, len + 1); - }); + test('openTextDocument', async () => { + const uri = await createRandomFile(); + + // not yet there + const existing1 = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); + assert.equal(existing1, undefined); + + // open and assert its there + const doc = await vscode.workspace.openTextDocument(uri); + assert.ok(doc); + assert.equal(doc.uri.toString(), uri.toString()); + const existing2 = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); + assert.equal(existing2 === doc, true); }); test('openTextDocument, illegal path', () => { @@ -163,6 +171,40 @@ suite('workspace-namespace', () => { }); }); + test('openTextDocument, actual casing first', async function () { + + const fs = new TestFS('this-fs', false); + const reg = vscode.workspace.registerFileSystemProvider(fs.scheme, fs, { isCaseSensitive: fs.isCaseSensitive }); + + let uriOne = vscode.Uri.parse('this-fs:/one'); + let uriTwo = vscode.Uri.parse('this-fs:/two'); + let uriONE = vscode.Uri.parse('this-fs:/ONE'); // same resource, different uri + let uriTWO = vscode.Uri.parse('this-fs:/TWO'); + + fs.writeFile(uriOne, Buffer.from('one'), { create: true, overwrite: true }); + fs.writeFile(uriTwo, Buffer.from('two'), { create: true, overwrite: true }); + + // lower case (actual case) comes first + let docOne = await vscode.workspace.openTextDocument(uriOne); + assert.equal(docOne.uri.toString(), uriOne.toString()); + + let docONE = await vscode.workspace.openTextDocument(uriONE); + assert.equal(docONE === docOne, true); + assert.equal(docONE.uri.toString(), uriOne.toString()); + assert.equal(docONE.uri.toString() !== uriONE.toString(), true); // yep + + // upper case (NOT the actual case) comes first + let docTWO = await vscode.workspace.openTextDocument(uriTWO); + assert.equal(docTWO.uri.toString(), uriTWO.toString()); + + let docTwo = await vscode.workspace.openTextDocument(uriTwo); + assert.equal(docTWO === docTwo, true); + assert.equal(docTwo.uri.toString(), uriTWO.toString()); + assert.equal(docTwo.uri.toString() !== uriTwo.toString(), true); // yep + + reg.dispose(); + }); + test('eol, read', () => { const a = createRandomFile('foo\nbar\nbar').then(file => { return vscode.workspace.openTextDocument(file).then(doc => { @@ -214,99 +256,96 @@ suite('workspace-namespace', () => { }); }); - test('eol, change via onWillSave', () => { - + test('eol, change via onWillSave', async function () { let called = false; let sub = vscode.workspace.onWillSaveTextDocument(e => { called = true; e.waitUntil(Promise.resolve([vscode.TextEdit.setEndOfLine(vscode.EndOfLine.LF)])); }); - return createRandomFile('foo\r\nbar\r\nbar').then(file => { - return vscode.workspace.openTextDocument(file).then(doc => { - assert.equal(doc.eol, vscode.EndOfLine.CRLF); - const edit = new vscode.WorkspaceEdit(); - edit.set(file, [vscode.TextEdit.insert(new vscode.Position(0, 0), '-changes-')]); + const file = await createRandomFile('foo\r\nbar\r\nbar'); + const doc = await vscode.workspace.openTextDocument(file); + assert.equal(doc.eol, vscode.EndOfLine.CRLF); - return vscode.workspace.applyEdit(edit).then(success => { - assert.ok(success); - return doc.save(); + const edit = new vscode.WorkspaceEdit(); + edit.set(file, [vscode.TextEdit.insert(new vscode.Position(0, 0), '-changes-')]); + const successEdit = await vscode.workspace.applyEdit(edit); + assert.ok(successEdit); - }).then(success => { - assert.ok(success); - assert.ok(called); - assert.ok(!doc.isDirty); - assert.equal(doc.eol, vscode.EndOfLine.LF); - sub.dispose(); - }); - }); - }); + const successSave = await doc.save(); + assert.ok(successSave); + assert.ok(called); + assert.ok(!doc.isDirty); + assert.equal(doc.eol, vscode.EndOfLine.LF); + sub.dispose(); }); - test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', () => { - return createRandomFile().then(file => { - let disposables: vscode.Disposable[] = []; + function assertEqualPath(a: string, b: string): void { + assert.ok(pathEquals(a, b), `${a} <-> ${b}`); + } - let onDidOpenTextDocument = false; - disposables.push(vscode.workspace.onDidOpenTextDocument(e => { - assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); - onDidOpenTextDocument = true; - })); + test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', async () => { + const file = await createRandomFile(); + let disposables: vscode.Disposable[] = []; - let onDidChangeTextDocument = false; - disposables.push(vscode.workspace.onDidChangeTextDocument(e => { - assert.ok(pathEquals(e.document.uri.fsPath, file.fsPath)); - onDidChangeTextDocument = true; - })); + await revertAllDirty(); // needed for a clean state for `onDidSaveTextDocument` (#102365) - let onDidSaveTextDocument = false; - disposables.push(vscode.workspace.onDidSaveTextDocument(e => { - assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); - onDidSaveTextDocument = true; - })); + let pendingAsserts: Function[] = []; + let onDidOpenTextDocument = false; + disposables.push(vscode.workspace.onDidOpenTextDocument(e => { + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); + onDidOpenTextDocument = true; + })); - return vscode.workspace.openTextDocument(file).then(doc => { - return vscode.window.showTextDocument(doc).then((editor) => { - return editor.edit((builder) => { - builder.insert(new vscode.Position(0, 0), 'Hello World'); - }).then(_applied => { - return doc.save().then(_saved => { - assert.ok(onDidOpenTextDocument); - assert.ok(onDidChangeTextDocument); - assert.ok(onDidSaveTextDocument); + let onDidChangeTextDocument = false; + disposables.push(vscode.workspace.onDidChangeTextDocument(e => { + pendingAsserts.push(() => assertEqualPath(e.document.uri.fsPath, file.fsPath)); + onDidChangeTextDocument = true; + })); - disposeAll(disposables); + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); + onDidSaveTextDocument = true; + })); - return deleteFile(file); - }); - }); - }); - }); + const doc = await vscode.workspace.openTextDocument(file); + const editor = await vscode.window.showTextDocument(doc); + + await editor.edit((builder) => { + builder.insert(new vscode.Position(0, 0), 'Hello World'); }); + await doc.save(); + + assert.ok(onDidOpenTextDocument); + assert.ok(onDidChangeTextDocument); + assert.ok(onDidSaveTextDocument); + pendingAsserts.forEach(assert => assert()); + disposeAll(disposables); + return deleteFile(file); }); - test('events: onDidSaveTextDocument fires even for non dirty file when saved', () => { - return createRandomFile().then(file => { - let disposables: vscode.Disposable[] = []; + test('events: onDidSaveTextDocument fires even for non dirty file when saved', async () => { + const file = await createRandomFile(); + let disposables: vscode.Disposable[] = []; + let pendingAsserts: Function[] = []; - let onDidSaveTextDocument = false; - disposables.push(vscode.workspace.onDidSaveTextDocument(e => { - assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); - onDidSaveTextDocument = true; - })); + await revertAllDirty(); // needed for a clean state for `onDidSaveTextDocument` (#102365) - return vscode.workspace.openTextDocument(file).then(doc => { - return vscode.window.showTextDocument(doc).then(() => { - return vscode.commands.executeCommand('workbench.action.files.save').then(() => { - assert.ok(onDidSaveTextDocument); + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); + onDidSaveTextDocument = true; + })); - disposeAll(disposables); + const doc = await vscode.workspace.openTextDocument(file); + await vscode.window.showTextDocument(doc); + await vscode.commands.executeCommand('workbench.action.files.save'); - return deleteFile(file); - }); - }); - }); - }); + assert.ok(onDidSaveTextDocument); + pendingAsserts.forEach(fn => fn()); + disposeAll(disposables); + return deleteFile(file); }); test('openTextDocument, with selection', function () { @@ -497,7 +536,7 @@ suite('workspace-namespace', () => { assert.equal(callCount, 1); assert.equal(doc.getText(), 'call0'); - return new Promise(resolve => { + return new Promise<void>(resolve => { let subscription = vscode.workspace.onDidChangeTextDocument(event => { assert.ok(event.document === doc); @@ -512,7 +551,7 @@ suite('workspace-namespace', () => { }); test('findFiles', () => { - return vscode.workspace.findFiles('**/*.png').then((res) => { + return vscode.workspace.findFiles('**/image.png').then((res) => { assert.equal(res.length, 2); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); @@ -533,14 +572,14 @@ suite('workspace-namespace', () => { }); test('findFiles - exclude', () => { - return vscode.workspace.findFiles('**/*.png').then((res) => { + return vscode.workspace.findFiles('**/image.png').then((res) => { assert.equal(res.length, 2); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); }); test('findFiles, exclude', () => { - return vscode.workspace.findFiles('**/*.png', '**/sub/**').then((res) => { + return vscode.workspace.findFiles('**/image.png', '**/sub/**').then((res) => { assert.equal(res.length, 1); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); @@ -935,4 +974,44 @@ suite('workspace-namespace', () => { // const expected2 = 'import2;import1;'; assert.equal(document.getText(), expected); }); + + test('issue #107739 - Redo of rename Java Class name has no effect', async () => { + const file = await createRandomFile('hello'); + const fileName = basename(file.fsPath); + const newFile = vscode.Uri.parse(file.toString().replace(fileName, `${fileName}2`)); + + // apply edit + { + const we = new vscode.WorkspaceEdit(); + we.insert(file, new vscode.Position(0, 5), '2'); + we.renameFile(file, newFile); + await vscode.workspace.applyEdit(we); + } + + // show the new document + { + const document = await vscode.workspace.openTextDocument(newFile); + await vscode.window.showTextDocument(document); + assert.equal(document.getText(), 'hello2'); + assert.equal(document.isDirty, true); + } + + // undo and show the old document + { + await vscode.commands.executeCommand('undo'); + const document = await vscode.workspace.openTextDocument(file); + await vscode.window.showTextDocument(document); + assert.equal(document.getText(), 'hello'); + } + + // redo and show the new document + { + await vscode.commands.executeCommand('redo'); + const document = await vscode.workspace.openTextDocument(newFile); + await vscode.window.showTextDocument(document); + assert.equal(document.getText(), 'hello2'); + assert.equal(document.isDirty, true); + } + + }); }); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index e270cd73adf..392d0be8461 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { MemFS } from './memfs'; +import { TestFS } from './memfs'; import * as assert from 'assert'; export function rndName() { return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); } -export const testFs = new MemFS(); -vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs); +export const testFs = new TestFS('fake-fs', true); +vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensitive: testFs.isCaseSensitive }); export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, ext = ''): Promise<vscode.Uri> { let fakeFile: vscode.Uri; @@ -22,13 +22,13 @@ export async function createRandomFile(contents = '', dir: vscode.Uri | undefine } else { fakeFile = vscode.Uri.parse(`${testFs.scheme}:/${rndName() + ext}`); } - await testFs.writeFile(fakeFile, Buffer.from(contents), { create: true, overwrite: true }); + testFs.writeFile(fakeFile, Buffer.from(contents), { create: true, overwrite: true }); return fakeFile; } export async function deleteFile(file: vscode.Uri): Promise<boolean> { try { - await testFs.delete(file); + testFs.delete(file); return true; } catch { return false; @@ -48,25 +48,14 @@ export function closeAllEditors(): Thenable<any> { return vscode.commands.executeCommand('workbench.action.closeAllEditors'); } +export async function revertAllDirty(): Promise<void> { + return vscode.commands.executeCommand('_workbench.revertAllDirty'); +} + export function disposeAll(disposables: vscode.Disposable[]) { vscode.Disposable.from(...disposables).dispose(); } -export function conditionalTest(name: string, testCallback: (done: MochaDone) => void | Thenable<any>) { - if (isTestTypeActive()) { - const async = !!testCallback.length; - if (async) { - test(name, (done) => testCallback(done)); - } else { - test(name, () => (<() => void | Thenable<any>>testCallback)()); - } - } -} - -function isTestTypeActive(): boolean { - return !!vscode.extensions.getExtension('vscode-resolver-test'); -} - export function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/extensions/vscode-api-tests/src/workspace-tests/index.ts b/extensions/vscode-api-tests/src/workspace-tests/index.ts index dfef493b2ab..9486d8ed3e5 100644 --- a/extensions/vscode-api-tests/src/workspace-tests/index.ts +++ b/extensions/vscode-api-tests/src/workspace-tests/index.ts @@ -6,21 +6,31 @@ const path = require('path'); const testRunner = require('vscode/lib/testrunner'); -const suite = 'Integration Workspace Tests'; - const options: any = { ui: 'tdd', useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Workspace Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Workspace Tests'; +} else { + suite = 'Integration Workspace Tests'; +} + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { options.reporter = 'mocha-multi-reporters'; options.reporterOptions = { reporterEnabled: 'spec, mocha-junit-reporter', mochaJunitReporterReporterOptions: { testsuitesTitle: `${suite} ${process.platform}`, - mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) } }; } diff --git a/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts b/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts index f018f581c42..1b4ef88325a 100644 --- a/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts @@ -8,7 +8,7 @@ import * as vscode from 'vscode'; import { closeAllEditors, pathEquals } from '../utils'; import { join } from 'path'; -suite('workspace-namespace', () => { +suite('vscode API - workspace', () => { teardown(closeAllEditors); diff --git a/extensions/vscode-api-tests/testWorkspace/.vscode/launch.json b/extensions/vscode-api-tests/testWorkspace/.vscode/launch.json new file mode 100644 index 00000000000..518e00c6724 --- /dev/null +++ b/extensions/vscode-api-tests/testWorkspace/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // 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": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch debug.js", + "stopOnEntry": true, + "program": "${workspaceFolder}/debug.js" + } + ] +} diff --git a/src/vs/editor/contrib/clipboard/clipboard.css b/extensions/vscode-api-tests/testWorkspace/debug.js similarity index 79% rename from src/vs/editor/contrib/clipboard/clipboard.css rename to extensions/vscode-api-tests/testWorkspace/debug.js index 7f14a01f057..1ca212281c7 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.css +++ b/extensions/vscode-api-tests/testWorkspace/debug.js @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-menu .monaco-action-bar.vertical .action-label.hover { - background-color: #EEE; -} \ No newline at end of file +let y = 0; +for (let i = 0; i < 100; i++) { + console.log(y); + y = y + i; +} diff --git a/extensions/vscode-api-tests/testWorkspace/image%.png b/extensions/vscode-api-tests/testWorkspace/image%.png new file mode 100644 index 00000000000..15b462975be Binary files /dev/null and b/extensions/vscode-api-tests/testWorkspace/image%.png differ diff --git a/extensions/vscode-api-tests/testWorkspace/image%02.png b/extensions/vscode-api-tests/testWorkspace/image%02.png new file mode 100644 index 00000000000..15b462975be Binary files /dev/null and b/extensions/vscode-api-tests/testWorkspace/image%02.png differ diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index 152a4c91595..c976dbc8d0f 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -1,57 +1,59 @@ { - "name": "vscode-colorize-tests", - "description": "Colorize tests for VS Code", - "version": "0.0.1", - "publisher": "vscode", - "license": "MIT", - "private": true, - "activationEvents": [ - "onLanguage:json" - ], - "main": "./out/colorizerTestMain", - "enableProposedApi": true, - "engines": { - "vscode": "*" - }, - "scripts": { - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" - }, - "dependencies": { - "jsonc-parser": "2.2.0" - }, - "devDependencies": { - "@types/node": "^12.11.7", - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7", - "vscode": "1.1.5" - }, - "contributes": { - "tokenTypes": [ - { - "id": "testToken", - "description": "A test token" - } - ], - "tokenModifiers": [ - { - "id": "testModifier", - "description": "A test modifier" - } - ], - "tokenStyleDefaults": [ - { - "selector": "testToken.testModifier", - "light": { - "fontStyle": "bold" - }, - "dark": { - "fontStyle": "bold" - }, - "highContrast": { - "fontStyle": "bold" - } - - } - ] - } + "name": "vscode-colorize-tests", + "description": "Colorize tests for VS Code", + "version": "0.0.1", + "publisher": "vscode", + "license": "MIT", + "private": true, + "activationEvents": [ + "onLanguage:json" + ], + "main": "./out/colorizerTestMain", + "enableProposedApi": true, + "engines": { + "vscode": "*" + }, + "scripts": { + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" + }, + "dependencies": { + "jsonc-parser": "2.2.1" + }, + "devDependencies": { + "@types/node": "^12.11.7", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "vscode": "1.1.5" + }, + "contributes": { + "semanticTokenTypes": [ + { + "id": "testToken", + "description": "A test token" + } + ], + "semanticTokenModifiers": [ + { + "id": "testModifier", + "description": "A test modifier" + } + ], + "semanticTokenScopes": [ + { + "scopes": { + "testToken": [ + "entity.name.function.special" + ] + } + } + ], + "productIconThemes": [ + { + "id": "Test Product Icons", + "label": "The Test Product Icon Theme", + "path": "./producticons/test-product-icon-theme.json", + "_watch": true + } + ] + } } diff --git a/extensions/vscode-colorize-tests/producticons/ElegantIcons.woff b/extensions/vscode-colorize-tests/producticons/ElegantIcons.woff new file mode 100644 index 00000000000..393305253e5 Binary files /dev/null and b/extensions/vscode-colorize-tests/producticons/ElegantIcons.woff differ diff --git a/extensions/vscode-colorize-tests/producticons/index.html b/extensions/vscode-colorize-tests/producticons/index.html new file mode 100644 index 00000000000..0d34ddedb57 --- /dev/null +++ b/extensions/vscode-colorize-tests/producticons/index.html @@ -0,0 +1,3049 @@ +<!doctype html> +<html> + +<head> + <title>Your Font/Glyphs + + + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+

Class Names

+
+ + +  arrow_up + + + +  arrow_down + + + +  arrow_left + + + +  arrow_right + + + +  arrow_left-up + + + +  arrow_right-up + + + +  arrow_right-down + + + +  arrow_left-down + + + +  arrow-up-down + + + +  arrow_up-down_alt + + + +  arrow_left-right_alt + + + +  arrow_left-right + + + +  arrow_expand_alt2 + + + +  arrow_expand_alt + + + +  arrow_condense + + + +  arrow_expand + + + +  arrow_move + + + +  arrow_carrot-up + + + +  arrow_carrot-down + + + +  arrow_carrot-left + + + +  arrow_carrot-right + + + +  arrow_carrot-2up + + + +  arrow_carrot-2down + + + +  arrow_carrot-2left + + + +  arrow_carrot-2right + + + +  arrow_carrot-up_alt2 + + + +  arrow_carrot-down_alt2 + + + +  arrow_carrot-left_alt2 + + + +  arrow_carrot-right_alt2 + + + +  arrow_carrot-2up_alt2 + + + +  arrow_carrot-2down_alt2 + + + +  arrow_carrot-2left_alt2 + + + +  arrow_carrot-2right_alt2 + + + +  arrow_triangle-up + + + +  arrow_triangle-down + + + +  arrow_triangle-left + + + +  arrow_triangle-right + + + +  arrow_triangle-up_alt2 + + + +  arrow_triangle-down_alt2 + + + +  arrow_triangle-left_alt2 + + + +  arrow_triangle-right_alt2 + + + +  arrow_back + + + +  icon_minus-06 + + + +  icon_plus + + + +  icon_close + + + +  icon_check + + + +  icon_minus_alt2 + + + +  icon_plus_alt2 + + + +  icon_close_alt2 + + + +  icon_check_alt2 + + + +  icon_zoom-out_alt + + + +  icon_zoom-in_alt + + + +  icon_search + + + +  icon_box-empty + + + +  icon_box-selected + + + +  icon_minus-box + + + +  icon_plus-box + + + +  icon_box-checked + + + +  icon_circle-empty + + + +  icon_circle-slelected + + + +  icon_stop_alt2 + + + +  icon_stop + + + +  icon_pause_alt2 + + + +  icon_pause + + + +  icon_menu + + + +  icon_menu-square_alt2 + + + +  icon_menu-circle_alt2 + + + +  icon_ul + + + +  icon_ol + + + +  icon_adjust-horiz + + + +  icon_adjust-vert + + + +  icon_document_alt + + + +  icon_documents_alt + + + +  icon_pencil + + + +  icon_pencil-edit_alt + + + +  icon_pencil-edit + + + +  icon_folder-alt + + + +  icon_folder-open_alt + + + +  icon_folder-add_alt + + + +  icon_info_alt + + + +  icon_error-oct_alt + + + +  icon_error-circle_alt + + + +  icon_error-triangle_alt + + + +  icon_question_alt2 + + + +  icon_question + + + +  icon_comment_alt + + + +  icon_chat_alt + + + +  icon_vol-mute_alt + + + +  icon_volume-low_alt + + + +  icon_volume-high_alt + + + +  icon_quotations + + + +  icon_quotations_alt2 + + + +  icon_clock_alt + + + +  icon_lock_alt + + + +  icon_lock-open_alt + + + +  icon_key_alt + + + +  icon_cloud_alt + + + +  icon_cloud-upload_alt + + + +  icon_cloud-download_alt + + + +  icon_image + + + +  icon_images + + + +  icon_lightbulb_alt + + + +  icon_gift_alt + + + +  icon_house_alt + + + +  icon_genius + + + +  icon_mobile + + + +  icon_tablet + + + +  icon_laptop + + + +  icon_desktop + + + +  icon_camera_alt + + + +  icon_mail_alt + + + +  icon_cone_alt + + + +  icon_ribbon_alt + + + +  icon_bag_alt + + + +  icon_creditcard + + + +  icon_cart_alt + + + +  icon_paperclip + + + +  icon_tag_alt + + + +  icon_tags_alt + + + +  icon_trash_alt + + + +  icon_cursor_alt + + + +  icon_mic_alt + + + +  icon_compass_alt + + + +  icon_pin_alt + + + +  icon_pushpin_alt + + + +  icon_map_alt + + + +  icon_drawer_alt + + + +  icon_toolbox_alt + + + +  icon_book_alt + + + +  icon_calendar + + + +  icon_film + + + +  icon_table + + + +  icon_contacts_alt + + + +  icon_headphones + + + +  icon_lifesaver + + + +  icon_piechart + + + +  icon_refresh + + + +  icon_link_alt + + + +  icon_link + + + +  icon_loading + + + +  icon_blocked + + + +  icon_archive_alt + + + +  icon_heart_alt + + +
+ + + +  icon_printer + + + +  icon_calulator + + + +  icon_building + + + +  icon_floppy + + + +  icon_drive + + + +  icon_search-2 + + + +  icon_id + + + +  icon_id-2 + + + +  icon_puzzle + + + +  icon_like + + + +  icon_dislike + + + +  icon_mug + + + +  icon_currency + + + +  icon_wallet + + + +  icon_pens + + + +  icon_easel + + + +  icon_flowchart + + + +  icon_datareport + + + +  icon_briefcase + + + +  icon_shield + + + +  icon_percent + + + +  icon_globe + + + +  icon_globe-2 + + + +  icon_target + + + +  icon_hourglass + + + +  icon_balance + + +
+ + + +  icon_star_alt + + + +  icon_star-half_alt + + + +  icon_star + + + +  icon_star-half + + + +  icon_tools + + + +  icon_tool + + + +  icon_cog + + + +  icon_cogs + + + +  arrow_up_alt + + + +  arrow_down_alt + + + +  arrow_left_alt + + + +  arrow_right_alt + + + +  arrow_left-up_alt + + + +  arrow_right-up_alt + + + +  arrow_right-down_alt + + + +  arrow_left-down_alt + + + +  arrow_condense_alt + + + +  arrow_expand_alt3 + + + +  arrow_carrot_up_alt + + + +  arrow_carrot-down_alt + + + +  arrow_carrot-left_alt + + + +  arrow_carrot-right_alt + + + +  arrow_carrot-2up_alt + + + +  arrow_carrot-2dwnn_alt + + + +  arrow_carrot-2left_alt + + + +  arrow_carrot-2right_alt + + + +  arrow_triangle-up_alt + + + +  arrow_triangle-down_alt + + + +  arrow_triangle-left_alt + + + +  arrow_triangle-right_alt + + + +  icon_minus_alt + + + +  icon_plus_alt + + + +  icon_close_alt + + + +  icon_check_alt + + + +  icon_zoom-out + + + +  icon_zoom-in + + + +  icon_stop_alt + + + +  icon_menu-square_alt + + + +  icon_menu-circle_alt + + + +  icon_document + + + +  icon_documents + + + +  icon_pencil_alt + + + +  icon_folder + + + +  icon_folder-open + + + +  icon_folder-add + + + +  icon_folder_upload + + + +  icon_folder_download + + + +  icon_info + + + +  icon_error-circle + + + +  icon_error-oct + + + +  icon_error-triangle + + + +  icon_question_alt + + + +  icon_comment + + + +  icon_chat + + + +  icon_vol-mute + + + +  icon_volume-low + + + +  icon_volume-high + + + +  icon_quotations_alt + + + +  icon_clock + + + +  icon_lock + + + +  icon_lock-open + + + +  icon_key + + + +  icon_cloud + + + +  icon_cloud-upload + + + +  icon_cloud-download + + + +  icon_lightbulb + + + +  icon_gift + + + +  icon_house + + + +  icon_camera + + + +  icon_mail + + + +  icon_cone + + + +  icon_ribbon + + + +  icon_bag + + + +  icon_cart + + + +  icon_tag + + + +  icon_tags + + + +  icon_trash + + + +  icon_cursor + + + +  icon_mic + + + +  icon_compass + + + +  icon_pin + + + +  icon_pushpin + + + +  icon_map + + + +  icon_drawer + + + +  icon_toolbox + + + +  icon_book + + + +  icon_contacts + + + +  icon_archive + + + +  icon_heart + + + +  icon_profile + + + +  icon_group + + + +  icon_grid-2x2 + + + +  icon_grid-3x3 + + + +  icon_music + + + +  icon_pause_alt + + + +  icon_phone + + + +  icon_upload + + + +  icon_download + + + +  icon_rook + + +
+ + + +  icon_printer-alt + + + +  icon_calculator_alt + + + +  icon_building_alt + + + +  icon_floppy_alt + + + +  icon_drive_alt + + + +  icon_search_alt + + + +  icon_id_alt + + + +  icon_id-2_alt + + + +  icon_puzzle_alt + + + +  icon_like_alt + + + +  icon_dislike_alt + + + +  icon_mug_alt + + + +  icon_currency_alt + + + +  icon_wallet_alt + + + +  icon_pens_alt + + + +  icon_easel_alt + + + +  icon_flowchart_alt + + + +  icon_datareport_alt + + + +  icon_briefcase_alt + + + +  icon_shield_alt + + + +  icon_percent_alt + + + +  icon_globe_alt + + + +  icon_clipboard + + +
+ + + +  social_facebook + + + +  social_twitter + + + +  social_pinterest + + + +  social_googleplus + + + +  social_tumblr + + + +  social_tumbleupon + + + +  social_wordpress + + + +  social_instagram + + + +  social_dribbble + + + +  social_vimeo + + + +  social_linkedin + + + +  social_rss + + + +  social_deviantart + + + +  social_share + + + +  social_myspace + + + +  social_skype + + + +  social_youtube + + + +  social_picassa + + + +  social_googledrive + + + +  social_flickr + + + +  social_blogger + + + +  social_spotify + + + +  social_delicious + + + +  social_facebook_circle + + + +  social_twitter_circle + + + +  social_pinterest_circle + + + +  social_googleplus_circle + + + +  social_tumblr_circle + + + +  social_stumbleupon_circle + + + +  social_wordpress_circle + + + +  social_instagram_circle + + + +  social_dribbble_circle + + + +  social_vimeo_circle + + + +  social_linkedin_circle + + + +  social_rss_circle + + + +  social_deviantart_circle + + + +  social_share_circle + + + +  social_myspace_circle + + + +  social_skype_circle + + + +  social_youtube_circle + + + +  social_picassa_circle + + + +  social_googledrive_alt2 + + + +  social_flickr_circle + + + +  social_blogger_circle + + + +  social_spotify_circle + + + +  social_delicious_circle + + + +  social_facebook_square + + + +  social_twitter_square + + + +  social_pinterest_square + + + +  social_googleplus_square + + + +  social_tumblr_square + + + +  social_stumbleupon_square + + + +  social_wordpress_square + + + +  social_instagram_square + + + +  social_dribbble_square + + + +  social_vimeo_square + + + +  social_linkedin_square + + + +  social_rss_square + + + +  social_deviantart_square + + + +  social_share_square + + + +  social_myspace_square + + + +  social_skype_square + + + +  social_youtube_square + + + +  social_picassa_square + + + +  social_googledrive_square + + + +  social_flickr_square + + + +  social_blogger_square + + + +  social_spotify_square + + + +  social_delicious_square + +
+ +
+ + + + diff --git a/src/vs/editor/common/standalone/promise-polyfill/polyfill.license.txt b/extensions/vscode-colorize-tests/producticons/mit_license.txt similarity index 92% rename from src/vs/editor/common/standalone/promise-polyfill/polyfill.license.txt rename to extensions/vscode-colorize-tests/producticons/mit_license.txt index 6f7c0123162..effefee5f0c 100644 --- a/src/vs/editor/common/standalone/promise-polyfill/polyfill.license.txt +++ b/extensions/vscode-colorize-tests/producticons/mit_license.txt @@ -1,5 +1,6 @@ -Copyright (c) 2014 Taylor Hakes -Copyright (c) 2014 Forbes Lindesay +The MIT License (MIT) + +Copyright (c) <2013> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -17,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +THE SOFTWARE. \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/producticons/test-product-icon-theme.json b/extensions/vscode-colorize-tests/producticons/test-product-icon-theme.json new file mode 100644 index 00000000000..dc076aef5f9 --- /dev/null +++ b/extensions/vscode-colorize-tests/producticons/test-product-icon-theme.json @@ -0,0 +1,43 @@ +{ + // ElegantIcons from https://www.elegantthemes.com/icons/elegant_font.zip + "fonts": [ + { + "id": "elegant", + "src": [ + { + "path": "./ElegantIcons.woff", + "format": "woff" + } + ], + "weight": "normal", + "style": "normal", + } + ], + "iconDefinitions": { + "chevron-down": { + "fontCharacter": "\\43", + }, + "chevron-right": { + "fontCharacter": "\\45" + }, + "error": { + "fontCharacter": "\\e062" + }, + "warning": { + "fontCharacter": "\\e063" + }, + "settings-gear": { + "fontCharacter": "\\e030" + }, + "files": { + "fontCharacter": "\\e056" + }, + "extensions": { + "fontCharacter": "\\e015" + }, + "debug-alt-2": { + "fontCharacter": "\\e072" + } + + } +} diff --git a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts index a014275a7e6..450e7b4874b 100644 --- a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts +++ b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts @@ -22,24 +22,31 @@ export function activate(context: vscode.ExtensionContext): any { function addToken(value: string, startLine: number, startCharacter: number, length: number) { const [type, ...modifiers] = value.split('.'); + const selectedModifiers = []; + let tokenType = legend.tokenTypes.indexOf(type); if (tokenType === -1) { - return; - } - - let tokenModifiers = 0; - for (let i = 0; i < modifiers.length; i++) { - const index = legend.tokenModifiers.indexOf(modifiers[i]); - if (index !== -1) { - tokenModifiers = tokenModifiers | 1 << index; + if (type === 'notInLegend') { + tokenType = tokenTypes.length + 2; + } else { + return; } } - + let tokenModifiers = 0; + for (const modifier of modifiers) { + const index = legend.tokenModifiers.indexOf(modifier); + if (index !== -1) { + tokenModifiers = tokenModifiers | 1 << index; + selectedModifiers.push(modifier); + } else if (modifier === 'notInLegend') { + tokenModifiers = tokenModifiers | 1 << (legend.tokenModifiers.length + 2); + selectedModifiers.push(modifier); + } + } builder.push(startLine, startCharacter, length, tokenType, tokenModifiers); - const selectedModifiers = legend.tokenModifiers.filter((_val, bit) => tokenModifiers & (1 << bit)).join(' '); - outputChannel.appendLine(`line: ${startLine}, character: ${startCharacter}, length ${length}, ${legend.tokenTypes[tokenType]} (${tokenType}), ${selectedModifiers} ${tokenModifiers.toString(2)}`); + outputChannel.appendLine(`line: ${startLine}, character: ${startCharacter}, length ${length}, ${type} (${tokenType}), ${selectedModifiers} ${tokenModifiers.toString(2)}`); } outputChannel.appendLine('---'); @@ -56,7 +63,7 @@ export function activate(context: vscode.ExtensionContext): any { }; jsoncParser.visit(document.getText(), visitor); - return new vscode.SemanticTokens(builder.build()); + return builder.build(); } }; diff --git a/extensions/vscode-colorize-tests/src/index.ts b/extensions/vscode-colorize-tests/src/index.ts index a315ee36112..691ba5c6f07 100644 --- a/extensions/vscode-colorize-tests/src/index.ts +++ b/extensions/vscode-colorize-tests/src/index.ts @@ -20,7 +20,7 @@ if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { reporterEnabled: 'spec, mocha-junit-reporter', mochaJunitReporterReporterOptions: { testsuitesTitle: `${suite} ${process.platform}`, - mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) } }; } diff --git a/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json b/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json index b250b5d2bc1..1a2eeaf4408 100644 --- a/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json +++ b/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json @@ -4,6 +4,6 @@ "variable.declaration", "parameterNames", "function.member.declaration", "interface.declaration", - "function.member.declaration", "testToken.testModifier" + "function.member.declaration", "function.notInLegend" ] diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index c6b3fdb4313..b8a66d65eff 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -1042,10 +1042,10 @@ json3@3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= -jsonc-parser@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" - integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== +jsonc-parser@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== jsonify@~0.0.0: version "0.0.0" diff --git a/extensions/vscode-custom-editor-tests/customEditorMedia/textEditor.js b/extensions/vscode-custom-editor-tests/customEditorMedia/textEditor.js new file mode 100644 index 00000000000..8978b201c9f --- /dev/null +++ b/extensions/vscode-custom-editor-tests/customEditorMedia/textEditor.js @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// @ts-check +(function () { + // @ts-ignore + const vscode = acquireVsCodeApi(); + + const textArea = document.querySelector('textarea'); + + const initialState = vscode.getState(); + if (initialState) { + textArea.value = initialState.value; + } + + window.addEventListener('message', e => { + switch (e.data.type) { + case 'fakeInput': + { + const value = e.data.value; + textArea.value = value; + onInput(); + break; + } + + case 'setValue': + { + const value = e.data.value; + textArea.value = value; + vscode.setState({ value }); + + vscode.postMessage({ + type: 'didChangeContent', + value: value + }); + break; + } + } + }); + + const onInput = () => { + const value = textArea.value; + vscode.setState({ value }); + vscode.postMessage({ + type: 'edit', + value: value + }); + vscode.postMessage({ + type: 'didChangeContent', + value: value + }); + }; + + textArea.addEventListener('input', onInput); +}()); diff --git a/extensions/vscode-custom-editor-tests/package.json b/extensions/vscode-custom-editor-tests/package.json new file mode 100644 index 00000000000..08b6702b80a --- /dev/null +++ b/extensions/vscode-custom-editor-tests/package.json @@ -0,0 +1,44 @@ +{ + "name": "vscode-custom-editor-tests", + "description": "Custom editor tests for VS Code", + "version": "0.0.1", + "publisher": "vscode", + "license": "MIT", + "private": true, + "activationEvents": [ + "onCustomEditor:testWebviewEditor.abc" + ], + "main": "./out/extension", + "enableProposedApi": true, + "engines": { + "vscode": "^1.48.0" + }, + "scripts": { + "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-notebook-tests ./tsconfig.json" + }, + "dependencies": { + "p-limit": "^3.0.2" + }, + "devDependencies": { + "@types/node": "^12.11.7", + "@types/p-limit": "^2.2.0", + "mocha": "^2.3.3", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "vscode": "^1.1.36" + }, + "contributes": { + "customEditors": [ + { + "viewType": "testWebviewEditor.abc", + "displayName": "Test ABC editor", + "selector": [ + { + "filenamePattern": "*.abc" + } + ] + } + ] + } +} diff --git a/extensions/vscode-custom-editor-tests/src/customTextEditor.ts b/extensions/vscode-custom-editor-tests/src/customTextEditor.ts new file mode 100644 index 00000000000..cced14b9401 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/customTextEditor.ts @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as pLimit from 'p-limit'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { Disposable } from './dispose'; + +export namespace Testing { + export const abcEditorContentChangeCommand = '_abcEditor.contentChange'; + export const abcEditorTypeCommand = '_abcEditor.type'; + + export interface CustomEditorContentChangeEvent { + readonly content: string; + readonly source: vscode.Uri; + } +} + +export class AbcTextEditorProvider implements vscode.CustomTextEditorProvider { + + public static readonly viewType = 'testWebviewEditor.abc'; + + private activeEditor?: AbcEditor; + + public constructor( + private readonly context: vscode.ExtensionContext, + ) { } + + public register(): vscode.Disposable { + const provider = vscode.window.registerCustomEditorProvider(AbcTextEditorProvider.viewType, this); + + const commands: vscode.Disposable[] = []; + commands.push(vscode.commands.registerCommand(Testing.abcEditorTypeCommand, (content: string) => { + this.activeEditor?.testing_fakeInput(content); + })); + + return vscode.Disposable.from(provider, ...commands); + } + + public async resolveCustomTextEditor(document: vscode.TextDocument, panel: vscode.WebviewPanel) { + const editor = new AbcEditor(document, this.context.extensionPath, panel); + + this.activeEditor = editor; + + panel.onDidChangeViewState(({ webviewPanel }) => { + if (this.activeEditor === editor && !webviewPanel.active) { + this.activeEditor = undefined; + } + if (webviewPanel.active) { + this.activeEditor = editor; + } + }); + } +} + +class AbcEditor extends Disposable { + + public readonly _onDispose = this._register(new vscode.EventEmitter()); + public readonly onDispose = this._onDispose.event; + + private readonly limit = pLimit(1); + private syncedVersion: number = -1; + private currentWorkspaceEdit?: Thenable; + + constructor( + private readonly document: vscode.TextDocument, + private readonly _extensionPath: string, + private readonly panel: vscode.WebviewPanel, + ) { + super(); + + panel.webview.options = { + enableScripts: true, + }; + panel.webview.html = this.html; + + this._register(vscode.workspace.onDidChangeTextDocument(e => { + if (e.document === this.document) { + this.update(); + } + })); + + this._register(panel.webview.onDidReceiveMessage(message => { + switch (message.type) { + case 'edit': + this.doEdit(message.value); + break; + + case 'didChangeContent': + vscode.commands.executeCommand(Testing.abcEditorContentChangeCommand, { + content: message.value, + source: document.uri, + } as Testing.CustomEditorContentChangeEvent); + break; + } + })); + + this._register(panel.onDidDispose(() => { this.dispose(); })); + + this.update(); + } + + public testing_fakeInput(value: string) { + this.panel.webview.postMessage({ + type: 'fakeInput', + value: value, + }); + } + + private async doEdit(value: string) { + const edit = new vscode.WorkspaceEdit(); + edit.replace(this.document.uri, this.document.validateRange(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(999999, 999999))), value); + this.limit(() => { + this.currentWorkspaceEdit = vscode.workspace.applyEdit(edit).then(() => { + this.syncedVersion = this.document.version; + this.currentWorkspaceEdit = undefined; + }); + return this.currentWorkspaceEdit; + }); + } + + public dispose() { + if (this.isDisposed) { + return; + } + + this._onDispose.fire(); + super.dispose(); + } + + private get html() { + const contentRoot = path.join(this._extensionPath, 'customEditorMedia'); + const scriptUri = vscode.Uri.file(path.join(contentRoot, 'textEditor.js')); + const nonce = Date.now() + ''; + return /* html */` + + + + + + Document + + + + + + `; + } + + public async update() { + await this.currentWorkspaceEdit; + + if (this.isDisposed || this.syncedVersion >= this.document.version) { + return; + } + + this.panel.webview.postMessage({ + type: 'setValue', + value: this.document.getText(), + }); + this.syncedVersion = this.document.version; + } +} diff --git a/extensions/vscode-custom-editor-tests/src/dispose.ts b/extensions/vscode-custom-editor-tests/src/dispose.ts new file mode 100644 index 00000000000..548094c28e5 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/dispose.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function disposeAll(disposables: vscode.Disposable[]) { + while (disposables.length) { + const item = disposables.pop(); + if (item) { + item.dispose(); + } + } +} + +export abstract class Disposable { + private _isDisposed = false; + + protected _disposables: vscode.Disposable[] = []; + + public dispose(): any { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + disposeAll(this._disposables); + } + + protected _register(value: T): T { + if (this._isDisposed) { + value.dispose(); + } else { + this._disposables.push(value); + } + return value; + } + + protected get isDisposed() { + return this._isDisposed; + } +} \ No newline at end of file diff --git a/extensions/vscode-custom-editor-tests/src/extension.ts b/extensions/vscode-custom-editor-tests/src/extension.ts new file mode 100644 index 00000000000..0a83f97fce2 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/extension.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { AbcTextEditorProvider } from './customTextEditor'; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push(new AbcTextEditorProvider(context).register()); +} diff --git a/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts b/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts new file mode 100644 index 00000000000..d66ab665b8e --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts @@ -0,0 +1,314 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { Testing } from '../customTextEditor'; +import { closeAllEditors, delay, disposeAll, randomFilePath } from './utils'; + +assert.ok(vscode.workspace.rootPath); +const testWorkspaceRoot = vscode.Uri.file(path.join(vscode.workspace.rootPath!, 'customEditors')); + +const commands = Object.freeze({ + open: 'vscode.open', + openWith: 'vscode.openWith', + save: 'workbench.action.files.save', + undo: 'undo', +}); + +async function writeRandomFile(options: { ext: string; contents: string; }): Promise { + const fakeFile = randomFilePath({ root: testWorkspaceRoot, ext: options.ext }); + await fs.promises.writeFile(fakeFile.fsPath, Buffer.from(options.contents)); + return fakeFile; +} + +const disposables: vscode.Disposable[] = []; +function _register(disposable: T) { + disposables.push(disposable); + return disposable; +} + +class CustomEditorUpdateListener { + + public static create() { + return _register(new CustomEditorUpdateListener()); + } + + private readonly commandSubscription: vscode.Disposable; + + private readonly unconsumedResponses: Array = []; + private readonly callbackQueue: Array<(data: Testing.CustomEditorContentChangeEvent) => void> = []; + + private constructor() { + this.commandSubscription = vscode.commands.registerCommand(Testing.abcEditorContentChangeCommand, (data: Testing.CustomEditorContentChangeEvent) => { + if (this.callbackQueue.length) { + const callback = this.callbackQueue.shift(); + assert.ok(callback); + callback!(data); + } else { + this.unconsumedResponses.push(data); + } + }); + } + + dispose() { + this.commandSubscription.dispose(); + } + + async nextResponse(): Promise { + if (this.unconsumedResponses.length) { + return this.unconsumedResponses.shift()!; + } + + return new Promise(resolve => { + this.callbackQueue.push(resolve); + }); + } +} + + +suite('CustomEditor tests', () => { + setup(async () => { + await closeAllEditors(); + await resetTestWorkspace(); + }); + + teardown(async () => { + await closeAllEditors(); + disposeAll(disposables); + await resetTestWorkspace(); + }); + + test('Should load basic content from disk', async () => { + const startingContent = `load, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + }); + + test('Should support basic edits', async () => { + const startingContent = `basic edit, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `basic edit test`; + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + }); + + test('Should support single undo', async () => { + const startingContent = `single undo, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `undo test`; + { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + } + await delay(100); + { + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + } + }); + + test('Should support multiple undo', async () => { + const startingContent = `multiple undo, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const count = 10; + + // Make edits + for (let i = 0; i < count; ++i) { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, `${i}`); + const { content } = await listener.nextResponse(); + assert.equal(`${i}`, content); + } + + // Then undo them in order + for (let i = count - 1; i; --i) { + await delay(100); + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(`${i - 1}`, content); + } + + { + await delay(100); + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + } + }); + + test('Should update custom editor on file move', async () => { + const startingContent = `file move, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newFileName = vscode.Uri.file(path.join(testWorkspaceRoot.fsPath, 'y.abc')); + + const edit = new vscode.WorkspaceEdit(); + edit.renameFile(testDocument, newFileName); + + await vscode.workspace.applyEdit(edit); + + const response = (await listener.nextResponse()); + assert.equal(response.content, startingContent); + assert.equal(response.source.toString(), newFileName.toString()); + }); + + test('Should support saving custom editors', async () => { + const startingContent = `save, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `save, new`; + { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + } + { + await vscode.commands.executeCommand(commands.save); + const fileContent = (await fs.promises.readFile(testDocument.fsPath)).toString(); + assert.equal(fileContent, newContent); + } + }); + + test('Should undo after saving custom editor', async () => { + const startingContent = `undo after save, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `undo after save, new`; + { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + } + { + await vscode.commands.executeCommand(commands.save); + const fileContent = (await fs.promises.readFile(testDocument.fsPath)).toString(); + assert.equal(fileContent, newContent); + } + await delay(100); + { + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + } + }); + + test.skip('Should support untitled custom editors', async () => { + const listener = CustomEditorUpdateListener.create(); + + const untitledFile = randomFilePath({ root: testWorkspaceRoot, ext: '.abc' }).with({ scheme: 'untitled' }); + + await vscode.commands.executeCommand(commands.open, untitledFile); + assert.equal((await listener.nextResponse()).content, ''); + + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, `123`); + assert.equal((await listener.nextResponse()).content, '123'); + + await vscode.commands.executeCommand(commands.save); + const content = await fs.promises.readFile(untitledFile.fsPath); + assert.equal(content.toString(), '123'); + }); + + test.skip('When switching away from a non-default custom editors and then back, we should continue using the non-default editor', async () => { + const startingContent = `switch, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + { + await vscode.commands.executeCommand(commands.open, testDocument, { preview: false }); + const { content } = await listener.nextResponse(); + assert.strictEqual(content, startingContent.toString()); + assert.ok(!vscode.window.activeTextEditor); + } + + // Switch to non-default editor + await vscode.commands.executeCommand(commands.openWith, testDocument, 'default', { preview: false }); + assert.strictEqual(vscode.window.activeTextEditor!?.document.uri.toString(), testDocument.toString()); + + // Then open a new document (hiding existing one) + const otherFile = vscode.Uri.file(path.join(testWorkspaceRoot.fsPath, 'other.json')); + await vscode.commands.executeCommand(commands.open, otherFile); + assert.strictEqual(vscode.window.activeTextEditor!?.document.uri.toString(), otherFile.toString()); + + // And then back + await vscode.commands.executeCommand('workbench.action.navigateBack'); + await vscode.commands.executeCommand('workbench.action.navigateBack'); + + // Make sure we have the file on as text + assert.ok(vscode.window.activeTextEditor); + assert.strictEqual(vscode.window.activeTextEditor!?.document.uri.toString(), testDocument.toString()); + }); + + test('Should release the text document when the editor is closed', async () => { + const startingContent = `release document init,`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const doc = vscode.workspace.textDocuments.find(x => x.uri.toString() === testDocument.toString()); + assert.ok(doc); + assert.ok(!doc!.isClosed); + + await closeAllEditors(); + await delay(100); + assert.ok(doc!.isClosed); + }); +}); + +async function resetTestWorkspace() { + try { + await vscode.workspace.fs.delete(testWorkspaceRoot, { recursive: true }); + } catch { + // ok if file doesn't exist + } + await vscode.workspace.fs.createDirectory(testWorkspaceRoot); +} diff --git a/extensions/vscode-custom-editor-tests/src/test/index.ts b/extensions/vscode-custom-editor-tests/src/test/index.ts new file mode 100644 index 00000000000..a60622b2f28 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/test/index.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const testRunner = require('vscode/lib/testrunner'); + +const suite = 'Custom Editor Tests'; + +const options: any = { + ui: 'tdd', + useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), + timeout: 6000000 +}; + +if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { + options.reporter = 'mocha-multi-reporters'; + options.reporterOptions = { + reporterEnabled: 'spec, mocha-junit-reporter', + mochaJunitReporterReporterOptions: { + testsuitesTitle: `${suite} ${process.platform}`, + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + } + }; +} + +testRunner.configure(options); + +export = testRunner; diff --git a/extensions/vscode-custom-editor-tests/src/test/utils.ts b/extensions/vscode-custom-editor-tests/src/test/utils.ts new file mode 100644 index 00000000000..5edd53b27cc --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/test/utils.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function randomFilePath(args: { root: vscode.Uri, ext: string }): vscode.Uri { + const fileName = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); + return (vscode.Uri as any).joinPath(args.root, fileName + args.ext); +} + +export function closeAllEditors(): Thenable { + return vscode.commands.executeCommand('workbench.action.closeAllEditors'); +} + +export function disposeAll(disposables: vscode.Disposable[]) { + vscode.Disposable.from(...disposables).dispose(); +} + +export function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts b/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts new file mode 100644 index 00000000000..bf67b19225d --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// + diff --git a/extensions/vscode-custom-editor-tests/tsconfig.json b/extensions/vscode-custom-editor-tests/tsconfig.json new file mode 100644 index 00000000000..296ddb38fcb --- /dev/null +++ b/extensions/vscode-custom-editor-tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out" + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/vscode-custom-editor-tests/yarn.lock b/extensions/vscode-custom-editor-tests/yarn.lock new file mode 100644 index 00000000000..0d39ebbaa5e --- /dev/null +++ b/extensions/vscode-custom-editor-tests/yarn.lock @@ -0,0 +1,507 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/node@^12.11.7": + version "12.12.53" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.53.tgz#be0d375933c3d15ef2380dafb3b0350ea7021129" + integrity sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ== + +"@types/p-limit@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/p-limit/-/p-limit-2.2.0.tgz#94a608e9b258a6c6156a13d1a14fd720dba70b97" + integrity sha512-fGFbybl1r0oE9mqgfc2EHHUin9ZL5rbQIexWI6jYRU1ADVn4I3LHzT+g/kpPpZsfp8PB94CQ655pfAjNF8LP6A== + dependencies: + p-limit "*" + +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + integrity sha1-+mihT2qUXVTbvlDYzbMyDp47GgY= + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + +debug@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= + dependencies: + ms "0.7.1" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +diff@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + integrity sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE= + +escape-string-regexp@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + 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" + +glob@^7.1.2: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + 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" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-buffer@~1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + integrity sha1-jxDXl32NefL2/4YqgbBRPMslaGw= + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +lodash@^4.16.4: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + +md5@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mocha-junit-reporter@^1.17.0: + version "1.23.3" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981" + integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA== + dependencies: + debug "^2.2.0" + md5 "^2.1.0" + mkdirp "~0.5.1" + strip-ansi "^4.0.0" + xml "^1.0.0" + +mocha-multi-reporters@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" + integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= + dependencies: + debug "^3.1.0" + lodash "^4.16.4" + +mocha@^2.3.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-limit@*, p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +source-map-support@^0.5.0: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + integrity sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE= + +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + +vscode@^1.1.36: + version "1.1.37" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.37.tgz#c2a770bee4bb3fff765e2b72c7bcc813b8a6bb0a" + integrity sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg== + dependencies: + glob "^7.1.2" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + mocha "^5.2.0" + semver "^5.4.1" + source-map-support "^0.5.0" + vscode-test "^0.4.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xml@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= diff --git a/extensions/vscode-notebook-tests/.vscode/launch.json b/extensions/vscode-notebook-tests/.vscode/launch.json new file mode 100644 index 00000000000..73c3753c75c --- /dev/null +++ b/extensions/vscode-notebook-tests/.vscode/launch.json @@ -0,0 +1,17 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["${workspaceFolder}/../../", "${workspaceFolder}/test", "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out" ], + "stopOnEntry": false, + "sourceMaps": true, + "outDir": "${workspaceFolder}/out", + "preLaunchTask": "npm" + } + ] +} \ No newline at end of file diff --git a/extensions/vscode-notebook-tests/.vscode/tasks.json b/extensions/vscode-notebook-tests/.vscode/tasks.json new file mode 100644 index 00000000000..390a93a3a7f --- /dev/null +++ b/extensions/vscode-notebook-tests/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "command": "npm", + "type": "shell", + "presentation": { + "reveal": "silent" + }, + "args": ["run", "compile"], + "isBackground": true, + "problemMatcher": "$tsc-watch" +} diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json new file mode 100644 index 00000000000..4454b25fb2a --- /dev/null +++ b/extensions/vscode-notebook-tests/package.json @@ -0,0 +1,89 @@ +{ + "name": "vscode-notebook-tests", + "description": "Notebook tests for VS Code", + "version": "0.0.1", + "publisher": "vscode", + "license": "MIT", + "private": true, + "activationEvents": [ + "*" + ], + "main": "./out/notebookTestMain", + "enableProposedApi": true, + "engines": { + "vscode": "^1.25.0" + }, + "scripts": { + "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-notebook-tests ./tsconfig.json" + }, + "dependencies": {}, + "devDependencies": { + "typescript": "^3.8.3", + "@types/node": "^12.11.7", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "vscode": "~1.1.36", + "mocha": "^2.3.3" + }, + "contributes": { + "commands": [ + { + "command": "vscode-notebook-tests.createNewNotebook", + "title": "Create New Notebook" + }, + { + "command": "vscode-notebook-tests.debugAction", + "title": "Debug Notebook Test Cell Action", + "icon": "$(debug)" + } + ], + "notebookProvider": [ + { + "viewType": "notebookCoreTest", + "displayName": "Notebook Core Test", + "selector": [ + { + "filenamePattern": "*.vsctestnb", + "excludeFileNamePattern": "" + } + ] + }, + { + "viewType": "notebookSmokeTest", + "displayName": "Notebook Smoke Test", + "selector": [ + { + "filenamePattern": "*.smoke-nb", + "excludeFileNamePattern": "" + } + ] + } + ], + "notebookOutputRenderer": [ + { + "id": "notebookCoreTestRenderer", + "displayName": "Notebook Core Test Renderer", + "entrypoint": "./src/customRenderer.js", + "mimeTypes": [ + "text/custom" + ] + } + ], + "menus": { + "notebook/cell/title": [ + { + "command": "vscode-notebook-tests.debugAction", + "when": "notebookViewType == notebookSmokeTest", + "group": "inline@1" + } + ] + }, + "jsonValidation": [ + { + "fileMatch": "vscode://vscode-notebook-cell-metadata/*", + "url": "vscode://schemas/notebook/cellmetadata" + } + ] + } +} diff --git a/extensions/vscode-notebook-tests/src/customRenderer.js b/extensions/vscode-notebook-tests/src/customRenderer.js new file mode 100644 index 00000000000..f23538e38a7 --- /dev/null +++ b/extensions/vscode-notebook-tests/src/customRenderer.js @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const vscode = acquireVsCodeApi(); + +vscode.postMessage({ + type: 'custom_renderer_initialize', + payload: { + firstMessage: true + } +}); + +const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer'); + +notebook.onDidCreateOutput(({ element, mimeType }) => { + const div = document.createElement('div'); + div.innerText = `Hello ${mimeType}!`; + element.appendChild(div); +}); diff --git a/extensions/vscode-notebook-tests/src/index.ts b/extensions/vscode-notebook-tests/src/index.ts new file mode 100644 index 00000000000..293c02db743 --- /dev/null +++ b/extensions/vscode-notebook-tests/src/index.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const testRunner = require('vscode/lib/testrunner'); + +const options: any = { + ui: 'tdd', + useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), + timeout: 60000 +}; + +// These integration tests is being run in multiple environments (electron, web, remote) +// so we need to set the suite name based on the environment as the suite name is used +// for the test results file name +let suite = ''; +if (process.env.VSCODE_BROWSER) { + suite = `${process.env.VSCODE_BROWSER} Browser Integration Notebook Tests`; +} else if (process.env.REMOTE_VSCODE) { + suite = 'Remote Integration Notebook Tests'; +} else { + suite = 'Integration Notebook Tests'; +} + +if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { + options.reporter = 'mocha-multi-reporters'; + options.reporterOptions = { + reporterEnabled: 'spec, mocha-junit-reporter', + mochaJunitReporterReporterOptions: { + testsuitesTitle: `${suite} ${process.platform}`, + mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + } + }; +} + +testRunner.configure(options); + +export = testRunner; diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts new file mode 100644 index 00000000000..d1368d1d58f --- /dev/null +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -0,0 +1,1402 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { createRandomFile } from './utils'; + +export function timeoutAsync(n: number): Promise { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, n); + }); +} + +export function once(event: vscode.Event): vscode.Event { + return (listener: any, thisArgs = null, disposables?: any) => { + // we need this, in case the event fires during the listener call + let didFire = false; + let result: vscode.Disposable; + result = event(e => { + if (didFire) { + return; + } else if (result) { + result.dispose(); + } else { + didFire = true; + } + + return listener.call(thisArgs, e); + }, null, disposables); + + if (didFire) { + result.dispose(); + } + + return result; + }; +} + +async function getEventOncePromise(event: vscode.Event): Promise { + return new Promise((resolve, _reject) => { + once(event)((result: T) => resolve(result)); + }); +} + +// Since `workbench.action.splitEditor` command does await properly +// Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves +// The workaround here is waiting for the first visible notebook editor change event. +async function splitEditor() { + const once = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + await vscode.commands.executeCommand('workbench.action.splitEditor'); + await once; +} + +async function saveFileAndCloseAll(resource: vscode.Uri) { + const documentClosed = new Promise((resolve, _reject) => { + const d = vscode.notebook.onDidCloseNotebookDocument(e => { + if (e.uri.toString() === resource.toString()) { + d.dispose(); + resolve(); + } + }); + }); + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await documentClosed; +} + +async function saveAllFilesAndCloseAll(resource: vscode.Uri | undefined) { + const documentClosed = new Promise((resolve, _reject) => { + if (!resource) { + return resolve(); + } + const d = vscode.notebook.onDidCloseNotebookDocument(e => { + if (e.uri.toString() === resource.toString()) { + d.dispose(); + resolve(); + } + }); + }); + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await documentClosed; +} + +function assertInitalState() { + // no-op unless we figure out why some documents are opened after the editor is closed + + // assert.equal(vscode.window.activeNotebookEditor, undefined); + // assert.equal(vscode.notebook.notebookDocuments.length, 0); + // assert.equal(vscode.notebook.visibleNotebookEditors.length, 0); +} + +suite('Notebook API tests', () => { + // test.only('crash', async function () { + // for (let i = 0; i < 200; i++) { + // let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + + // resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb')); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // await vscode.commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + // } + // }); + + // test.only('crash', async function () { + // for (let i = 0; i < 200; i++) { + // let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // await vscode.commands.executeCommand('workbench.action.files.save'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb')); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // await vscode.commands.executeCommand('workbench.action.files.save'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // } + // }); + + test('document open/close event', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await firstDocumentOpen; + + const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await firstDocumentClose; + }); + + test('notebook open/close, all cell-documents are ready', async function () { + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + + const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { + for (let cell of notebook.cells) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); + assert.ok(doc); + assert.strictEqual(doc === cell.document, true); + assert.strictEqual(doc?.languageId, cell.language); + assert.strictEqual(doc?.isDirty, false); + assert.strictEqual(doc?.isClosed, false); + } + }); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await p; + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('notebook open/close, notebook ready when cell-document open event is fired', async function () { + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + let didHappen = false; + const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => { + if (doc.uri.scheme !== 'vscode-notebook-cell') { + return; + } + const notebook = vscode.notebook.notebookDocuments.find(notebook => { + const cell = notebook.cells.find(cell => cell.document === doc); + return Boolean(cell); + }); + assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`); + didHappen = true; + }); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await p; + assert.strictEqual(didHappen, true); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('shared document in notebook editors', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + let counter = 0; + const disposables: vscode.Disposable[] = []; + disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => { + counter++; + })); + disposables.push(vscode.notebook.onDidCloseNotebookDocument(() => { + counter--; + })); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(counter, 1); + + await splitEditor(); + assert.equal(counter, 1); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + assert.equal(counter, 0); + + disposables.forEach(d => d.dispose()); + }); + + test('editor open/close event', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await firstEditorOpen; + + const firstEditorClose = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await firstEditorClose; + }); + + test('editor open/close event 2', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + let count = 0; + const disposables: vscode.Disposable[] = []; + disposables.push(vscode.window.onDidChangeVisibleNotebookEditors(() => { + count = vscode.window.visibleNotebookEditors.length; + })); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(count, 1); + + await splitEditor(); + assert.equal(count, 2); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + assert.equal(count, 0); + }); + + test('editor editing event 2', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + const cellChangeEventRet = await cellsChangeEvent; + assert.equal(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document); + assert.equal(cellChangeEventRet.changes.length, 1); + assert.deepEqual(cellChangeEventRet.changes[0], { + start: 1, + deletedCount: 0, + deletedItems: [], + items: [ + vscode.window.activeNotebookEditor!.document.cells[1] + ] + }); + + const secondCell = vscode.window.activeNotebookEditor!.document.cells[1]; + + const moveCellEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.commands.executeCommand('notebook.cell.moveUp'); + const moveCellEventRet = await moveCellEvent; + assert.deepEqual(moveCellEventRet, { + document: vscode.window.activeNotebookEditor!.document, + changes: [ + { + start: 1, + deletedCount: 1, + deletedItems: [secondCell], + items: [] + }, + { + start: 0, + deletedCount: 0, + deletedItems: [], + items: [vscode.window.activeNotebookEditor?.document.cells[0]] + } + ] + }); + + const cellOutputChange = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + await vscode.commands.executeCommand('notebook.cell.execute'); + const cellOutputsAddedRet = await cellOutputChange; + assert.deepEqual(cellOutputsAddedRet, { + document: vscode.window.activeNotebookEditor!.document, + cells: [vscode.window.activeNotebookEditor!.document.cells[0]] + }); + assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 1); + + const cellOutputClear = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + await vscode.commands.executeCommand('notebook.cell.clearOutputs'); + const cellOutputsCleardRet = await cellOutputClear; + assert.deepEqual(cellOutputsCleardRet, { + document: vscode.window.activeNotebookEditor!.document, + cells: [vscode.window.activeNotebookEditor!.document.cells[0]] + }); + assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 0); + + // const cellChangeLanguage = getEventOncePromise(vscode.notebook.onDidChangeCellLanguage); + // await vscode.commands.executeCommand('notebook.cell.changeToMarkdown'); + // const cellChangeLanguageRet = await cellChangeLanguage; + // assert.deepEqual(cellChangeLanguageRet, { + // document: vscode.window.activeNotebookEditor!.document, + // cells: vscode.window.activeNotebookEditor!.document.cells[0], + // language: 'markdown' + // }); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('editor move cell event', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + await vscode.commands.executeCommand('notebook.focusTop'); + + const activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + const moveChange = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.commands.executeCommand('notebook.cell.moveDown'); + const ret = await moveChange; + assert.deepEqual(ret, { + document: vscode.window.activeNotebookEditor?.document, + changes: [ + { + start: 0, + deletedCount: 1, + deletedItems: [activeCell], + items: [] + }, + { + start: 1, + deletedCount: 0, + deletedItems: [], + items: [activeCell] + } + ] + }); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + const firstEditor = vscode.window.activeNotebookEditor; + assert.equal(firstEditor?.document.cells.length, 1); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('notebook editor active/visible', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + const firstEditor = vscode.window.activeNotebookEditor; + assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); + + await splitEditor(); + const secondEditor = vscode.window.activeNotebookEditor; + assert.strictEqual(secondEditor && vscode.window.visibleNotebookEditors.indexOf(secondEditor) >= 0, true); + assert.notStrictEqual(firstEditor, secondEditor); + assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); + assert.equal(vscode.window.visibleNotebookEditors.length, 2); + + const untitledEditorChange = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('workbench.action.files.newUntitledFile'); + await untitledEditorChange; + assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); + assert.notStrictEqual(firstEditor, vscode.window.activeNotebookEditor); + assert.strictEqual(secondEditor && vscode.window.visibleNotebookEditors.indexOf(secondEditor) < 0, true); + assert.notStrictEqual(secondEditor, vscode.window.activeNotebookEditor); + assert.equal(vscode.window.visibleNotebookEditors.length, 1); + + const activeEditorClose = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await activeEditorClose; + assert.strictEqual(secondEditor, vscode.window.activeNotebookEditor); + assert.equal(vscode.window.visibleNotebookEditors.length, 2); + assert.strictEqual(secondEditor && vscode.window.visibleNotebookEditors.indexOf(secondEditor) >= 0, true); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('notebook active editor change', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await firstEditorOpen; + + const firstEditorDeactivate = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('workbench.action.splitEditor'); + await firstEditorDeactivate; + + await saveFileAndCloseAll(resource); + }); + + test('edit API (replaceCells)', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + }); + + const cellChangeEventRet = await cellsChangeEvent; + assert.strictEqual(cellChangeEventRet.document === vscode.window.activeNotebookEditor?.document, true); + assert.strictEqual(cellChangeEventRet.document.isDirty, true); + assert.strictEqual(cellChangeEventRet.changes.length, 1); + assert.strictEqual(cellChangeEventRet.changes[0].start, 1); + assert.strictEqual(cellChangeEventRet.changes[0].deletedCount, 0); + assert.strictEqual(cellChangeEventRet.changes[0].items[0] === vscode.window.activeNotebookEditor!.document.cells[1], true); + + await saveAllFilesAndCloseAll(resource); + }); + + test('edit API (replaceOutput, USE NotebookCellOutput-type)', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('application/foo', 'bar'), + new vscode.NotebookCellOutputItem('application/json', { data: true }, { metadata: true }), + ])]); + }); + + const document = vscode.window.activeNotebookEditor?.document!; + assert.strictEqual(document.isDirty, true); + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].outputs.length, 1); + + // consuming is OLD api (for now) + const [output] = document.cells[0].outputs; + + assert.strictEqual(output.outputKind, vscode.CellOutputKind.Rich); + assert.strictEqual((output).data['application/foo'], 'bar'); + assert.deepStrictEqual((output).data['application/json'], { data: true }); + assert.deepStrictEqual((output).metadata, { custom: { 'application/json': { metadata: true } } }); + + await saveAllFilesAndCloseAll(undefined); + }); + + test('edit API (replaceOutput)', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]); + }); + + const document = vscode.window.activeNotebookEditor?.document!; + assert.strictEqual(document.isDirty, true); + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].outputs.length, 1); + assert.strictEqual(document.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich); + + await saveAllFilesAndCloseAll(undefined); + }); + + test('edit API (replaceOutput, event)', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const outputChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]); + }); + + const value = await outputChangeEvent; + assert.strictEqual(value.document === vscode.window.activeNotebookEditor?.document, true); + assert.strictEqual(value.document.isDirty, true); + assert.strictEqual(value.cells.length, 1); + assert.strictEqual(value.cells[0].outputs.length, 1); + assert.strictEqual(value.cells[0].outputs[0].outputKind, vscode.CellOutputKind.Rich); + + await saveAllFilesAndCloseAll(undefined); + }); + + test('edit API (replaceMetadata)', async function () { + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + }); + + const document = vscode.window.activeNotebookEditor?.document!; + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].metadata.executionOrder, 17); + assert.strictEqual(document.cells[0].metadata.inputCollapsed, true); + + assert.strictEqual(document.isDirty, true); + await saveFileAndCloseAll(resource); + }); + + test('edit API (replaceMetadata, event)', async function () { + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const event = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + }); + + const data = await event; + assert.strictEqual(data.document, vscode.window.activeNotebookEditor?.document); + assert.strictEqual(data.cell.metadata.executionOrder, 17); + assert.strictEqual(data.cell.metadata.inputCollapsed, true); + + assert.strictEqual(data.document.isDirty, true); + await saveFileAndCloseAll(resource); + }); + + test('workspace edit API (replaceCells)', async function () { + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const { document } = vscode.window.activeNotebookEditor!; + assert.strictEqual(document.cells.length, 1); + + // inserting two new cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 0, [{ + cellKind: vscode.CellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.CellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + + assert.strictEqual(document.cells.length, 3); + assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new_code'); + + // deleting cell 1 and 3 + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 1, []); + edit.replaceNotebookCells(document.uri, 2, 3, []); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].document.getText(), 'new_code'); + + // replacing all cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 1, [{ + cellKind: vscode.CellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new2_markdown' + }, { + cellKind: vscode.CellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new2_code' + }]); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + assert.strictEqual(document.cells.length, 2); + assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new2_code'); + + // remove all cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, document.cells.length, []); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + assert.strictEqual(document.cells.length, 0); + + await saveFileAndCloseAll(resource); + }); + + test('workspace edit API (replaceCells, event)', async function () { + + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const { document } = vscode.window.activeNotebookEditor!; + assert.strictEqual(document.cells.length, 1); + + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 0, [{ + cellKind: vscode.CellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.CellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const event = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + + const data = await event; + + // check document + assert.strictEqual(document.cells.length, 3); + assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new_code'); + + // check event data + assert.strictEqual(data.document === document, true); + assert.strictEqual(data.changes.length, 1); + assert.strictEqual(data.changes[0].deletedCount, 0); + assert.strictEqual(data.changes[0].deletedItems.length, 0); + assert.strictEqual(data.changes[0].items.length, 2); + assert.strictEqual(data.changes[0].items[0], document.cells[0]); + assert.strictEqual(data.changes[0].items[1], document.cells[1]); + await saveFileAndCloseAll(resource); + }); + + test('edit API batch edits', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellMetadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const version = vscode.window.activeNotebookEditor!.document.version; + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + editBuilder.replaceCellMetadata(0, { runnable: false }); + }); + + await cellsChangeEvent; + await cellMetadataChangeEvent; + assert.strictEqual(version + 1, vscode.window.activeNotebookEditor!.document.version); + await saveAllFilesAndCloseAll(resource); + }); + + test('edit API batch edits undo/redo', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellMetadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const version = vscode.window.activeNotebookEditor!.document.version; + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.CellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + editBuilder.replaceCellMetadata(0, { runnable: false }); + }); + + await cellsChangeEvent; + await cellMetadataChangeEvent; + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.length, 2); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0]?.metadata?.runnable, false); + assert.strictEqual(version + 1, vscode.window.activeNotebookEditor!.document.version); + + await vscode.commands.executeCommand('undo'); + assert.strictEqual(version + 2, vscode.window.activeNotebookEditor!.document.version); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0]?.metadata?.runnable, undefined); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.length, 1); + + await saveAllFilesAndCloseAll(resource); + }); + + test('initialzation should not emit cell change events.', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + + let count = 0; + const disposables: vscode.Disposable[] = []; + disposables.push(vscode.notebook.onDidChangeNotebookCells(() => { + count++; + })); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(count, 0); + + disposables.forEach(d => d.dispose()); + + await saveFileAndCloseAll(resource); + }); +}); + +suite('notebook workflow', () => { + test('notebook open', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const activeCell = vscode.window.activeNotebookEditor!.selection; + assert.notEqual(vscode.window.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + }); + + test('notebook cell actions', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + // ---- insert cell below and focus ---- // + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + // ---- insert cell above and focus ---- // + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + let activeCell = vscode.window.activeNotebookEditor!.selection; + assert.notEqual(vscode.window.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + + // ---- focus bottom ---- // + await vscode.commands.executeCommand('notebook.focusBottom'); + activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2); + + // ---- focus top and then copy down ---- // + await vscode.commands.executeCommand('notebook.focusTop'); + activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + + await vscode.commands.executeCommand('notebook.cell.copyDown'); + activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + assert.equal(activeCell?.document.getText(), 'test'); + + await vscode.commands.executeCommand('notebook.cell.delete'); + activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + assert.equal(activeCell?.document.getText(), ''); + + // ---- focus top and then copy up ---- // + await vscode.commands.executeCommand('notebook.focusTop'); + await vscode.commands.executeCommand('notebook.cell.copyUp'); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 4); + assert.equal(vscode.window.activeNotebookEditor!.document.cells[0].document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.document.cells[1].document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.document.cells[2].document.getText(), ''); + assert.equal(vscode.window.activeNotebookEditor!.document.cells[3].document.getText(), ''); + activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + + + // ---- move up and down ---- // + + await vscode.commands.executeCommand('notebook.cell.moveDown'); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(vscode.window.activeNotebookEditor!.selection!), 1, + `first move down, active cell ${vscode.window.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.window.activeNotebookEditor!.selection!.document.getText()}`); + + // await vscode.commands.executeCommand('notebook.cell.moveDown'); + // activeCell = vscode.window.activeNotebookEditor!.selection; + + // assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2, + // `second move down, active cell ${vscode.window.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.window.activeNotebookEditor!.selection!.document.getText()}`); + // assert.equal(vscode.window.activeNotebookEditor!.document.cells[0].document.getText(), 'test'); + // assert.equal(vscode.window.activeNotebookEditor!.document.cells[1].document.getText(), ''); + // assert.equal(vscode.window.activeNotebookEditor!.document.cells[2].document.getText(), 'test'); + // assert.equal(vscode.window.activeNotebookEditor!.document.cells[3].document.getText(), ''); + + // ---- ---- // + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + }); + + test('notebook join cells', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.commands.executeCommand('notebook.cell.joinAbove'); + await cellsChangeEvent; + + assert.deepEqual(vscode.window.activeNotebookEditor!.selection?.document.getText().split(/\r\n|\r|\n/), ['test', 'var abc = 0;']); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + }); + + test('move cells will not recreate cells in ExtHost', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + await vscode.commands.executeCommand('notebook.focusTop'); + + const activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + await vscode.commands.executeCommand('notebook.cell.moveDown'); + await vscode.commands.executeCommand('notebook.cell.moveDown'); + + const newActiveCell = vscode.window.activeNotebookEditor!.selection; + assert.deepEqual(activeCell, newActiveCell); + + await saveFileAndCloseAll(resource); + // TODO@rebornix, there are still some events order issue. + // assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(newActiveCell!), 2); + }); + + // test.only('document metadata is respected', async function () { + // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + // assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + // const editor = vscode.window.activeNotebookEditor!; + + // assert.equal(editor.document.cells.length, 1); + // editor.document.metadata.editable = false; + // await editor.edit(builder => builder.delete(0)); + // assert.equal(editor.document.cells.length, 1, 'should not delete cell'); // Not editable, no effect + // await editor.edit(builder => builder.insert(0, 'test', 'python', vscode.CellKind.Code, [], undefined)); + // assert.equal(editor.document.cells.length, 1, 'should not insert cell'); // Not editable, no effect + + // editor.document.metadata.editable = true; + // await editor.edit(builder => builder.delete(0)); + // assert.equal(editor.document.cells.length, 0, 'should delete cell'); // Editable, it worked + // await editor.edit(builder => builder.insert(0, 'test', 'python', vscode.CellKind.Code, [], undefined)); + // assert.equal(editor.document.cells.length, 1, 'should insert cell'); // Editable, it worked + + // // await vscode.commands.executeCommand('workbench.action.files.save'); + // await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + // }); + + test('cell runnable metadata is respected', async () => { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + const editor = vscode.window.activeNotebookEditor!; + + await vscode.commands.executeCommand('notebook.focusTop'); + const cell = editor.document.cells[0]; + assert.equal(cell.outputs.length, 0); + + let metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + cell.metadata.runnable = false; + await metadataChangeEvent; + + await vscode.commands.executeCommand('notebook.cell.execute'); + assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work + + metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + cell.metadata.runnable = true; + await metadataChangeEvent; + + await vscode.commands.executeCommand('notebook.cell.execute'); + assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + }); + + test('document runnable metadata is respected', async () => { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + const editor = vscode.window.activeNotebookEditor!; + + const cell = editor.document.cells[0]; + assert.equal(cell.outputs.length, 0); + let metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); + editor.document.metadata.runnable = false; + await metadataChangeEvent; + + await vscode.commands.executeCommand('notebook.execute'); + assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work + + metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); + editor.document.metadata.runnable = true; + await metadataChangeEvent; + + await vscode.commands.executeCommand('notebook.execute'); + assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + }); +}); + +suite('notebook dirty state', () => { + test('notebook open', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const activeCell = vscode.window.activeNotebookEditor!.selection; + assert.notEqual(vscode.window.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + + const edit = new vscode.WorkspaceEdit(); + edit.insert(activeCell!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true); + assert.equal(vscode.window.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells[1], vscode.window.activeNotebookEditor?.selection); + assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); + + await saveFileAndCloseAll(resource); + }); +}); + +suite('notebook undo redo', () => { + test('notebook open', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const activeCell = vscode.window.activeNotebookEditor!.selection; + assert.notEqual(vscode.window.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + + + // modify the second cell, delete it + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + await vscode.commands.executeCommand('notebook.cell.delete'); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 2); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(vscode.window.activeNotebookEditor!.selection!), 1); + + + // undo should bring back the deleted cell, and revert to previous content and selection + await vscode.commands.executeCommand('undo'); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(vscode.window.activeNotebookEditor!.selection!), 1); + assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); + + // redo + // await vscode.commands.executeCommand('notebook.redo'); + // assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 2); + // assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(vscode.window.activeNotebookEditor!.selection!), 1); + // assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'test'); + + await saveFileAndCloseAll(resource); + }); + + test.skip('execute and then undo redo', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + const cellChangeEventRet = await cellsChangeEvent; + assert.equal(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document); + assert.equal(cellChangeEventRet.changes.length, 1); + assert.deepEqual(cellChangeEventRet.changes[0], { + start: 1, + deletedCount: 0, + deletedItems: [], + items: [ + vscode.window.activeNotebookEditor!.document.cells[1] + ] + }); + + const secondCell = vscode.window.activeNotebookEditor!.document.cells[1]; + + const moveCellEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.commands.executeCommand('notebook.cell.moveUp'); + const moveCellEventRet = await moveCellEvent; + assert.deepEqual(moveCellEventRet, { + document: vscode.window.activeNotebookEditor!.document, + changes: [ + { + start: 1, + deletedCount: 1, + deletedItems: [secondCell], + items: [] + }, + { + start: 0, + deletedCount: 0, + deletedItems: [], + items: [vscode.window.activeNotebookEditor?.document.cells[0]] + } + ] + }); + + const cellOutputChange = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + await vscode.commands.executeCommand('notebook.cell.execute'); + const cellOutputsAddedRet = await cellOutputChange; + assert.deepEqual(cellOutputsAddedRet, { + document: vscode.window.activeNotebookEditor!.document, + cells: [vscode.window.activeNotebookEditor!.document.cells[0]] + }); + assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 1); + + const cellOutputClear = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + await vscode.commands.executeCommand('undo'); + const cellOutputsCleardRet = await cellOutputClear; + assert.deepEqual(cellOutputsCleardRet, { + document: vscode.window.activeNotebookEditor!.document, + cells: [vscode.window.activeNotebookEditor!.document.cells[0]] + }); + assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 0); + + await saveFileAndCloseAll(resource); + }); + +}); + +suite('notebook working copy', () => { + // test('notebook revert on close', async function () { + // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + // assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + // await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + // await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); + + // // close active editor from command will revert the file + // await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // assert.equal(vscode.window.activeNotebookEditor !== undefined, true); + // assert.equal(vscode.window.activeNotebookEditor?.selection !== undefined, true); + // assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells[0], vscode.window.activeNotebookEditor?.selection); + // assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'test'); + + // await vscode.commands.executeCommand('workbench.action.files.save'); + // await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + // }); + + // test('notebook revert', async function () { + // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + // assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + // await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + // await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); + // await vscode.commands.executeCommand('workbench.action.files.revert'); + + // assert.equal(vscode.window.activeNotebookEditor !== undefined, true); + // assert.equal(vscode.window.activeNotebookEditor?.selection !== undefined, true); + // assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells[0], vscode.window.activeNotebookEditor?.selection); + // assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells.length, 1); + // assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'test'); + + // await vscode.commands.executeCommand('workbench.action.files.saveAll'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // }); + + test('multiple tabs: dirty + clean', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + + // make sure that the previous dirty editor is still restored in the extension host and no data loss + assert.equal(vscode.window.activeNotebookEditor !== undefined, true); + assert.equal(vscode.window.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells[1], vscode.window.activeNotebookEditor?.selection); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); + + await saveFileAndCloseAll(resource); + }); + + test('multiple tabs: two dirty tabs and switching', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + + // switch to the first editor + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true); + assert.equal(vscode.window.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells[1], vscode.window.activeNotebookEditor?.selection); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells.length, 3); + assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); + + // switch to the second editor + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true); + assert.equal(vscode.window.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells[1], vscode.window.activeNotebookEditor?.selection); + assert.deepEqual(vscode.window.activeNotebookEditor?.document.cells.length, 2); + assert.equal(vscode.window.activeNotebookEditor?.selection?.document.getText(), ''); + + await saveAllFilesAndCloseAll(secondResource); + // await vscode.commands.executeCommand('workbench.action.files.saveAll'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('multiple tabs: different editors with same document', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + const firstNotebookEditor = vscode.window.activeNotebookEditor; + assert.equal(firstNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(firstNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(firstNotebookEditor!.selection?.language, 'typescript'); + + await splitEditor(); + const secondNotebookEditor = vscode.window.activeNotebookEditor; + assert.equal(secondNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(secondNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(secondNotebookEditor!.selection?.language, 'typescript'); + + assert.notEqual(firstNotebookEditor, secondNotebookEditor); + assert.equal(firstNotebookEditor?.document, secondNotebookEditor?.document, 'split notebook editors share the same document'); + assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png'))); + + await saveAllFilesAndCloseAll(resource); + + // await vscode.commands.executeCommand('workbench.action.files.saveAll'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); +}); + +suite('metadata', () => { + test('custom metadata should be supported', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); + assert.equal(vscode.window.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + await saveFileAndCloseAll(resource); + }); + + + // TODO@rebornix skip as it crashes the process all the time + test.skip('custom metadata should be supported 2', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); + assert.equal(vscode.window.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + // TODO see #101462 + // await vscode.commands.executeCommand('notebook.cell.copyDown'); + // const activeCell = vscode.window.activeNotebookEditor!.selection; + // assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + // assert.equal(activeCell?.metadata.custom!['testCellMetadata'] as number, 123); + + await saveFileAndCloseAll(resource); + }); +}); + +suite('regression', () => { + // test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () { + // assertInitalState(); + // await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { "viewType": "notebookCoreTest" }); + // assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + // assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); + // assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // }); + + test('#106657. Opening a notebook from markers view is broken ', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const document = vscode.window.activeNotebookEditor?.document!; + const [cell] = document.cells; + + await saveAllFilesAndCloseAll(document.uri); + assert.strictEqual(vscode.window.activeNotebookEditor, undefined); + + // opening a cell-uri opens a notebook editor + await vscode.commands.executeCommand('vscode.open', cell.uri, vscode.ViewColumn.Active); + + assert.strictEqual(!!vscode.window.activeNotebookEditor, true); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.uri.toString(), resource.toString()); + }); + + test('Cannot open notebook from cell-uri with vscode.open-command', async function () { + this.skip(); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const document = vscode.window.activeNotebookEditor?.document!; + const [cell] = document.cells; + + await saveAllFilesAndCloseAll(document.uri); + assert.strictEqual(vscode.window.activeNotebookEditor, undefined); + + // BUG is that the editor opener (https://github.com/microsoft/vscode/blob/8e7877bdc442f1e83a7fec51920d82b696139129/src/vs/editor/browser/services/openerService.ts#L69) + // removes the fragment if it matches something numeric. For notebooks that's not wanted... + await vscode.commands.executeCommand('vscode.open', cell.uri); + + assert.strictEqual(vscode.window.activeNotebookEditor?.document.uri.toString(), resource.toString()); + }); + + test('#97830, #97764. Support switch to other editor types', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;'); + assert.equal(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); + assert.equal(vscode.window.activeTextEditor?.document.uri.path, resource.path); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + // open text editor, pin, and then open a notebook + test('#96105 - dirty editors', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); + const edit = new vscode.WorkspaceEdit(); + edit.insert(resource, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + // now it's dirty, open the resource with notebook editor should open a new one + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.notEqual(vscode.window.activeNotebookEditor, undefined, 'notebook first'); + // assert.notEqual(vscode.window.activeTextEditor, undefined); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('#102411 - untitled notebook creation failed', async function () { + assertInitalState(); + await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { viewType: 'notebookCoreTest' }); + assert.notEqual(vscode.window.activeNotebookEditor, undefined, 'untitled notebook editor is not undefined'); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('#102423 - copy/paste shares the same text buffer', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + let activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(activeCell?.document.getText(), 'test'); + + await vscode.commands.executeCommand('notebook.cell.copyDown'); + await vscode.commands.executeCommand('notebook.cell.edit'); + activeCell = vscode.window.activeNotebookEditor!.selection; + assert.equal(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + assert.equal(activeCell?.document.getText(), 'test'); + + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + assert.equal(vscode.window.activeNotebookEditor!.document.cells.length, 2); + assert.notEqual(vscode.window.activeNotebookEditor!.document.cells[0].document.getText(), vscode.window.activeNotebookEditor!.document.cells[1].document.getText()); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); +}); + +suite('webview', () => { + // for web, `asWebUri` gets `https`? + // test('asWebviewUri', async function () { + // if (vscode.env.uiKind === vscode.UIKind.Web) { + // return; + // } + + // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // assert.equal(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); + // const uri = vscode.window.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png')); + // assert.equal(uri.scheme, 'vscode-webview-resource'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // }); + + + // 404 on web + // test('custom renderer message', async function () { + // if (vscode.env.uiKind === vscode.UIKind.Web) { + // return; + // } + + // const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './customRenderer.vsctestnb')); + // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + // const editor = vscode.window.activeNotebookEditor; + // const promise = new Promise(resolve => { + // const messageEmitter = editor?.onDidReceiveMessage(e => { + // if (e.type === 'custom_renderer_initialize') { + // resolve(); + // messageEmitter?.dispose(); + // } + // }); + // }); + + // await vscode.commands.executeCommand('notebook.cell.execute'); + // await promise; + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // }); +}); diff --git a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts new file mode 100644 index 00000000000..10ab61306dc --- /dev/null +++ b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as child_process from 'child_process'; +import * as path from 'path'; + +function wait(ms: number): Promise { + return new Promise(r => setTimeout(r, ms)); +} + +export function smokeTestActivate(context: vscode.ExtensionContext): any { + context.subscriptions.push(vscode.commands.registerCommand('vscode-notebook-tests.createNewNotebook', async () => { + const workspacePath = vscode.workspace.workspaceFolders![0].uri.fsPath; + const notebookPath = path.join(workspacePath, 'test.smoke-nb'); + child_process.execSync('echo \'\' > ' + notebookPath); + await wait(500); + await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(notebookPath)); + })); + + context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookSmokeTest', { + onDidChangeNotebook: new vscode.EventEmitter().event, + openNotebook: async (_resource: vscode.Uri) => { + const dto: vscode.NotebookData = { + languages: ['typescript'], + metadata: {}, + cells: [ + { + source: 'code()', + language: 'typescript', + cellKind: vscode.CellKind.Code, + outputs: [], + metadata: { + custom: { testCellMetadata: 123 } + } + }, + { + source: 'Markdown Cell', + language: 'markdown', + cellKind: vscode.CellKind.Markdown, + outputs: [], + metadata: { + custom: { testCellMetadata: 123 } + } + } + ] + }; + + return dto; + }, + resolveNotebook: async (_document: vscode.NotebookDocument) => { + return; + }, + saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => { + return { + id: '1', + delete: () => { } + }; + } + })); + + const kernel: vscode.NotebookKernel = { + label: 'notebookSmokeTest', + isPreferred: true, + executeAllCells: async (_document: vscode.NotebookDocument) => { + for (let i = 0; i < _document.cells.length; i++) { + _document.cells[i].outputs = [{ + outputKind: vscode.CellOutputKind.Rich, + data: { + 'text/html': ['test output'] + } + }]; + } + }, + cancelAllCellsExecution: async () => { }, + executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined) => { + if (!_cell) { + _cell = _document.cells[0]; + } + + _cell.outputs = [{ + outputKind: vscode.CellOutputKind.Rich, + data: { + 'text/html': ['test output'] + } + }]; + return; + }, + cancelCellExecution: async () => { } + }; + + context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.smoke-nb' }, { + provideKernels: async () => { + return [kernel]; + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('vscode-notebook-tests.debugAction', async (cell: vscode.NotebookCell) => { + if (cell) { + const edit = new vscode.WorkspaceEdit(); + const fullRange = new vscode.Range(0, 0, cell.document.lineCount - 1, cell.document.lineAt(cell.document.lineCount - 1).range.end.character); + edit.replace(cell.document.uri, fullRange, 'test'); + await vscode.workspace.applyEdit(edit); + } else { + throw new Error('Cell not set correctly'); + } + })); +} diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts new file mode 100644 index 00000000000..03c8c7435ce --- /dev/null +++ b/extensions/vscode-notebook-tests/src/notebookTestMain.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { smokeTestActivate } from './notebookSmokeTestMain'; + +export function activate(context: vscode.ExtensionContext): any { + smokeTestActivate(context); + + const _onDidChangeNotebook = new vscode.EventEmitter(); + context.subscriptions.push(_onDidChangeNotebook); + context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', { + onDidChangeNotebook: _onDidChangeNotebook.event, + openNotebook: async (_resource: vscode.Uri) => { + if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { + return { + languages: ['typescript'], + metadata: {}, + cells: [] + }; + } + + const dto: vscode.NotebookData = { + languages: ['typescript'], + metadata: { + custom: { testMetadata: false } + }, + cells: [ + { + source: 'test', + language: 'typescript', + cellKind: vscode.CellKind.Code, + outputs: [], + metadata: { + custom: { testCellMetadata: 123 } + } + } + ] + }; + + return dto; + }, + resolveNotebook: async (_document: vscode.NotebookDocument) => { + return; + }, + saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => { + return { + id: '1', + delete: () => { } + }; + } + })); + + const kernel: vscode.NotebookKernel = { + label: 'Notebook Test Kernel', + isPreferred: true, + executeAllCells: async (_document: vscode.NotebookDocument) => { + const cell = _document.cells[0]; + + cell.outputs = [{ + outputKind: vscode.CellOutputKind.Rich, + data: { + 'text/plain': ['my output'] + } + }]; + return; + }, + cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, + executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { + if (!cell) { + cell = document.cells[0]; + } + + if (document.uri.path.endsWith('customRenderer.vsctestnb')) { + cell.outputs = [{ + outputKind: vscode.CellOutputKind.Rich, + data: { + 'text/custom': 'test' + } + }]; + + return; + } + + const previousOutputs = cell.outputs; + const newOutputs: vscode.CellOutput[] = [{ + outputKind: vscode.CellOutputKind.Rich, + data: { + 'text/plain': ['my output'] + } + }]; + + cell.outputs = newOutputs; + + _onDidChangeNotebook.fire({ + document: document, + undo: () => { + if (cell) { + cell.outputs = previousOutputs; + } + }, + redo: () => { + if (cell) { + cell.outputs = newOutputs; + } + } + }); + return; + }, + cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } + }; + + context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, { + provideKernels: async () => { + return [kernel]; + } + })); +} diff --git a/extensions/vscode-notebook-tests/src/typings/ref.d.ts b/extensions/vscode-notebook-tests/src/typings/ref.d.ts new file mode 100644 index 00000000000..a17099ac50c --- /dev/null +++ b/extensions/vscode-notebook-tests/src/typings/ref.d.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// +/// + diff --git a/extensions/vscode-notebook-tests/src/utils.ts b/extensions/vscode-notebook-tests/src/utils.ts new file mode 100644 index 00000000000..0a8f20a1942 --- /dev/null +++ b/extensions/vscode-notebook-tests/src/utils.ts @@ -0,0 +1,261 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import * as path from 'path'; +import * as vscode from 'vscode'; + +class File implements vscode.FileStat { + + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + data?: Uint8Array; + + constructor(name: string) { + this.type = vscode.FileType.File; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + } +} + +class Directory implements vscode.FileStat { + + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + entries: Map; + + constructor(name: string) { + this.type = vscode.FileType.Directory; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + this.entries = new Map(); + } +} + +export type Entry = File | Directory; + +export class TestFS implements vscode.FileSystemProvider { + + constructor( + readonly scheme: string, + readonly isCaseSensitive: boolean + ) { } + + readonly root = new Directory(''); + + // --- manage file metadata + + stat(uri: vscode.Uri): vscode.FileStat { + return this._lookup(uri, false); + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { + const entry = this._lookupAsDirectory(uri, false); + const result: [string, vscode.FileType][] = []; + for (const [name, child] of entry.entries) { + result.push([name, child.type]); + } + return result; + } + + // --- manage file contents + + readFile(uri: vscode.Uri): Uint8Array { + const data = this._lookupAsFile(uri, false).data; + if (data) { + return data; + } + throw vscode.FileSystemError.FileNotFound(); + } + + writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { + const basename = path.posix.basename(uri.path); + const parent = this._lookupParentDirectory(uri); + let entry = parent.entries.get(basename); + if (entry instanceof Directory) { + throw vscode.FileSystemError.FileIsADirectory(uri); + } + if (!entry && !options.create) { + throw vscode.FileSystemError.FileNotFound(uri); + } + if (entry && options.create && !options.overwrite) { + throw vscode.FileSystemError.FileExists(uri); + } + if (!entry) { + entry = new File(basename); + parent.entries.set(basename, entry); + this._fireSoon({ type: vscode.FileChangeType.Created, uri }); + } + entry.mtime = Date.now(); + entry.size = content.byteLength; + entry.data = content; + + this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); + } + + // --- manage files/folders + + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void { + + if (!options.overwrite && this._lookup(newUri, true)) { + throw vscode.FileSystemError.FileExists(newUri); + } + + const entry = this._lookup(oldUri, false); + const oldParent = this._lookupParentDirectory(oldUri); + + const newParent = this._lookupParentDirectory(newUri); + const newName = path.posix.basename(newUri.path); + + oldParent.entries.delete(entry.name); + entry.name = newName; + newParent.entries.set(newName, entry); + + this._fireSoon( + { type: vscode.FileChangeType.Deleted, uri: oldUri }, + { type: vscode.FileChangeType.Created, uri: newUri } + ); + } + + delete(uri: vscode.Uri): void { + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + const basename = path.posix.basename(uri.path); + const parent = this._lookupAsDirectory(dirname, false); + if (!parent.entries.has(basename)) { + throw vscode.FileSystemError.FileNotFound(uri); + } + parent.entries.delete(basename); + parent.mtime = Date.now(); + parent.size -= 1; + this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted }); + } + + createDirectory(uri: vscode.Uri): void { + const basename = path.posix.basename(uri.path); + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + const parent = this._lookupAsDirectory(dirname, false); + + const entry = new Directory(basename); + parent.entries.set(entry.name, entry); + parent.mtime = Date.now(); + parent.size += 1; + this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri }); + } + + // --- lookup + + private _lookup(uri: vscode.Uri, silent: false): Entry; + private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined; + private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined { + const parts = uri.path.split('/'); + let entry: Entry = this.root; + for (const part of parts) { + const partLow = part.toLowerCase(); + if (!part) { + continue; + } + let child: Entry | undefined; + if (entry instanceof Directory) { + if (this.isCaseSensitive) { + child = entry.entries.get(part); + } else { + for (const [key, value] of entry.entries) { + if (key.toLowerCase() === partLow) { + child = value; + break; + } + } + } + } + if (!child) { + if (!silent) { + throw vscode.FileSystemError.FileNotFound(uri); + } else { + return undefined; + } + } + entry = child; + } + return entry; + } + + private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory { + const entry = this._lookup(uri, silent); + if (entry instanceof Directory) { + return entry; + } + throw vscode.FileSystemError.FileNotADirectory(uri); + } + + private _lookupAsFile(uri: vscode.Uri, silent: boolean): File { + const entry = this._lookup(uri, silent); + if (entry instanceof File) { + return entry; + } + throw vscode.FileSystemError.FileIsADirectory(uri); + } + + private _lookupParentDirectory(uri: vscode.Uri): Directory { + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + return this._lookupAsDirectory(dirname, false); + } + + // --- manage file events + + private _emitter = new vscode.EventEmitter(); + private _bufferedEvents: vscode.FileChangeEvent[] = []; + private _fireSoonHandle?: NodeJS.Timer; + + readonly onDidChangeFile: vscode.Event = this._emitter.event; + + watch(_resource: vscode.Uri): vscode.Disposable { + // ignore, fires for all changes... + return new vscode.Disposable(() => { }); + } + + private _fireSoon(...events: vscode.FileChangeEvent[]): void { + this._bufferedEvents.push(...events); + + if (this._fireSoonHandle) { + clearTimeout(this._fireSoonHandle); + } + + this._fireSoonHandle = setTimeout(() => { + this._emitter.fire(this._bufferedEvents); + this._bufferedEvents.length = 0; + }, 5); + } +} + +export function rndName() { + return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); +} + +export const testFs = new TestFS('fake-fs', true); +vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensitive: testFs.isCaseSensitive }); + +export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, prefix = '', ext = ''): Promise { + let fakeFile: vscode.Uri; + if (dir) { + fakeFile = dir.with({ path: dir.path + '/' + rndName() + ext }); + } else { + fakeFile = vscode.Uri.parse(`${testFs.scheme}:/${prefix}-${rndName() + ext}`); + } + + await testFs.writeFile(fakeFile, Buffer.from(contents), { create: true, overwrite: true }); + return fakeFile; +} diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.css b/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb similarity index 88% rename from src/vs/editor/standalone/browser/quickOpen/gotoLine.css rename to extensions/vscode-notebook-tests/test/customRenderer.vsctestnb index e71a5e1dd76..a4a092d8349 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.css +++ b/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb @@ -2,7 +2,3 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -.monaco-quick-open-widget { - font-size: 13px; -} \ No newline at end of file diff --git a/extensions/vscode-notebook-tests/test/empty.vsctestnb b/extensions/vscode-notebook-tests/test/empty.vsctestnb new file mode 100644 index 00000000000..a4a092d8349 --- /dev/null +++ b/extensions/vscode-notebook-tests/test/empty.vsctestnb @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/vscode-notebook-tests/test/first.vsctestnb b/extensions/vscode-notebook-tests/test/first.vsctestnb new file mode 100644 index 00000000000..a4a092d8349 --- /dev/null +++ b/extensions/vscode-notebook-tests/test/first.vsctestnb @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/vscode-notebook-tests/test/second.vsctestnb b/extensions/vscode-notebook-tests/test/second.vsctestnb new file mode 100644 index 00000000000..a4a092d8349 --- /dev/null +++ b/extensions/vscode-notebook-tests/test/second.vsctestnb @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/vscode-notebook-tests/tsconfig.json b/extensions/vscode-notebook-tests/tsconfig.json new file mode 100644 index 00000000000..296ddb38fcb --- /dev/null +++ b/extensions/vscode-notebook-tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out" + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/vscode-notebook-tests/yarn.lock b/extensions/vscode-notebook-tests/yarn.lock new file mode 100644 index 00000000000..465cb67d0aa --- /dev/null +++ b/extensions/vscode-notebook-tests/yarn.lock @@ -0,0 +1,793 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^12.11.7": + version "12.12.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.37.tgz#cb4782d847f801fa58316da5b4801ca3a59ae790" + integrity sha512-4mXKoDptrXAwZErQHrLzpe0FN/0Wmf5JRniSVIdwUrtDf9wnmEV1teCNLBo/TwuXhkK/bVegoEn/wmb+x0AuPg== + +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +ajv@^6.5.5: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + 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" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + integrity sha1-+mihT2qUXVTbvlDYzbMyDp47GgY= + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= + dependencies: + ms "0.7.1" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +diff@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + integrity sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE= + +escape-string-regexp@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + 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" + +glob@^7.1.2: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + 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" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-buffer@~1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + integrity sha1-jxDXl32NefL2/4YqgbBRPMslaGw= + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +lodash@^4.16.4: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + +md5@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + +mime-db@1.43.0: + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + dependencies: + mime-db "1.43.0" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mocha-junit-reporter@^1.17.0: + version "1.23.3" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981" + integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA== + dependencies: + debug "^2.2.0" + md5 "^2.1.0" + mkdirp "~0.5.1" + strip-ansi "^4.0.0" + xml "^1.0.0" + +mocha-multi-reporters@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" + integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= + dependencies: + debug "^3.1.0" + lodash "^4.16.4" + +mocha@^2.3.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + 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.3" + 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.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +source-map-support@^0.5.0: + version "0.5.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.18.tgz#f5f33489e270bd7f7d7e7b8debf283f3a4066960" + integrity sha512-9luZr/BZ2QeU6tO2uG8N2aZpVSli4TSAOAqFOyTO51AJcD9P99c0K1h6dD6r6qo5dyT44BR5exweOaLLeldTkQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + 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" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + integrity sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE= + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +typescript@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +url-parse@^1.4.4: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + +vscode@~1.1.36: + version "1.1.36" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" + integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + dependencies: + glob "^7.1.2" + mocha "^5.2.0" + request "^2.88.0" + semver "^5.4.1" + source-map-support "^0.5.0" + url-parse "^1.4.4" + vscode-test "^0.4.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xml@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 90e3e0e87ee..fd7a9c2f881 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -11,7 +11,7 @@ "extensionKind": [ "ui" ], "scripts": { "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver ./tsconfig.json" + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver" }, "activationEvents": [ "onResolveRemoteAuthority:test", diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index d95ee976f6a..61c900c4b76 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -195,7 +195,7 @@ export function activate(context: vscode.ExtensionContext) { proxyServer.listen(0, () => { const port = (proxyServer.address()).port; outputChannel.appendLine(`Going through proxy at port ${port}`); - res({ host: '127.0.0.1', port }); + res(new vscode.ResolvedAuthority('127.0.0.1', port)); }); context.subscriptions.push({ dispose: () => { diff --git a/extensions/xml/package.json b/extensions/xml/package.json index 77e36951b7c..3e5042df702 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -23,8 +23,8 @@ ".dita", ".ditamap", ".dtd", - ".ent", - ".mod", + ".ent", + ".mod", ".dtml", ".fsproj", ".fxml", diff --git a/extensions/xml/xsl.language-configuration.json b/extensions/xml/xsl.language-configuration.json index 7605fc4a7d1..cf787c79edd 100644 --- a/extensions/xml/xsl.language-configuration.json +++ b/extensions/xml/xsl.language-configuration.json @@ -4,7 +4,11 @@ "blockComment": [""] }, "brackets": [ - ["<", ">"] + [""], + ["<", ">"], + ["{", "}"], + ["(", ")"], + ["[", "]"] ] // enhancedBrackets: [{ diff --git a/extensions/yarn.lock b/extensions/yarn.lock index dc1fa7c2de8..20586e5c587 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.7.5: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +typescript@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== diff --git a/gulpfile.js b/gulpfile.js index 8a5eff502f6..1d13cff608c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -40,4 +40,4 @@ process.on('unhandledRejection', (reason, p) => { // Load all the gulpfiles only if running tasks other than the editor tasks const build = path.join(__dirname, 'build'); require('glob').sync('gulpfile.*.js', { cwd: build }) - .forEach(f => require(`./build/${f}`)); \ No newline at end of file + .forEach(f => require(`./build/${f}`)); diff --git a/package.json b/package.json index f13416fb9a2..45d63a743bd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.42.0", - "distro": "17f41b9e0d67fc6e53bd63a5f199a0f89ee70e1b", + "version": "1.52.0", + "distro": "8c7413ba1917b58eecc155ef75a6fa8b8e247267", "author": { "name": "Microsoft Corporation" }, @@ -10,13 +10,25 @@ "private": true, "scripts": { "test": "mocha", + "test-browser": "node test/unit/browser/index.js", "preinstall": "node build/npm/preinstall.js", "postinstall": "node build/npm/postinstall.js", "compile": "gulp compile --max_old_space_size=4095", - "watch": "gulp watch --max_old_space_size=4095", + "watch": "concurrently \"npm:watch-client\" \"npm:watch-extensions\"", + "watchd": "deemon yarn watch", + "watch-webd": "deemon yarn watch-web", + "kill-watchd": "deemon --kill yarn watch", + "kill-watch-webd": "deemon --kill yarn watch-web", + "restart-watchd": "deemon --restart yarn watch", + "restart-watch-webd": "deemon --restart yarn watch-web", "watch-client": "gulp watch-client --max_old_space_size=4095", - "monaco-editor-test": "mocha --only-monaco-editor", - "precommit": "node build/gulpfile.hygiene.js", + "watch-clientd": "deemon yarn watch-client", + "kill-watch-clientd": "deemon --kill yarn watch-client", + "watch-extensions": "gulp watch-extensions --max_old_space_size=4095", + "watch-extensionsd": "deemon yarn watch-extensions", + "kill-watch-extensionsd": "deemon --kill yarn watch-extensions", + "mocha": "mocha test/unit/node/all.js --delay", + "precommit": "node build/hygiene.js", "gulp": "gulp --max_old_space_size=8192", "electron": "node build/lib/electron", "7z": "7z", @@ -25,40 +37,43 @@ "smoketest": "cd test/smoke && node test/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", + "tsec-compile-check": "node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit", "valid-layers-check": "node build/lib/layersChecker.js", "strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes", "update-distro": "node build/npm/update-distro.js", - "web": "node scripts/code-web.js", + "web": "node resources/web/code-web.js", + "compile-web": "gulp compile-web --max_old_space_size=4095", + "watch-web": "gulp watch-web --max_old_space_size=4095", "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions" }, "dependencies": { "applicationinsights": "1.0.8", - "chokidar": "3.2.3", - "graceful-fs": "4.1.11", + "chokidar": "3.4.3", + "graceful-fs": "4.2.3", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite": "0.5.0", - "jschardet": "2.1.1", - "keytar": "^4.11.0", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", + "keytar": "^5.5.0", + "minimist": "^1.2.5", "native-is-elevated": "0.4.1", - "native-keymap": "2.1.1", + "native-keymap": "2.2.0", "native-watchdog": "1.3.0", - "node-pty": "^0.10.0-beta2", - "onigasm-umd": "2.2.5", - "semver-umd": "^5.5.5", + "node-pty": "0.10.0-beta17", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", + "tas-client-umd": "0.1.2", "v8-inspect-profiler": "^0.0.20", - "vscode-minimist": "^1.2.2", - "vscode-nsfw": "1.2.8", + "vscode-nsfw": "1.2.9", + "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.5.7", - "vscode-sqlite3": "4.0.9", - "vscode-textmate": "4.4.0", - "xterm": "4.4.0-beta.15", - "xterm-addon-search": "0.4.0-beta4", - "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.5.0-beta.7", + "vscode-ripgrep": "^1.9.0", + "vscode-sqlite3": "4.0.10", + "vscode-textmate": "5.2.0", + "xterm": "4.10.0-beta.4", + "xterm-addon-search": "0.8.0-beta.3", + "xterm-addon-unicode11": "0.3.0-beta.3", + "xterm-addon-webgl": "0.10.0-beta.1", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, @@ -67,13 +82,15 @@ "@types/applicationinsights": "0.20.0", "@types/chokidar": "2.1.3", "@types/cookie": "^0.3.3", + "@types/debug": "^4.1.5", "@types/graceful-fs": "4.1.2", "@types/http-proxy-agent": "^2.0.1", - "@types/iconv-lite": "0.0.1", "@types/keytar": "^4.4.0", + "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", "@types/node": "^12.11.7", "@types/sinon": "^1.16.36", + "@types/trusted-types": "^1.0.6", "@types/vscode-windows-registry": "^1.0.0", "@types/webpack": "^4.4.10", "@types/windows-foreground-love": "^0.3.0", @@ -82,22 +99,25 @@ "@types/winreg": "^1.2.30", "@types/yauzl": "^2.9.1", "@types/yazl": "^2.4.2", - "@typescript-eslint/eslint-plugin": "2.3.2", - "@typescript-eslint/parser": "^2.12.0", + "@typescript-eslint/eslint-plugin": "3.2.0", + "@typescript-eslint/parser": "^3.3.0", "ansi-colors": "^3.2.3", - "asar": "^0.14.0", + "asar": "^3.0.3", "chromium-pickle-js": "^0.2.0", + "concurrently": "^5.2.0", "copy-webpack-plugin": "^4.5.2", - "coveralls": "^2.11.11", "cson-parser": "^1.3.3", + "css-loader": "^3.2.0", "debounce": "^1.0.0", - "electron": "7.1.7", + "deemon": "^1.4.0", + "electron": "9.3.3", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", + "eslint-plugin-mocha": "8.0.0", "event-stream": "3.3.4", - "express": "^4.13.1", "fancy-log": "^1.3.3", "fast-plist": "0.1.2", + "file-loader": "^4.2.0", "glob": "^5.0.13", "gulp": "^4.0.0", "gulp-atom-electron": "^1.22.0", @@ -119,7 +139,7 @@ "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "husky": "^0.13.1", - "innosetup": "5.6.1", + "innosetup": "6.0.5", "is": "^3.1.0", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^4.0.0", @@ -137,35 +157,39 @@ "opn": "^6.0.0", "optimist": "0.3.5", "p-all": "^1.0.0", + "playwright": "1.3.0", "pump": "^1.0.1", "queue": "3.0.6", "rcedit": "^1.1.0", "rimraf": "^2.2.8", "sinon": "^1.17.2", "source-map": "^0.4.4", + "style-loader": "^1.0.0", "ts-loader": "^4.4.2", - "typescript": "3.8.0-beta", + "tsec": "googleinterns/tsec", + "typescript": "^4.1.0-dev.20201018", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.37.0", + "vscode-debugprotocol": "1.41.0", "vscode-nls-dev": "^3.3.1", - "webpack": "^4.16.5", - "webpack-cli": "^3.3.8", - "webpack-stream": "^5.1.1" + "webpack": "^4.43.0", + "webpack-cli": "^3.3.12", + "webpack-stream": "^5.2.1", + "yaserver": "^0.2.0" }, "repository": { "type": "git", - "url": "https://github.com/Microsoft/vscode.git" + "url": "https://github.com/microsoft/vscode.git" }, "bugs": { - "url": "https://github.com/Microsoft/vscode/issues" + "url": "https://github.com/microsoft/vscode/issues" }, "optionalDependencies": { "vscode-windows-ca-certs": "0.2.0", - "vscode-windows-registry": "1.0.2", + "vscode-windows-registry": "1.0.3", "windows-foreground-love": "0.2.0", "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" diff --git a/product.json b/product.json index 759d7655333..7cab6d1b9f3 100644 --- a/product.json +++ b/product.json @@ -5,22 +5,136 @@ "dataFolderName": ".vscode-oss", "win32MutexName": "vscodeoss", "licenseName": "MIT", - "licenseUrl": "https://github.com/Microsoft/vscode/blob/master/LICENSE.txt", + "licenseUrl": "https://github.com/microsoft/vscode/blob/master/LICENSE.txt", "win32DirName": "Microsoft Code OSS", "win32NameVersion": "Microsoft Code OSS", "win32RegValueName": "CodeOSS", "win32AppId": "{{E34003BB-9E10-4501-8C11-BE3FAA83F23F}", "win32x64AppId": "{{D77B7E06-80BA-4137-BCF4-654B95CCEBC5}", + "win32arm64AppId": "{{D1ACE434-89C5-48D1-88D3-E2991DF85475}", "win32UserAppId": "{{C6065F05-9603-4FC4-8101-B9781A25D88E}", - "win32x64UserAppId": "{{C6065F05-9603-4FC4-8101-B9781A25D88E}", + "win32x64UserAppId": "{{CC6B787D-37A0-49E8-AE24-8559A032BE0C}", + "win32arm64UserAppId": "{{3AEBF0C8-F733-4AD4-BADE-FDB816D53D7B}", "win32AppUserModelId": "Microsoft.CodeOSS", "win32ShellNameShort": "C&ode - OSS", "darwinBundleIdentifier": "com.visualstudio.code.oss", "linuxIconName": "com.visualstudio.code.oss", "licenseFileName": "LICENSE.txt", - "reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new", + "reportIssueUrl": "https://github.com/microsoft/vscode/issues/new", "urlProtocol": "code-oss", "extensionAllowedProposedApi": [ - "ms-vscode.references-view" + "ms-vscode.vscode-js-profile-flame", + "ms-vscode.vscode-js-profile-table", + "ms-vscode.references-view", + "ms-vscode.github-browser" + ], + "builtInExtensions": [ + { + "name": "ms-vscode.node-debug", + "version": "1.44.14", + "repo": "https://github.com/microsoft/vscode-node-debug", + "metadata": { + "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + }, + { + "name": "ms-vscode.node-debug2", + "version": "1.42.5", + "repo": "https://github.com/microsoft/vscode-node-debug2", + "metadata": { + "id": "36d19e17-7569-4841-a001-947eb18602b2", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + }, + { + "name": "ms-vscode.references-view", + "version": "0.0.71", + "repo": "https://github.com/microsoft/vscode-reference-view", + "metadata": { + "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + }, + { + "name": "ms-vscode.js-debug-companion", + "version": "1.0.8", + "repo": "https://github.com/microsoft/vscode-js-debug-companion", + "metadata": { + "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + }, + { + "name": "ms-vscode.js-debug", + "version": "1.51.0", + "repo": "https://github.com/microsoft/vscode-js-debug", + "metadata": { + "id": "25629058-ddac-4e17-abba-74678e126c5d", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + }, + { + "name": "ms-vscode.vscode-js-profile-table", + "version": "0.0.11", + "repo": "https://github.com/microsoft/vscode-js-debug", + "metadata": { + "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + } + ], + "webBuiltInExtensions": [ + { + "name": "ms-vscode.github-browser", + "version": "0.0.13", + "repo": "https://github.com/microsoft/vscode-github-browser", + "metadata": { + "id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } + } ] } diff --git a/remote/.yarnrc b/remote/.yarnrc index 1e16cde724c..c1a32ce532a 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,3 +1,3 @@ disturl "http://nodejs.org/dist" -target "12.4.0" +target "12.14.1" runtime "node" diff --git a/remote/package.json b/remote/package.json index 95ea409067f..d353586d751 100644 --- a/remote/package.json +++ b/remote/package.json @@ -3,27 +3,27 @@ "version": "0.0.0", "dependencies": { "applicationinsights": "1.0.8", - "chokidar": "3.2.3", + "chokidar": "3.4.3", "cookie": "^0.4.0", - "graceful-fs": "4.1.11", + "graceful-fs": "4.2.3", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite": "0.5.0", - "jschardet": "2.1.1", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", + "minimist": "^1.2.5", "native-watchdog": "1.3.0", - "node-pty": "^0.10.0-beta2", - "onigasm-umd": "2.2.5", - "semver-umd": "^5.5.5", + "node-pty": "0.10.0-beta17", "spdlog": "^0.11.1", - "vscode-minimist": "^1.2.2", - "vscode-nsfw": "1.2.8", + "tas-client-umd": "0.1.2", + "vscode-nsfw": "1.2.9", + "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.5.7", - "vscode-textmate": "4.4.0", - "xterm": "4.4.0-beta.15", - "xterm-addon-search": "0.4.0-beta4", - "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.5.0-beta.7", + "vscode-ripgrep": "^1.9.0", + "vscode-textmate": "5.2.0", + "xterm": "4.10.0-beta.4", + "xterm-addon-search": "0.8.0-beta.3", + "xterm-addon-unicode11": "0.3.0-beta.3", + "xterm-addon-webgl": "0.10.0-beta.1", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 5bf2c6c7d9c..09ecb81a3da 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -2,12 +2,14 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { - "onigasm-umd": "2.2.5", - "semver-umd": "^5.5.5", - "vscode-textmate": "4.4.0", - "xterm": "4.4.0-beta.15", - "xterm-addon-search": "0.4.0-beta4", - "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.5.0-beta.7" + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", + "tas-client-umd": "0.1.2", + "vscode-oniguruma": "1.3.1", + "vscode-textmate": "5.2.0", + "xterm": "4.10.0-beta.4", + "xterm-addon-search": "0.8.0-beta.3", + "xterm-addon-unicode11": "0.3.0-beta.3", + "xterm-addon-webgl": "0.10.0-beta.1" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 12afd3c3d84..e1a49f7e976 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,51 +2,47 @@ # yarn lockfile v1 -nan@^2.14.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== -onigasm-umd@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.5.tgz#f104247334a543accd3f8d641a4d99b3d908d6a1" - integrity sha512-R3qD7hq6i2bBklF+QyjqZl/G4fe7GwtukI28YLH2vuiatqx52tb9vpg2sxwemKc3nF76SgkeyOKJLchBmTm0Aw== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== -oniguruma@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.2.0.tgz#c9a59c1ea7b9fe67e237a02e02139b638856f3af" - integrity sha512-bh+ZLdykY1sdIx8jBp2zpLbVFDBc3XmKH4Ceo2lijNaN1WhEqtnpqFlmtCbRuDB17nJ58RAUStVwfW8e8uEbnA== - dependencies: - nan "^2.14.0" +tas-client-umd@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.2.tgz#fe93ae085f65424292ac79feff4f1add3e50e624" + integrity sha512-rT9BdDCejckqOTQL2ShX67QtTiAUGbmPm5ZTC8giXobBvZC6JuvBVy5G32hoGZ3Q0dpTvMfgpf3iVFNN2F7wzg== -semver-umd@^5.5.5: - version "5.5.5" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" - integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== +vscode-oniguruma@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.3.1.tgz#e2383879c3485b19f533ec34efea9d7a2b14be8f" + integrity sha512-gz6ZBofA7UXafVA+m2Yt2zHKgXC2qedArprIsHAPKByTkwq9l5y/izAGckqxYml7mSbYxTRTfdRwsFq3cwF4LQ== -vscode-textmate@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" - integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== - dependencies: - oniguruma "^7.2.0" +vscode-textmate@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== -xterm-addon-search@0.4.0-beta4: - version "0.4.0-beta4" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0-beta4.tgz#7762ea342c6b4f5e824d83466bd93793c9d7d779" - integrity sha512-TIbEBVhydGIxcyu/CfKJbD+BKHisMGbkAfaWlCPaWis2Xmw8yE7CKrCPn+lhZYl1MdjDVEmb8lQI6WetbC2OZA== +xterm-addon-search@0.8.0-beta.3: + version "0.8.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.8.0-beta.3.tgz#c6c7e36a03706bd43d8bba383511acf9e435aed0" + integrity sha512-EZP97KJIJ4KGQaOPYiiOaRRJst6LOgeEFoQL46WcBl5EWH9pH8qfrv0BHAJ8+6nBV2B9u5M6rzxO1GvLLec19w== -xterm-addon-web-links@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" - integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== +xterm-addon-unicode11@0.3.0-beta.3: + version "0.3.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.3.0-beta.3.tgz#70af2dfb67809258edb62c19e2861f7ce5ccf5cd" + integrity sha512-vaYopnOjn19wCLDCyIWPWLwKR7CvLPxB5YZ3CAxt9qL05o3symxIJJJC0DuCa4GaGKVjNc7EmjRCs5bsJ2O1tw== -xterm-addon-webgl@0.5.0-beta.7: - version "0.5.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0-beta.7.tgz#b7b95a362e942ad6f86fa286d7b7bd8ee3e7cf67" - integrity sha512-v6aCvhm1C6mvaurGwUYQfyhb2cAUyuVnzf3Ob/hy5ebtyzUj4wW0N9NbqDEJk67UeMi1lV2xZqrO5gNeTpVqFA== +xterm-addon-webgl@0.10.0-beta.1: + version "0.10.0-beta.1" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.10.0-beta.1.tgz#e0bf964945a9aa8fc18318ddbd32e56ec99c219e" + integrity sha512-XNZMrmiyFaz3XiPq+LqF0qn2QHpUEwuk+cG53JwpJHnWo3dd2jxoIgHFQUcrnvHIVPZMbTKySIwLCCC9uQVl7Q== -xterm@4.4.0-beta.15: - version "4.4.0-beta.15" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0-beta.15.tgz#5897bf79d29d1a2496ccd54665aded28c341b1cc" - integrity sha512-Dvz1CMCYKeoxPF7uIDznbRgUA2Mct49Bq93K2nnrDU0pDMM3Sf1t9fkEyz59wxSx5XEHVdLS80jywsz4sjXBjQ== +xterm@4.10.0-beta.4: + version "4.10.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.10.0-beta.4.tgz#95efce7a40ec582101ec9777f4ccc6e68e95185e" + integrity sha512-q/yRy2nn4mp1jWZe218TJwlKjXCIr6h28Kw0JMB+lcTeU+MebZ3TrHqlrNVnB+UJfFDOpkw0qfKYfRoV8G/hXA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 387226ccd2a..5ba96e1c5ed 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -9,6 +9,11 @@ agent-base@4: dependencies: es6-promisify "^5.0.0" +agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -64,10 +69,10 @@ buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -chokidar@3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.2.3.tgz#b9270a565d14f02f6bfdd537a6a2bbf5549b8c8c" - integrity sha512-GtrxGuRf6bzHQmXWRepvsGnXpkQkVU+D2/9a7dAe4a7v1NhrfZOZ2oKf76M3nOs46fFYL8D+Q8JYA4GYeJ8Cjw== +chokidar@3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -75,9 +80,9 @@ chokidar@3.2.3: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.2.0" + readdirp "~3.5.0" optionalDependencies: - fsevents "~2.1.1" + fsevents "~2.1.2" cookie@^0.4.0: version "0.4.0" @@ -91,6 +96,13 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" +debug@4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -143,10 +155,10 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" -fsevents@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.1.tgz#74c64e21df71721845d0c44fe54b7f56b82995a9" - integrity sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw== +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== glob-parent@~5.1.0: version "5.1.0" @@ -155,12 +167,7 @@ glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -graceful-fs@4.1.11: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= - -graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@4.2.3, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== @@ -181,12 +188,18 @@ https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" -iconv-lite@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" - integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== +https-proxy-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" + integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== dependencies: - safer-buffer ">= 2.1.2 < 3" + agent-base "5" + debug "4" + +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== ip@^1.1.5: version "1.1.5" @@ -217,10 +230,10 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -jschardet@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" - integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== jsonfile@^4.0.0: version "4.0.0" @@ -244,6 +257,11 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -256,6 +274,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + nan@^2.10.0, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -271,10 +294,10 @@ node-addon-api@1.6.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== -node-pty@^0.10.0-beta2: - version "0.10.0-beta2" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta2.tgz#6fd0d2fbbe881869e4e19795a05c557ac958da81" - integrity sha512-IU2lzlPUZ+gKG7pHJjzBHpnuwPTxWGgT3iyQicZfdL7dwLvP5cm00QxavAXCInBmRkOMhvM4aBSKvfzqQnCDBA== +node-pty@0.10.0-beta17: + version "0.10.0-beta17" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta17.tgz#962d4a3f4dc6772385e0cad529c209cef3bc79e6" + integrity sha512-tn7EANQacnAvnOQCImvgag1DL0tVmUoY/1yIZbh3u/BBpvCcGHLZJNn7TXheodRLr6hmGSUS2VbfcUr9p0gOug== dependencies: nan "^2.14.0" @@ -283,18 +306,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -onigasm-umd@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.5.tgz#f104247334a543accd3f8d641a4d99b3d908d6a1" - integrity sha512-R3qD7hq6i2bBklF+QyjqZl/G4fe7GwtukI28YLH2vuiatqx52tb9vpg2sxwemKc3nF76SgkeyOKJLchBmTm0Aw== - -oniguruma@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.2.0.tgz#c9a59c1ea7b9fe67e237a02e02139b638856f3af" - integrity sha512-bh+ZLdykY1sdIx8jBp2zpLbVFDBc3XmKH4Ceo2lijNaN1WhEqtnpqFlmtCbRuDB17nJ58RAUStVwfW8e8uEbnA== - dependencies: - nan "^2.14.0" - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -305,22 +316,22 @@ picomatch@^2.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== -readdirp@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" - integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== +picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: - picomatch "^2.0.4" - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver-umd@^5.5.5: - version "5.5.5" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" - integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== + picomatch "^2.2.1" semver@^5.3.0: version "5.6.0" @@ -357,6 +368,11 @@ spdlog@^0.11.1: mkdirp "^0.5.1" nan "^2.14.0" +tas-client-umd@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.2.tgz#fe93ae085f65424292ac79feff4f1add3e50e624" + integrity sha512-rT9BdDCejckqOTQL2ShX67QtTiAUGbmPm5ZTC8giXobBvZC6JuvBVy5G32hoGZ3Q0dpTvMfgpf3iVFNN2F7wzg== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -369,21 +385,21 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -vscode-minimist@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/vscode-minimist/-/vscode-minimist-1.2.2.tgz#65403f44f0c6010d259b2271d36eb5c6f4ad8aab" - integrity sha512-DXMNG2QgrXn1jOP12LzjVfvxVkzxv/0Qa27JrMBj/XP2esj+fJ/wP2T4YUH5derj73Lc96dC8F25WyfDUbTpxQ== - -vscode-nsfw@1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.2.8.tgz#1bf452e72ff1304934de63692870d039a2d972af" - integrity sha512-yRLFDk2nwV0Fp+NWJkIbeXkHYIWoTuWC2siz6JfHc21FkRUjDmuI/1rVL6B+MXW15AsSbXnH5dw4Fo9kUyYclw== +vscode-nsfw@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.2.9.tgz#87def7c538ae713d08c88592a8a043d2b2009951" + integrity sha512-VwaB8H1fS0ngl9eAHgFqtF/q3N3J/ZWfI17zvC8Gfj33cL4UrIW/XxdoFYptBZR/MIA6Qj8FZyYu9D/JvlKWXg== dependencies: fs-extra "^7.0.0" lodash.isinteger "^4.0.4" lodash.isundefined "^3.0.1" nan "^2.10.0" +vscode-oniguruma@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.3.1.tgz#e2383879c3485b19f533ec34efea9d7a2b14be8f" + integrity sha512-gz6ZBofA7UXafVA+m2Yt2zHKgXC2qedArprIsHAPKByTkwq9l5y/izAGckqxYml7mSbYxTRTfdRwsFq3cwF4LQ== + vscode-proxy-agent@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.5.2.tgz#0c90d24d353957b841d741da7b2701e3f0a044c4" @@ -394,17 +410,18 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.5.7: - version "1.5.7" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.7.tgz#acb6b548af488a4bca5d0f1bb5faf761343289ce" - integrity sha512-/Vsz/+k8kTvui0q3O74pif9FK0nKopgFTiGNVvxicZANxtSA8J8gUE9GQ/4dpi7D/2yI/YVORszwVskFbz46hQ== - -vscode-textmate@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.4.0.tgz#14032afeb50152e8f53258c95643e555f2948305" - integrity sha512-dFpm2eK0HwEjeFSD1DDh3j0q47bDSVuZt20RiJWxGqjtm73Wu2jip3C2KaZI3dQx/fSeeXCr/uEN4LNaNj7Ytw== +vscode-ripgrep@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db" + integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg== dependencies: - oniguruma "^7.2.0" + https-proxy-agent "^4.0.0" + proxy-from-env "^1.1.0" + +vscode-textmate@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== vscode-windows-ca-certs@0.2.0: version "0.2.0" @@ -418,25 +435,25 @@ vscode-windows-registry@1.0.2: resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a" integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA== -xterm-addon-search@0.4.0-beta4: - version "0.4.0-beta4" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.4.0-beta4.tgz#7762ea342c6b4f5e824d83466bd93793c9d7d779" - integrity sha512-TIbEBVhydGIxcyu/CfKJbD+BKHisMGbkAfaWlCPaWis2Xmw8yE7CKrCPn+lhZYl1MdjDVEmb8lQI6WetbC2OZA== +xterm-addon-search@0.8.0-beta.3: + version "0.8.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.8.0-beta.3.tgz#c6c7e36a03706bd43d8bba383511acf9e435aed0" + integrity sha512-EZP97KJIJ4KGQaOPYiiOaRRJst6LOgeEFoQL46WcBl5EWH9pH8qfrv0BHAJ8+6nBV2B9u5M6rzxO1GvLLec19w== -xterm-addon-web-links@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" - integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== +xterm-addon-unicode11@0.3.0-beta.3: + version "0.3.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.3.0-beta.3.tgz#70af2dfb67809258edb62c19e2861f7ce5ccf5cd" + integrity sha512-vaYopnOjn19wCLDCyIWPWLwKR7CvLPxB5YZ3CAxt9qL05o3symxIJJJC0DuCa4GaGKVjNc7EmjRCs5bsJ2O1tw== -xterm-addon-webgl@0.5.0-beta.7: - version "0.5.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0-beta.7.tgz#b7b95a362e942ad6f86fa286d7b7bd8ee3e7cf67" - integrity sha512-v6aCvhm1C6mvaurGwUYQfyhb2cAUyuVnzf3Ob/hy5ebtyzUj4wW0N9NbqDEJk67UeMi1lV2xZqrO5gNeTpVqFA== +xterm-addon-webgl@0.10.0-beta.1: + version "0.10.0-beta.1" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.10.0-beta.1.tgz#e0bf964945a9aa8fc18318ddbd32e56ec99c219e" + integrity sha512-XNZMrmiyFaz3XiPq+LqF0qn2QHpUEwuk+cG53JwpJHnWo3dd2jxoIgHFQUcrnvHIVPZMbTKySIwLCCC9uQVl7Q== -xterm@4.4.0-beta.15: - version "4.4.0-beta.15" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0-beta.15.tgz#5897bf79d29d1a2496ccd54665aded28c341b1cc" - integrity sha512-Dvz1CMCYKeoxPF7uIDznbRgUA2Mct49Bq93K2nnrDU0pDMM3Sf1t9fkEyz59wxSx5XEHVdLS80jywsz4sjXBjQ== +xterm@4.10.0-beta.4: + version "4.10.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.10.0-beta.4.tgz#95efce7a40ec582101ec9777f4ccc6e68e95185e" + integrity sha512-q/yRy2nn4mp1jWZe218TJwlKjXCIr6h28Kw0JMB+lcTeU+MebZ3TrHqlrNVnB+UJfFDOpkw0qfKYfRoV8G/hXA== yauzl@^2.9.2: version "2.10.0" diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 516c05e4ee0..06973937f14 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -1,38 +1,47 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # test that VSCode wasn't installed inside WSL if grep -qi Microsoft /proc/version && [ -z "$DONT_PROMPT_WSL_INSTALL" ]; then - echo "To use VS Code with the Windows Subsystem for Linux, please install VS Code in Windows and uninstall the Linux version in WSL. You can then use the '@@PRODNAME@@' command in a WSL terminal just as you would in a normal command prompt." 1>&2 - read -e -p "Do you want to continue anyways ? [y/N] " YN - - [[ $YN == "n" || $YN == "N" || $YN == "" ]] && exit 1 + echo "To use @@PRODNAME@@ with the Windows Subsystem for Linux, please install @@PRODNAME@@ in Windows and uninstall the Linux version in WSL. You can then use the \`@@NAME@@\` command in a WSL terminal just as you would in a normal command prompt." 1>&2 + printf "Do you want to continue anyway? [y/N] " 1>&2 + read -r YN + YN=$(printf '%s' "$YN" | tr '[:upper:]' '[:lower:]') + case "$YN" in + y | yes ) + ;; + * ) + exit 1 + ;; + esac + echo "To no longer see this prompt, start @@PRODNAME@@ with the environment variable DONT_PROMPT_WSL_INSTALL defined." 1>&2 fi - # If root, ensure that --user-data-dir or --file-write is specified if [ "$(id -u)" = "0" ]; then - for i in $@ + for i in "$@" do - if [[ $i == --user-data-dir || $i == --user-data-dir=* || $i == --file-write ]]; then - CAN_LAUNCH_AS_ROOT=1 - fi + case "$i" in + --user-data-dir | --user-data-dir=* | --file-write ) + CAN_LAUNCH_AS_ROOT=1 + ;; + esac done if [ -z $CAN_LAUNCH_AS_ROOT ]; then - echo "You are trying to start vscode as a super user which is not recommended. If you really want to, you must specify an alternate user data directory using the --user-data-dir argument." 1>&2 + echo "You are trying to start @@PRODNAME@@ as a super user which isn't recommended. If this was intended, please specify an alternate user data directory using the \`--user-data-dir\` argument." 1>&2 exit 1 fi fi -if [ ! -L $0 ]; then +if [ ! -L "$0" ]; then # if path is not a symlink, find relatively - VSCODE_PATH="$(dirname $0)/.." + VSCODE_PATH="$(dirname "$0")/.." else if command -v readlink >/dev/null; then # if readlink exists, follow the symlink and find relatively - VSCODE_PATH="$(dirname $(readlink -f $0))/.." + VSCODE_PATH="$(dirname "$(readlink -f "$0")")/.." else # else use the standard install location VSCODE_PATH="/usr/share/@@NAME@@" diff --git a/resources/linux/code-workspace.xml b/resources/linux/code-workspace.xml new file mode 100644 index 00000000000..a025612bb2f --- /dev/null +++ b/resources/linux/code-workspace.xml @@ -0,0 +1,7 @@ + + + + @@NAME_LONG@@ Workspace + + + diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop old mode 100644 new mode 100755 index b975e1094a2..62d6bfc47b4 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -8,7 +8,7 @@ Type=Application StartupNotify=false StartupWMClass=@@NAME_SHORT@@ Categories=Utility;TextEditor;Development;IDE; -MimeType=text/plain;inode/directory; +MimeType=text/plain;inode/directory;application/x-@@NAME@@-workspace; Actions=new-empty-window; Keywords=vscode; diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template index 27d36308c88..5a6d7be652b 100644 --- a/resources/linux/debian/control.template +++ b/resources/linux/debian/control.template @@ -1,7 +1,7 @@ Package: @@NAME@@ Version: @@VERSION@@ Section: devel -Depends: libnotify4, libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1 +Depends: libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1, libgbm1 Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 2c4a1fdd1d6..9f26b350999 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -18,6 +18,11 @@ if hash desktop-file-install 2>/dev/null; then desktop-file-install /usr/share/applications/@@NAME@@-url-handler.desktop fi +# Update mimetype database to pickup workspace mimetype +if hash update-mime-database 2>/dev/null; then + update-mime-database /usr/share/mime +fi + if [ "@@NAME@@" != "code-oss" ]; then # Remove the legacy bin command if this is the stable build if [ "@@NAME@@" = "code" ]; then diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template index c43a2b16ae3..238a566c486 100755 --- a/resources/linux/debian/postrm.template +++ b/resources/linux/debian/postrm.template @@ -3,4 +3,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -rm -f /usr/bin/@@NAME@@ \ No newline at end of file +rm -f /usr/bin/@@NAME@@ + +# Update mimetype database for removed workspace mimetype +if hash update-mime-database 2>/dev/null; then + update-mime-database /usr/share/mime +fi diff --git a/resources/linux/rpm/code.spec.template b/resources/linux/rpm/code.spec.template index 93e20cd4825..3407d4a0c51 100644 --- a/resources/linux/rpm/code.spec.template +++ b/resources/linux/rpm/code.spec.template @@ -20,9 +20,11 @@ mkdir -p %{buildroot}/usr/share/applications mkdir -p %{buildroot}/usr/share/pixmaps mkdir -p %{buildroot}/usr/share/bash-completion/completions mkdir -p %{buildroot}/usr/share/zsh/site-functions +mkdir -p %{buildroot}/usr/share/mime/packages cp -r usr/share/@@NAME@@/* %{buildroot}/usr/share/@@NAME@@ cp -r usr/share/applications/@@NAME@@.desktop %{buildroot}/usr/share/applications cp -r usr/share/applications/@@NAME@@-url-handler.desktop %{buildroot}/usr/share/applications +cp -r usr/share/mime/packages/@@NAME@@-workspace.xml %{buildroot}/usr/share/mime/packages/@@NAME@@-workspace.xml cp -r usr/share/pixmaps/@@ICON@@.png %{buildroot}/usr/share/pixmaps cp usr/share/bash-completion/completions/@@NAME@@ %{buildroot}/usr/share/bash-completion/completions/@@NAME@@ cp usr/share/zsh/site-functions/_@@NAME@@ %{buildroot}/usr/share/zsh/site-functions/_@@NAME@@ @@ -46,17 +48,24 @@ ln -sf /usr/share/@@NAME@@/bin/@@NAME@@ %{_bindir}/@@NAME@@ # fi #fi +# Update mimetype database to pickup workspace mimetype +update-mime-database /usr/share/mime &> /dev/null || : + %postun if [ $1 = 0 ]; then rm -f /usr/bin/@@NAME@@ fi +# Update mimetype database for removed workspace mimetype +update-mime-database /usr/share/mime &> /dev/null || : + %files %defattr(-,root,root) /usr/share/@@NAME@@/ /usr/share/applications/@@NAME@@.desktop /usr/share/applications/@@NAME@@-url-handler.desktop +/usr/share/mime/packages/@@NAME@@-workspace.xml /usr/share/pixmaps/@@ICON@@.png /usr/share/bash-completion/completions/@@NAME@@ /usr/share/zsh/site-functions/_@@NAME@@ diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index 34f127e1ae5..7f95cd3e5db 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -62,6 +62,137 @@ "libc.so.6(GLIBC_2.9)(64bit)", "libxcb.so.1()(64bit)", "libxkbfile.so.1()(64bit)", - "libsecret-1.so.0()(64bit)" + "libsecret-1.so.0()(64bit)", + "libgbm.so.1()(64bit)" + ], + "aarch64": [ + "libpthread.so.0()(aarch64)", + "libpthread.so.0(GLIBC_2.2.5)(aarch64)", + "libpthread.so.0(GLIBC_2.3.2)(aarch64)", + "libpthread.so.0(GLIBC_2.3.3)(aarch64)", + "libgtk-3.so.0()(aarch64)", + "libgdk-x11-2.0.so.0()(aarch64)", + "libatk-1.0.so.0()(aarch64)", + "libgio-2.0.so.0()(aarch64)", + "libpangocairo-1.0.so.0()(aarch64)", + "libgdk_pixbuf-2.0.so.0()(aarch64)", + "libcairo.so.2()(aarch64)", + "libpango-1.0.so.0()(aarch64)", + "libfreetype.so.6()(aarch64)", + "libfontconfig.so.1()(aarch64)", + "libgobject-2.0.so.0()(aarch64)", + "libdbus-1.so.3()(aarch64)", + "libXi.so.6()(aarch64)", + "libXcursor.so.1()(aarch64)", + "libXdamage.so.1()(aarch64)", + "libXrandr.so.2()(aarch64)", + "libXcomposite.so.1()(aarch64)", + "libXext.so.6()(aarch64)", + "libXfixes.so.3()(aarch64)", + "libXrender.so.1()(aarch64)", + "libX11.so.6()(aarch64)", + "libXss.so.1()(aarch64)", + "libXtst.so.6()(aarch64)", + "libgmodule-2.0.so.0()(aarch64)", + "librt.so.1()(aarch64)", + "libglib-2.0.so.0()(aarch64)", + "libnss3.so()(aarch64)", + "libnssutil3.so()(aarch64)", + "libsmime3.so()(aarch64)", + "libnspr4.so()(aarch64)", + "libasound.so.2()(aarch64)", + "libcups.so.2()(aarch64)", + "libdl.so.2()(aarch64)", + "libexpat.so.1()(aarch64)", + "libstdc++.so.6()(aarch64)", + "libstdc++.so.6(GLIBCXX_3.4)(aarch64)", + "libstdc++.so.6(GLIBCXX_3.4.10)(aarch64)", + "libstdc++.so.6(GLIBCXX_3.4.11)(aarch64)", + "libstdc++.so.6(GLIBCXX_3.4.14)(aarch64)", + "libstdc++.so.6(GLIBCXX_3.4.15)(aarch64)", + "libstdc++.so.6(GLIBCXX_3.4.9)(aarch64)", + "libm.so.6()(aarch64)", + "libm.so.6(GLIBC_2.2.5)(aarch64)", + "libgcc_s.so.1()(aarch64)", + "libgcc_s.so.1(GCC_3.0)(aarch64)", + "libgcc_s.so.1(GCC_4.0.0)(aarch64)", + "libc.so.6()(aarch64)", + "libc.so.6(GLIBC_2.11)(aarch64)", + "libc.so.6(GLIBC_2.2.5)(aarch64)", + "libc.so.6(GLIBC_2.3)(aarch64)", + "libc.so.6(GLIBC_2.3.2)(aarch64)", + "libc.so.6(GLIBC_2.3.4)(aarch64)", + "libc.so.6(GLIBC_2.4)(aarch64)", + "libc.so.6(GLIBC_2.6)(aarch64)", + "libc.so.6(GLIBC_2.7)(aarch64)", + "libc.so.6(GLIBC_2.9)(aarch64)", + "libxcb.so.1()(aarch64)", + "libxkbfile.so.1()(aarch64)", + "libsecret-1.so.0()(aarch64)" + ], + "armv7hl": [ + "libpthread.so.0()(armv7hl)", + "libpthread.so.0(GLIBC_2.2.5)(armv7hl)", + "libpthread.so.0(GLIBC_2.3.2)(armv7hl)", + "libpthread.so.0(GLIBC_2.3.3)(armv7hl)", + "libgtk-3.so.0()(armv7hl)", + "libgdk-x11-2.0.so.0()(armv7hl)", + "libatk-1.0.so.0()(armv7hl)", + "libgio-2.0.so.0()(armv7hl)", + "libpangocairo-1.0.so.0()(armv7hl)", + "libgdk_pixbuf-2.0.so.0()(armv7hl)", + "libcairo.so.2()(armv7hl)", + "libpango-1.0.so.0()(armv7hl)", + "libfreetype.so.6()(armv7hl)", + "libfontconfig.so.1()(armv7hl)", + "libgobject-2.0.so.0()(armv7hl)", + "libdbus-1.so.3()(armv7hl)", + "libXi.so.6()(armv7hl)", + "libXcursor.so.1()(armv7hl)", + "libXdamage.so.1()(armv7hl)", + "libXrandr.so.2()(armv7hl)", + "libXcomposite.so.1()(armv7hl)", + "libXext.so.6()(armv7hl)", + "libXfixes.so.3()(armv7hl)", + "libXrender.so.1()(armv7hl)", + "libX11.so.6()(armv7hl)", + "libXss.so.1()(armv7hl)", + "libXtst.so.6()(armv7hl)", + "libgmodule-2.0.so.0()(armv7hl)", + "librt.so.1()(armv7hl)", + "libglib-2.0.so.0()(armv7hl)", + "libnss3.so()(armv7hl)", + "libnssutil3.so()(armv7hl)", + "libsmime3.so()(armv7hl)", + "libnspr4.so()(armv7hl)", + "libasound.so.2()(armv7hl)", + "libcups.so.2()(armv7hl)", + "libdl.so.2()(armv7hl)", + "libexpat.so.1()(armv7hl)", + "libstdc++.so.6()(armv7hl)", + "libstdc++.so.6(GLIBCXX_3.4)(armv7hl)", + "libstdc++.so.6(GLIBCXX_3.4.10)(armv7hl)", + "libstdc++.so.6(GLIBCXX_3.4.11)(armv7hl)", + "libstdc++.so.6(GLIBCXX_3.4.14)(armv7hl)", + "libstdc++.so.6(GLIBCXX_3.4.15)(armv7hl)", + "libstdc++.so.6(GLIBCXX_3.4.9)(armv7hl)", + "libm.so.6()(armv7hl)", + "libm.so.6(GLIBC_2.2.5)(armv7hl)", + "libgcc_s.so.1()(armv7hl)", + "libgcc_s.so.1(GCC_3.0)(armv7hl)", + "libgcc_s.so.1(GCC_4.0.0)(armv7hl)", + "libc.so.6()(armv7hl)", + "libc.so.6(GLIBC_2.11)(armv7hl)", + "libc.so.6(GLIBC_2.2.5)(armv7hl)", + "libc.so.6(GLIBC_2.3)(armv7hl)", + "libc.so.6(GLIBC_2.3.2)(armv7hl)", + "libc.so.6(GLIBC_2.3.4)(armv7hl)", + "libc.so.6(GLIBC_2.4)(armv7hl)", + "libc.so.6(GLIBC_2.6)(armv7hl)", + "libc.so.6(GLIBC_2.7)(armv7hl)", + "libc.so.6(GLIBC_2.9)(armv7hl)", + "libxcb.so.1()(armv7hl)", + "libxkbfile.so.1()(armv7hl)", + "libsecret-1.so.0()(armv7hl)" ] -} \ No newline at end of file +} diff --git a/resources/linux/snap/electron-launch b/resources/linux/snap/electron-launch index 2a1c4395187..9f4eb6a23b8 100755 --- a/resources/linux/snap/electron-launch +++ b/resources/linux/snap/electron-launch @@ -2,7 +2,7 @@ # On Fedora $SNAP is under /var and there is some magic to map it to /snap. # We need to handle that case and reset $SNAP -SNAP=$(echo $SNAP | sed -e "s|/var/lib/snapd||g") +SNAP=$(echo "$SNAP" | sed -e "s|/var/lib/snapd||g") if [ "$SNAP_ARCH" == "amd64" ]; then ARCH="x86_64-linux-gnu" @@ -14,21 +14,21 @@ else ARCH="$SNAP_ARCH-linux-gnu" fi -export XDG_CACHE_HOME=$SNAP_USER_COMMON/.cache -if [[ -d $SNAP_USER_DATA/.cache && ! -e $XDG_CACHE_HOME ]]; then +GDK_CACHE_DIR="$SNAP_USER_COMMON/.cache" +if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$GDK_CACHE_DIR" ]]; then # the .cache directory used to be stored under $SNAP_USER_DATA, migrate it - mv $SNAP_USER_DATA/.cache $SNAP_USER_COMMON/ + mv "$SNAP_USER_DATA/.cache" "$SNAP_USER_COMMON/" fi -mkdir -p $XDG_CACHE_HOME +[ ! -d "$GDK_CACHE_DIR" ] && mkdir -p "$GDK_CACHE_DIR" # Gdk-pixbuf loaders -export GDK_PIXBUF_MODULE_FILE=$XDG_CACHE_HOME/gdk-pixbuf-loaders.cache -export GDK_PIXBUF_MODULEDIR=$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders -if [ -f $SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders ]; then - $SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders > $GDK_PIXBUF_MODULE_FILE +export GDK_PIXBUF_MODULE_FILE="$GDK_CACHE_DIR/gdk-pixbuf-loaders.cache" +export GDK_PIXBUF_MODULEDIR="$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders" +if [ -f "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" ]; then + "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" > "$GDK_PIXBUF_MODULE_FILE" fi # Create $XDG_RUNTIME_DIR if not exists (to be removed when https://pad.lv/1656340 is fixed) -[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700 +[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p "$XDG_RUNTIME_DIR" -m 700 exec "$@" diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index c39e5f4f849..046158e888e 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -24,12 +24,14 @@ parts: plugin: dump source: . stage-packages: + - ibus-gtk3 - fcitx-frontend-gtk3 - gvfs-libs - libasound2 - libgconf-2-4 - libglib2.0-bin - libgnome-keyring0 + - libgbm1 - libgtk-3-0 - libnotify4 - libnspr4 diff --git a/resources/web/callback.html b/resources/web/callback.html new file mode 100644 index 00000000000..81f217e980b --- /dev/null +++ b/resources/web/callback.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + Visual Studio Code + + + + + + + + Visual Studio Code + +
+
+ You can close this page now. +
+
+ + + diff --git a/resources/web/code-web.js b/resources/web/code-web.js new file mode 100644 index 00000000000..9be3bc2d811 --- /dev/null +++ b/resources/web/code-web.js @@ -0,0 +1,617 @@ +#!/usr/bin/env node + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// @ts-check + +const http = require('http'); +const url = require('url'); +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const opn = require('opn'); +const minimist = require('minimist'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); +const remote = require('gulp-remote-retry-src'); +const vfs = require('vinyl-fs'); +const uuid = require('uuid'); + +const extensions = require('../../build/lib/extensions'); + +const APP_ROOT = path.join(__dirname, '..', '..'); +const BUILTIN_EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions'); +const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInExtensions'); +const WEB_DEV_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInWebDevExtensions'); +const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); + +const WEB_PLAYGROUND_VERSION = '0.0.10'; + +const args = minimist(process.argv, { + boolean: [ + 'no-launch', + 'help', + 'verbose', + 'wrap-iframe', + 'enable-sync', + 'trusted-types' + ], + string: [ + 'scheme', + 'host', + 'port', + 'local_port', + 'extension', + 'github-auth' + ], +}); + +if (args.help) { + console.log( + 'yarn web [options]\n' + + ' --no-launch Do not open VSCode web in the browser\n' + + ' --wrap-iframe Wrap the Web Worker Extension Host in an iframe\n' + + ' --trusted-types Enable trusted types (report only)\n' + + ' --enable-sync Enable sync by default\n' + + ' --scheme Protocol (https or http)\n' + + ' --host Remote host\n' + + ' --port Remote/Local port\n' + + ' --local_port Local port override\n' + + ' --secondary-port Secondary port\n' + + ' --extension Path of an extension to include\n' + + ' --github-auth Github authentication token\n' + + ' --verbose Print out more information\n' + + ' --help\n' + + '[Example]\n' + + ' yarn web --scheme https --host example.com --port 8080 --local_port 30000' + ); + process.exit(0); +} + +const PORT = args.port || process.env.PORT || 8080; +const LOCAL_PORT = args.local_port || process.env.LOCAL_PORT || PORT; +const SECONDARY_PORT = args['secondary-port'] || (parseInt(PORT, 10) + 1); +const SCHEME = args.scheme || process.env.VSCODE_SCHEME || 'http'; +const HOST = args.host || 'localhost'; +const AUTHORITY = process.env.VSCODE_AUTHORITY || `${HOST}:${PORT}`; + +const exists = (path) => util.promisify(fs.exists)(path); +const readFile = (path) => util.promisify(fs.readFile)(path); + +async function getBuiltInExtensionInfos() { + const allExtensions = []; + /** @type {Object.} */ + const locations = {}; + + const [localExtensions, marketplaceExtensions, webDevExtensions] = await Promise.all([ + extensions.scanBuiltinExtensions(BUILTIN_EXTENSIONS_ROOT), + extensions.scanBuiltinExtensions(BUILTIN_MARKETPLACE_EXTENSIONS_ROOT), + ensureWebDevExtensions().then(() => extensions.scanBuiltinExtensions(WEB_DEV_EXTENSIONS_ROOT)) + ]); + for (const ext of localExtensions) { + allExtensions.push(ext); + locations[ext.extensionPath] = path.join(BUILTIN_EXTENSIONS_ROOT, ext.extensionPath); + } + for (const ext of marketplaceExtensions) { + allExtensions.push(ext); + locations[ext.extensionPath] = path.join(BUILTIN_MARKETPLACE_EXTENSIONS_ROOT, ext.extensionPath); + } + for (const ext of webDevExtensions) { + allExtensions.push(ext); + locations[ext.extensionPath] = path.join(WEB_DEV_EXTENSIONS_ROOT, ext.extensionPath); + } + for (const ext of allExtensions) { + if (ext.packageJSON.browser) { + let mainFilePath = path.join(locations[ext.extensionPath], ext.packageJSON.browser); + if (path.extname(mainFilePath) !== '.js') { + mainFilePath += '.js'; + } + if (!await exists(mainFilePath)) { + fancyLog(`${ansiColors.red('Error')}: Could not find ${mainFilePath}. Use ${ansiColors.cyan('yarn watch-web')} to build the built-in extensions.`); + } + } + } + return { extensions: allExtensions, locations }; +} + +async function ensureWebDevExtensions() { + + // Playground (https://github.com/microsoft/vscode-web-playground) + const webDevPlaygroundRoot = path.join(WEB_DEV_EXTENSIONS_ROOT, 'vscode-web-playground'); + const webDevPlaygroundExists = await exists(webDevPlaygroundRoot); + + let downloadPlayground = false; + if (webDevPlaygroundExists) { + try { + const webDevPlaygroundPackageJson = JSON.parse(((await readFile(path.join(webDevPlaygroundRoot, 'package.json'))).toString())); + if (webDevPlaygroundPackageJson.version !== WEB_PLAYGROUND_VERSION) { + downloadPlayground = true; + } + } catch (error) { + downloadPlayground = true; + } + } else { + downloadPlayground = true; + } + + if (downloadPlayground) { + if (args.verbose) { + fancyLog(`${ansiColors.magenta('Web Development extensions')}: Downloading vscode-web-playground to ${webDevPlaygroundRoot}`); + } + await new Promise((resolve, reject) => { + remote(['package.json', 'dist/extension.js', 'dist/extension.js.map'], { + base: 'https://raw.githubusercontent.com/microsoft/vscode-web-playground/main/' + }).pipe(vfs.dest(webDevPlaygroundRoot)).on('end', resolve).on('error', reject); + }); + } else { + if (args.verbose) { + fancyLog(`${ansiColors.magenta('Web Development extensions')}: Using existing vscode-web-playground in ${webDevPlaygroundRoot}`); + } + } +} + +async function getCommandlineProvidedExtensionInfos() { + const extensions = []; + + /** @type {Object.} */ + const locations = {}; + + let extensionArg = args['extension']; + if (!extensionArg) { + return { extensions, locations }; + } + + const extensionPaths = Array.isArray(extensionArg) ? extensionArg : [extensionArg]; + await Promise.all(extensionPaths.map(async extensionPath => { + extensionPath = path.resolve(process.cwd(), extensionPath); + const packageJSON = await getExtensionPackageJSON(extensionPath); + if (packageJSON) { + const extensionId = `${packageJSON.publisher}.${packageJSON.name}`; + extensions.push({ + packageJSON, + extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/extension/${extensionId}` } + }); + locations[extensionId] = extensionPath; + } + })); + return { extensions, locations }; +} + +async function getExtensionPackageJSON(extensionPath) { + + const packageJSONPath = path.join(extensionPath, 'package.json'); + if (await exists(packageJSONPath)) { + try { + let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString()); + if (packageJSON.main && !packageJSON.browser) { + return; // unsupported + } + + const packageNLSPath = path.join(extensionPath, 'package.nls.json'); + const packageNLSExists = await exists(packageNLSPath); + if (packageNLSExists) { + packageJSON = extensions.translatePackageJSON(packageJSON, packageNLSPath); // temporary, until fixed in core + } + + return packageJSON; + } catch (e) { + console.log(e); + } + } + return undefined; +} + +const builtInExtensionsPromise = getBuiltInExtensionInfos(); +const commandlineProvidedExtensionsPromise = getCommandlineProvidedExtensionInfos(); + +const mapCallbackUriToRequestId = new Map(); + +/** + * @param req {http.IncomingMessage} + * @param res {http.ServerResponse} + */ +const requestHandler = (req, res) => { + const parsedUrl = url.parse(req.url, true); + const pathname = parsedUrl.pathname; + + try { + if (pathname === '/favicon.ico') { + // favicon + return serveFile(req, res, path.join(APP_ROOT, 'resources', 'win32', 'code.ico')); + } + if (pathname === '/manifest.json') { + // manifest + res.writeHead(200, { 'Content-Type': 'application/json' }); + return res.end(JSON.stringify({ + 'name': 'Code Web - OSS', + 'short_name': 'Code Web - OSS', + 'start_url': '/', + 'lang': 'en-US', + 'display': 'standalone' + })); + } + if (/^\/static\//.test(pathname)) { + // static requests + return handleStatic(req, res, parsedUrl); + } + if (/^\/extension\//.test(pathname)) { + // default extension requests + return handleExtension(req, res, parsedUrl); + } + if (pathname === '/') { + // main web + return handleRoot(req, res); + } else if (pathname === '/callback') { + // callback support + return handleCallback(req, res, parsedUrl); + } else if (pathname === '/fetch-callback') { + // callback fetch support + return handleFetchCallback(req, res, parsedUrl); + } + + return serveError(req, res, 404, 'Not found.'); + } catch (error) { + console.error(error.toString()); + + return serveError(req, res, 500, 'Internal Server Error.'); + } +}; + +const server = http.createServer(requestHandler); +server.listen(LOCAL_PORT, () => { + if (LOCAL_PORT !== PORT) { + console.log(`Operating location at http://0.0.0.0:${LOCAL_PORT}`); + } + console.log(`Web UI available at ${SCHEME}://${AUTHORITY}`); +}); +server.on('error', err => { + console.error(`Error occurred in server:`); + console.error(err); +}); + +const secondaryServer = http.createServer(requestHandler); +secondaryServer.listen(SECONDARY_PORT, () => { + console.log(`Secondary server available at ${SCHEME}://${HOST}:${SECONDARY_PORT}`); +}); +secondaryServer.on('error', err => { + console.error(`Error occurred in server:`); + console.error(err); +}); + +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl + */ +async function handleStatic(req, res, parsedUrl) { + + if (/^\/static\/extensions\//.test(parsedUrl.pathname)) { + const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/static/extensions/'.length)); + const filePath = getExtensionFilePath(relativePath, (await builtInExtensionsPromise).locations); + const responseHeaders = { + 'Access-Control-Allow-Origin': '*' + }; + if (!filePath) { + return serveError(req, res, 400, `Bad request.`, responseHeaders); + } + return serveFile(req, res, filePath, responseHeaders); + } + + // Strip `/static/` from the path + const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static/'.length))); + + return serveFile(req, res, path.join(APP_ROOT, relativeFilePath)); +} + +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl + */ +async function handleExtension(req, res, parsedUrl) { + // Strip `/extension/` from the path + const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length)); + const filePath = getExtensionFilePath(relativePath, (await commandlineProvidedExtensionsPromise).locations); + const responseHeaders = { + 'Access-Control-Allow-Origin': '*' + }; + if (!filePath) { + return serveError(req, res, 400, `Bad request.`, responseHeaders); + } + return serveFile(req, res, filePath, responseHeaders); +} + +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + */ +async function handleRoot(req, res) { + let folderUri = { scheme: 'memfs', path: `/sample-folder` }; + + const match = req.url && req.url.match(/\?([^#]+)/); + if (match) { + const qs = new URLSearchParams(match[1]); + + let gh = qs.get('gh'); + if (gh) { + if (gh.startsWith('/')) { + gh = gh.substr(1); + } + + const [owner, repo, ...branch] = gh.split('/', 3); + const ref = branch.join('/'); + folderUri = { scheme: 'github', authority: `${owner}+${repo}${ref ? `+${ref}` : ''}`, path: '/' }; + } else { + let cs = qs.get('cs'); + if (cs) { + if (cs.startsWith('/')) { + cs = cs.substr(1); + } + + const [owner, repo, ...branch] = cs.split('/'); + const ref = branch.join('/'); + folderUri = { scheme: 'codespace', authority: `${owner}+${repo}${ref ? `+${ref}` : ''}`, path: '/' }; + } + } + } + + const { extensions: builtInExtensions } = await builtInExtensionsPromise; + const { extensions: staticExtensions, locations: staticLocations } = await commandlineProvidedExtensionsPromise; + + const dedupedBuiltInExtensions = []; + for (const builtInExtension of builtInExtensions) { + const extensionId = `${builtInExtension.packageJSON.publisher}.${builtInExtension.packageJSON.name}`; + if (staticLocations[extensionId]) { + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: Ignoring built-in ${extensionId} because it was overridden via --extension argument`); + continue; + } + + dedupedBuiltInExtensions.push(builtInExtension); + } + + if (args.verbose) { + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${dedupedBuiltInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`); + fancyLog(`${ansiColors.magenta('Additional extensions')}: ${staticExtensions.map(e => path.basename(e.extensionLocation.path)).join(', ') || 'None'}`); + } + + const webConfigJSON = { + folderUri: folderUri, + staticExtensions, + enableSyncByDefault: args['enable-sync'], + webWorkerExtensionHostIframeSrc: `${SCHEME}://${HOST}:${SECONDARY_PORT}/static/out/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html` + }; + if (args['wrap-iframe']) { + webConfigJSON._wrapWebWorkerExtHostInIframe = true; + } + if (req.headers['x-forwarded-host']) { + // support for running in codespace => no iframe wrapping + delete webConfigJSON.webWorkerExtensionHostIframeSrc; + } + + const authSessionInfo = args['github-auth'] ? { + id: uuid.v4(), + providerId: 'github', + accessToken: args['github-auth'], + scopes: [['user:email'], ['repo']] + } : undefined; + + const data = (await readFile(WEB_MAIN)).toString() + .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => escapeAttribute(JSON.stringify(webConfigJSON))) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied + .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(dedupedBuiltInExtensions))) + .replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '') + .replace('{{WEBVIEW_ENDPOINT}}', ''); + + + const headers = { 'Content-Type': 'text/html' }; + if (args['trusted-types']) { + headers['Content-Security-Policy-Report-Only'] = 'require-trusted-types-for \'script\';'; + } + + res.writeHead(200, headers); + return res.end(data); +} + +/** + * Handle HTTP requests for /callback + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl +*/ +async function handleCallback(req, res, parsedUrl) { + const wellKnownKeys = ['vscode-requestId', 'vscode-scheme', 'vscode-authority', 'vscode-path', 'vscode-query', 'vscode-fragment']; + const [requestId, vscodeScheme, vscodeAuthority, vscodePath, vscodeQuery, vscodeFragment] = wellKnownKeys.map(key => { + const value = getFirstQueryValue(parsedUrl, key); + if (value) { + return decodeURIComponent(value); + } + + return value; + }); + + if (!requestId) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + return res.end(`Bad request.`); + } + + // merge over additional query values that we got + let query = vscodeQuery; + let index = 0; + getFirstQueryValues(parsedUrl, wellKnownKeys).forEach((value, key) => { + if (!query) { + query = ''; + } + + const prefix = (index++ === 0) ? '' : '&'; + query += `${prefix}${key}=${value}`; + }); + + + // add to map of known callbacks + mapCallbackUriToRequestId.set(requestId, JSON.stringify({ scheme: vscodeScheme || 'code-oss', authority: vscodeAuthority, path: vscodePath, query, fragment: vscodeFragment })); + return serveFile(req, res, path.join(APP_ROOT, 'resources', 'web', 'callback.html'), { 'Content-Type': 'text/html' }); +} + +/** + * Handle HTTP requests for /fetch-callback + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl +*/ +async function handleFetchCallback(req, res, parsedUrl) { + const requestId = getFirstQueryValue(parsedUrl, 'vscode-requestId'); + if (!requestId) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + return res.end(`Bad request.`); + } + + const knownCallbackUri = mapCallbackUriToRequestId.get(requestId); + if (knownCallbackUri) { + mapCallbackUriToRequestId.delete(requestId); + } + + res.writeHead(200, { 'Content-Type': 'text/json' }); + return res.end(knownCallbackUri); +} + +/** + * @param {import('url').UrlWithParsedQuery} parsedUrl + * @param {string} key + * @returns {string | undefined} +*/ +function getFirstQueryValue(parsedUrl, key) { + const result = parsedUrl.query[key]; + return Array.isArray(result) ? result[0] : result; +} + +/** + * @param {import('url').UrlWithParsedQuery} parsedUrl + * @param {string[] | undefined} ignoreKeys + * @returns {Map} +*/ +function getFirstQueryValues(parsedUrl, ignoreKeys) { + const queryValues = new Map(); + + for (const key in parsedUrl.query) { + if (ignoreKeys && ignoreKeys.indexOf(key) >= 0) { + continue; + } + + const value = getFirstQueryValue(parsedUrl, key); + if (typeof value === 'string') { + queryValues.set(key, value); + } + } + + return queryValues; +} + +/** + * @param {string} value + */ +function escapeAttribute(value) { + return value.replace(/"/g, '"'); +} + +/** + * @param {string} relativePath + * @param {Object.} locations + * @returns {string | undefined} +*/ +function getExtensionFilePath(relativePath, locations) { + const firstSlash = relativePath.indexOf('/'); + if (firstSlash === -1) { + return undefined; + } + const extensionId = relativePath.substr(0, firstSlash); + + const extensionPath = locations[extensionId]; + if (!extensionPath) { + return undefined; + } + return path.join(extensionPath, relativePath.substr(firstSlash + 1)); +} + +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {string} errorMessage + */ +function serveError(req, res, errorCode, errorMessage, responseHeaders = Object.create(null)) { + responseHeaders['Content-Type'] = 'text/plain'; + res.writeHead(errorCode, responseHeaders); + res.end(errorMessage); +} + +const textMimeType = { + '.html': 'text/html', + '.js': 'text/javascript', + '.json': 'application/json', + '.css': 'text/css', + '.svg': 'image/svg+xml', +}; + +const mapExtToMediaMimes = { + '.bmp': 'image/bmp', + '.gif': 'image/gif', + '.ico': 'image/x-icon', + '.jpe': 'image/jpg', + '.jpeg': 'image/jpg', + '.jpg': 'image/jpg', + '.png': 'image/png', + '.tga': 'image/x-tga', + '.tif': 'image/tiff', + '.tiff': 'image/tiff', + '.woff': 'application/font-woff' +}; + +/** + * @param {string} forPath + */ +function getMediaMime(forPath) { + const ext = path.extname(forPath); + + return mapExtToMediaMimes[ext.toLowerCase()]; +} + +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {string} filePath + */ +async function serveFile(req, res, filePath, responseHeaders = Object.create(null)) { + try { + + // Sanity checks + filePath = path.normalize(filePath); // ensure no "." and ".." + + const stat = await util.promisify(fs.stat)(filePath); + + // Check if file modified since + const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; // weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) + if (req.headers['if-none-match'] === etag) { + res.writeHead(304); + return res.end(); + } + + // Headers + responseHeaders['Content-Type'] = textMimeType[path.extname(filePath)] || getMediaMime(filePath) || 'text/plain'; + responseHeaders['Etag'] = etag; + + res.writeHead(200, responseHeaders); + + // Data + fs.createReadStream(filePath).pipe(res); + } catch (error) { + console.error(error.toString()); + responseHeaders['Content-Type'] = 'text/plain'; + res.writeHead(404, responseHeaders); + return res.end('Not found'); + } +} + +if (args.launch !== false) { + opn(`${SCHEME}://${HOST}:${PORT}`); +} diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 66e11adc2f0..23fbbc9bf20 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -2,6 +2,10 @@ # # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +if [ "$VSCODE_WSL_DEBUG_INFO" = true ]; then + set -x +fi + COMMIT="@@COMMIT@@" APP_NAME="@@APPNAME@@" QUALITY="@@QUALITY@@" @@ -9,48 +13,46 @@ NAME="@@NAME@@" DATAFOLDER="@@DATAFOLDER@@" VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" ELECTRON="$VSCODE_PATH/$NAME.exe" -if grep -qi Microsoft /proc/version; then - # in a wsl shell - WSL_BUILD=$(uname -r | sed -E 's/^[0-9.]+-([0-9]+)-Microsoft|([0-9]+).([0-9]+).([0-9]+)-microsoft-standard|.*/\1\2\3\4/') - if [ -z "$WSL_BUILD" ]; then - WSL_BUILD=0 - fi - if [ $WSL_BUILD -ge 17063 ]; then - # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 - # WSLPATH is available since WSL build 17046 - # WSLENV is available since WSL build 17063 - export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV - CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") - - # use the Remote WSL extension if installed - WSL_EXT_ID="ms-vscode-remote.remote-wsl" - - if [ $WSL_BUILD -ge 41955 -a $WSL_BUILD -lt 41959 ]; then - # WSL2 workaround for https://github.com/microsoft/WSL/issues/4337 - CWD="$(pwd)" - cd "$VSCODE_PATH" - cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >%TEMP%\\remote-wsl-loc.txt" - WSL_EXT_WLOC=$(cmd.exe /C type %TEMP%\\remote-wsl-loc.txt) - cd "$CWD" +IN_WSL=false +if [ -n "$WSL_DISTRO_NAME" ]; then + # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 + IN_WSL=true +else + WSL_BUILD=$(uname -r | sed -E 's/^[0-9.]+-([0-9]+)-Microsoft.*|.*/\1/') + if [ -n "$WSL_BUILD" ]; then + if [ "$WSL_BUILD" -ge 17063 ]; then + # WSLPATH is available since WSL build 17046 + # WSLENV is available since WSL build 17063 + IN_WSL=true else - ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null - WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt) - fi - if [ -n "$WSL_EXT_WLOC" ]; then - # replace \r\n with \n in WSL_EXT_WLOC - WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh - "$WSL_CODE" "$COMMIT" "$QUALITY" "$ELECTRON" "$APP_NAME" "$DATAFOLDER" "$@" + # If running under older WSL, don't pass cli.js to Electron as + # environment vars cannot be transferred from WSL to Windows + # See: https://github.com/microsoft/BashOnWindows/issues/1363 + # https://github.com/microsoft/BashOnWindows/issues/1494 + "$ELECTRON" "$@" exit $? fi - else - # If running under older WSL, don't pass cli.js to Electron as - # environment vars cannot be transferred from WSL to Windows - # See: https://github.com/Microsoft/BashOnWindows/issues/1363 - # https://github.com/Microsoft/BashOnWindows/issues/1494 - "$ELECTRON" "$@" + fi +fi +if [ $IN_WSL = true ]; then + + export WSLENV="ELECTRON_RUN_AS_NODE/w:$WSLENV" + CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") + + # use the Remote WSL extension if installed + WSL_EXT_ID="ms-vscode-remote.remote-wsl" + + ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null { - const parsedUrl = url.parse(req.url, true); - const pathname = parsedUrl.pathname; - - try { - if (pathname === '/favicon.ico') { - // favicon - return serveFile(req, res, path.join(APP_ROOT, 'resources', 'win32', 'code.ico')); - } - if (pathname === '/manifest.json') { - // manifest - res.writeHead(200, { 'Content-Type': 'application/json' }); - return res.end(JSON.stringify({ - "name": "Code Web - OSS", - "short_name": "Code Web - OSS", - "start_url": "/", - "lang": "en-US", - "display": "standalone" - })); - } - if (/^\/static\//.test(pathname)) { - // static requests - return handleStatic(req, res, parsedUrl); - } - if (/^\/static-extension\//.test(pathname)) { - // static extension requests - return handleStaticExtension(req, res, parsedUrl); - } - if (pathname === '/') { - // main web - return handleRoot(req, res); - } - - return serveError(req, res, 404, 'Not found.'); - } catch (error) { - console.error(error.toString()); - - return serveError(req, res, 500, 'Internal Server Error.'); - } -}); - -server.listen(LOCAL_PORT, () => { - if (LOCAL_PORT !== PORT) { - console.log(`Operating location at http://0.0.0.0:${LOCAL_PORT}`); - } - console.log(`Web UI available at ${SCHEME}://${AUTHORITY}`); -}); - -server.on('error', err => { - console.error(`Error occurred in server:`); - console.error(err); -}); - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl - */ -function handleStatic(req, res, parsedUrl) { - - // Strip `/static/` from the path - const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static/'.length))); - - return serveFile(req, res, path.join(APP_ROOT, relativeFilePath)); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl - */ -function handleStaticExtension(req, res, parsedUrl) { - - // Strip `/static-extension/` from the path - const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static-extension/'.length))); - - const filePath = path.join(EXTENSIONS_ROOT, relativeFilePath); - - return serveFile(req, res, filePath); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - */ -async function handleRoot(req, res) { - const extensionFolders = await util.promisify(fs.readdir)(EXTENSIONS_ROOT); - const mapExtensionFolderToExtensionPackageJSON = new Map(); - - await Promise.all(extensionFolders.map(async extensionFolder => { - try { - const packageJSON = JSON.parse((await util.promisify(fs.readFile)(path.join(EXTENSIONS_ROOT, extensionFolder, 'package.json'))).toString()); - if (packageJSON.main && packageJSON.name !== 'vscode-api-tests') { - return; // unsupported - } - - if (packageJSON.name === 'scss') { - return; // seems to fail to JSON.parse()?! - } - - packageJSON.extensionKind = ['web']; // enable for Web - - mapExtensionFolderToExtensionPackageJSON.set(extensionFolder, packageJSON); - } catch (error) { - return null; - } - })); - - const staticExtensions = []; - - // Built in extensions - mapExtensionFolderToExtensionPackageJSON.forEach((packageJSON, extensionFolder) => { - staticExtensions.push({ - packageJSON, - extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${extensionFolder}` } - }); - }); - - const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString() - .replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({ - staticExtensions, - folderUri: { scheme: 'memfs', path: `/sample-folder` } - }))) - .replace('{{WEBVIEW_ENDPOINT}}', '') - .replace('{{REMOTE_USER_DATA_URI}}', ''); - - res.writeHead(200, { 'Content-Type': 'text/html' }); - return res.end(data); -} - -/** - * @param {string} value - */ -function escapeAttribute(value) { - return value.replace(/"/g, '"'); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {string} errorMessage - */ -function serveError(req, res, errorCode, errorMessage) { - res.writeHead(errorCode, { 'Content-Type': 'text/plain' }); - res.end(errorMessage); -} - -const textMimeType = { - '.html': 'text/html', - '.js': 'text/javascript', - '.json': 'application/json', - '.css': 'text/css', - '.svg': 'image/svg+xml', -}; - -const mapExtToMediaMimes = { - '.bmp': 'image/bmp', - '.gif': 'image/gif', - '.ico': 'image/x-icon', - '.jpe': 'image/jpg', - '.jpeg': 'image/jpg', - '.jpg': 'image/jpg', - '.png': 'image/png', - '.tga': 'image/x-tga', - '.tif': 'image/tiff', - '.tiff': 'image/tiff', - '.woff': 'application/font-woff' -}; - -/** - * @param {string} forPath - */ -function getMediaMime(forPath) { - const ext = path.extname(forPath); - - return mapExtToMediaMimes[ext.toLowerCase()]; -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {string} filePath - */ -async function serveFile(req, res, filePath, responseHeaders = Object.create(null)) { - try { - - // Sanity checks - filePath = path.normalize(filePath); // ensure no "." and ".." - if (filePath.indexOf(`${APP_ROOT}${path.sep}`) !== 0) { - // invalid location outside of APP_ROOT - return serveError(req, res, 400, `Bad request.`); - } - - const stat = await util.promisify(fs.stat)(filePath); - - // Check if file modified since - const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; // weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) - if (req.headers['if-none-match'] === etag) { - res.writeHead(304); - return res.end(); - } - - // Headers - responseHeaders['Content-Type'] = textMimeType[path.extname(filePath)] || getMediaMime(filePath) || 'text/plain'; - responseHeaders['Etag'] = etag; - - res.writeHead(200, responseHeaders); - - // Data - fs.createReadStream(filePath).pipe(res); - } catch (error) { - console.error(error.toString()); - res.writeHead(404, { 'Content-Type': 'text/plain' }); - return res.end('Not found'); - } -} - -if (args.launch !== false) { - opn(`${SCHEME}://${HOST}:${PORT}`); -} diff --git a/scripts/code.bat b/scripts/code.bat index 770d37b7aec..a05bea92d57 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -5,34 +5,23 @@ title VSCode Dev pushd %~dp0\.. -:: Node modules -if not exist node_modules call yarn +:: Get electron, compile, built-in extensions +if "%VSCODE_SKIP_PRELAUNCH%"=="" node build/lib/preLaunch.js for /f "tokens=2 delims=:," %%a in ('findstr /R /C:"\"nameShort\":.*" product.json') do set NAMESHORT=%%~a set NAMESHORT=%NAMESHORT: "=% set NAMESHORT=%NAMESHORT:"=%.exe set CODE=".build\electron\%NAMESHORT%" -:: Get electron -call yarn electron - :: Manage built-in extensions if "%1"=="--builtin" goto builtin -:: Sync built-in extensions -node build\lib\builtInExtensions.js - -:: Build -if not exist out yarn compile - :: Configuration set NODE_ENV=development set VSCODE_DEV=1 set VSCODE_CLI=1 -set ELECTRON_DEFAULT_ERROR_MODE=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 -set VSCODE_LOGS= :: Launch Code diff --git a/scripts/code.sh b/scripts/code.sh index 4ba1a00b9f7..3095f3897bf 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -7,7 +7,8 @@ if [[ "$OSTYPE" == "darwin"* ]]; then ROOT=$(dirname "$(dirname "$(realpath "$0")")") else ROOT=$(dirname "$(dirname "$(readlink -f $0)")") - if grep -qi Microsoft /proc/version; then + # If the script is running in Docker using the WSL2 engine, powershell.exe won't exist + if grep -qi Microsoft /proc/version && type powershell.exe > /dev/null 2>&1; then IN_WSL=true fi fi @@ -23,11 +24,10 @@ function code() { CODE=".build/electron/$NAME" fi - # Node modules - test -d node_modules || yarn - - # Get electron - yarn electron + # Get electron, compile, built-in extensions + if [[ -z "${VSCODE_SKIP_PRELAUNCH}" ]]; then + node build/lib/preLaunch.js + fi # Manage built-in extensions if [[ "$1" == "--builtin" ]]; then @@ -35,34 +35,30 @@ function code() { return fi - # Sync built-in extensions - node build/lib/builtInExtensions.js - - # Build - test -d out || yarn compile - # Configuration export NODE_ENV=development export VSCODE_DEV=1 export VSCODE_CLI=1 export ELECTRON_ENABLE_STACK_DUMPING=1 export ELECTRON_ENABLE_LOGGING=1 - export VSCODE_LOGS= # Launch Code - exec "$CODE" . "$@" + exec "$CODE" . --no-sandbox "$@" } function code-wsl() { + HOST_IP=$(echo "" | powershell.exe –noprofile -Command "& {(Get-NetIPAddress | Where-Object {\$_.InterfaceAlias -like '*WSL*' -and \$_.AddressFamily -eq 'IPv4'}).IPAddress | Write-Host -NoNewline}") + export DISPLAY="$HOST_IP:0" + # in a wsl shell ELECTRON="$ROOT/.build/electron/Code - OSS.exe" if [ -f "$ELECTRON" ]; then local CWD=$(pwd) cd $ROOT - export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV + export WSLENV=ELECTRON_RUN_AS_NODE/w:VSCODE_DEV/w:$WSLENV local WSL_EXT_ID="ms-vscode-remote.remote-wsl" - local WSL_EXT_WLOC=$(ELECTRON_RUN_AS_NODE=1 "$ROOT/.build/electron/Code - OSS.exe" "out/cli.js" --locate-extension $WSL_EXT_ID) + local WSL_EXT_WLOC=$(echo "" | VSCODE_DEV=1 ELECTRON_RUN_AS_NODE=1 "$ROOT/.build/electron/Code - OSS.exe" "out/cli.js" --locate-extension $WSL_EXT_ID) cd $CWD if [ -n "$WSL_EXT_WLOC" ]; then # replace \r\n with \n in WSL_EXT_WLOC diff --git a/scripts/generate-definitelytyped.sh b/scripts/generate-definitelytyped.sh index 82de9124a07..118401b43cb 100755 --- a/scripts/generate-definitelytyped.sh +++ b/scripts/generate-definitelytyped.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [ $# -eq 0 ]; then echo "Pass in a version like ./scripts/generate-vscode-dts.sh 1.30." @@ -8,13 +8,13 @@ fi header="// Type definitions for Visual Studio Code ${1} // Project: https://github.com/microsoft/vscode -// Definitions by: Visual Studio Code Team, Microsoft +// Definitions by: Visual Studio Code Team, Microsoft // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. - * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information. + * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information. *--------------------------------------------------------------------------------------------*/ /** @@ -28,4 +28,4 @@ if [ -f ./src/vs/vscode.d.ts ]; then echo "Generated index.d.ts for version ${1}." else echo "Can't find ./src/vs/vscode.d.ts. Run this script at vscode root." -fi \ No newline at end of file +fi diff --git a/scripts/node-electron.sh b/scripts/node-electron.sh index 1c3d894a1d1..0a822b6c383 100755 --- a/scripts/node-electron.sh +++ b/scripts/node-electron.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } diff --git a/scripts/npm.sh b/scripts/npm.sh index 93562dd24d6..30f11cfdcdf 100755 --- a/scripts/npm.sh +++ b/scripts/npm.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash -yarn $* \ No newline at end of file +yarn $* diff --git a/scripts/test-documentation.sh b/scripts/test-documentation.sh index 54a0672945a..6595e6d2240 100755 --- a/scripts/test-documentation.sh +++ b/scripts/test-documentation.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e if [[ "$OSTYPE" == "darwin"* ]]; then diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 695f1c940fe..5a493b0b1c5 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -3,59 +3,81 @@ setlocal pushd %~dp0\.. -set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5% +set VSCODEUSERDATADIR=%TEMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,2% +set VSCODECRASHDIR=%~dp0\..\.build\crashes :: Figure out which Electron to use for running tests if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( - :: Run out of sources: no need to compile as code.sh takes care of it + :: Run out of sources: no need to compile as code.bat takes care of it chcp 65001 set INTEGRATION_TEST_ELECTRON_PATH=.\scripts\code.bat set VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE=1 - echo "Running integration tests out of sources." + echo Storing crash reports into '%VSCODECRASHDIR%'. + echo Running integration tests out of sources. ) else ( :: Run from a built: need to compile all test extensions - call yarn gulp compile-extension:vscode-api-tests - call yarn gulp compile-extension:vscode-colorize-tests - call yarn gulp compile-extension:markdown-language-features - call yarn gulp compile-extension:emmet - call yarn gulp compile-extension:css-language-features-server - call yarn gulp compile-extension:html-language-features-server - call yarn gulp compile-extension:json-language-features-server + :: because we run extension tests from their source folders + :: and the build bundles extensions into .build webpacked + call yarn gulp compile-extension:vscode-api-tests^ + compile-extension:vscode-colorize-tests^ + compile-extension:markdown-language-features^ + compile-extension:typescript-language-features^ + compile-extension:vscode-custom-editor-tests^ + compile-extension:vscode-notebook-tests^ + compile-extension:emmet^ + compile-extension:css-language-features-server^ + compile-extension:html-language-features-server^ + compile-extension:json-language-features-server^ + compile-extension:git :: Configuration for more verbose output set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 - set ELECTRON_ENABLE_STACK_DUMPING=1 - echo "Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build." + echo Storing crash reports into '%VSCODECRASHDIR%'. + echo Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build. ) :: Integration & performance tests in AMD -call .\scripts\test.bat --runGlob **\*.integrationTest.js %* -if %errorlevel% neq 0 exit /b %errorlevel% +::call .\scripts\test.bat --runGlob **\*.integrationTest.js %* +::if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in the extension host -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . +REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\typescript-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\typescript-language-features --extensionTestsPath=%~dp0\..\extensions\typescript-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +REM if %errorlevel% neq 0 exit /b %errorlevel% + +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\markdown-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -:: Tests in commonJS (HTML, CSS, JSON language server tests...) -call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js +call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . if %errorlevel% neq 0 exit /b %errorlevel% -if exist ".\resources\server\test\test-remote-integration.bat" ( - call .\resources\server\test\test-remote-integration.bat -) +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +if %errorlevel% neq 0 exit /b %errorlevel% + +for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i +set GITWORKSPACE=%TEMPDIR%\git-%RANDOM% +mkdir %GITWORKSPACE% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --enable-proposed-api=vscode.git --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +if %errorlevel% neq 0 exit /b %errorlevel% + +:: Tests in commonJS (CSS, HTML) +call %~dp0\node-electron.bat %~dp0\..\extensions\css-language-features/server/test/index.js +if %errorlevel% neq 0 exit /b %errorlevel% + +call %~dp0\node-electron.bat %~dp0\..\extensions\html-language-features/server/test/index.js +if %errorlevel% neq 0 exit /b %errorlevel% rmdir /s /q %VSCODEUSERDATADIR% diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 89851e48908..b302ce28db9 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -1,16 +1,16 @@ -#!/bin/bash +#!/usr/bin/env bash set -e if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } ROOT=$(dirname $(dirname $(realpath "$0"))) - VSCODEUSERDATADIR=`mktemp -d -t 'myuserdatadir'` else ROOT=$(dirname $(dirname $(readlink -f $0))) - VSCODEUSERDATADIR=`mktemp -d 2>/dev/null` LINUX_NO_SANDBOX="--no-sandbox" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox. fi +VSCODEUSERDATADIR=`mktemp -d 2>/dev/null` +VSCODECRASHDIR=$ROOT/.build/crashes cd $ROOT # Figure out which Electron to use for running tests @@ -19,22 +19,30 @@ then # Run out of sources: no need to compile as code.sh takes care of it INTEGRATION_TEST_ELECTRON_PATH="./scripts/code.sh" + echo "Storing crash reports into '$VSCODECRASHDIR'." echo "Running integration tests out of sources." else # Run from a built: need to compile all test extensions - yarn gulp compile-extension:vscode-api-tests - yarn gulp compile-extension:vscode-colorize-tests - yarn gulp compile-extension:markdown-language-features - yarn gulp compile-extension:emmet - yarn gulp compile-extension:css-language-features-server - yarn gulp compile-extension:html-language-features-server - yarn gulp compile-extension:json-language-features-server + # because we run extension tests from their source folders + # and the build bundles extensions into .build webpacked + yarn gulp compile-extension:vscode-api-tests \ + compile-extension:vscode-colorize-tests \ + compile-extension:vscode-custom-editor-tests \ + compile-extension:vscode-notebook-tests \ + compile-extension:markdown-language-features \ + compile-extension:typescript-language-features \ + compile-extension:emmet \ + compile-extension:css-language-features-server \ + compile-extension:html-language-features-server \ + compile-extension:json-language-features-server \ + compile-extension:git # Configuration for more verbose output export VSCODE_CLI=1 export ELECTRON_ENABLE_STACK_DUMPING=1 export ELECTRON_ENABLE_LOGGING=1 + echo "Storing crash reports into '$VSCODECRASHDIR'." echo "Running integration tests with '$INTEGRATION_TEST_ELECTRON_PATH' as build." fi @@ -42,22 +50,17 @@ fi ./scripts/test.sh --runGlob **/*.integrationTest.js "$@" # Tests in the extension host -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +#"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR -mkdir -p $ROOT/extensions/emmet/test-fixtures -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR -rm -rf $ROOT/extensions/emmet/test-fixtures - -# Remote Integration Tests -if [ -f ./resources/server/test/test-remote-integration.sh ]; then - ./resources/server/test/test-remote-integration.sh -fi - -# Tests in commonJS +# Tests in commonJS (CSS, HTML) cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js -rm -r $VSCODEUSERDATADIR +rm -rf $VSCODEUSERDATADIR diff --git a/scripts/test.bat b/scripts/test.bat index 0d1350a79ec..b37aa32e0ad 100644 --- a/scripts/test.bat +++ b/scripts/test.bat @@ -17,14 +17,14 @@ if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron :: Run tests set ELECTRON_ENABLE_LOGGING=1 -%CODE% .\test\electron\index.js %* +%CODE% .\test\unit\electron\index.js %* popd endlocal :: app.exit(0) is exiting with code 255 in Electron 1.7.4. -:: See https://github.com/Microsoft/vscode/issues/28582 +:: See https://github.com/microsoft/vscode/issues/28582 echo errorlevel: %errorlevel% if %errorlevel% == 255 set errorlevel=0 diff --git a/scripts/test.sh b/scripts/test.sh index 28298993295..0744c096e54 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,5 +1,5 @@ -#!/bin/bash - +#!/usr/bin/env bash +set -e if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } @@ -29,10 +29,10 @@ if [[ "$OSTYPE" == "darwin"* ]]; then cd $ROOT ; ulimit -n 4096 ; \ ELECTRON_ENABLE_LOGGING=1 \ "$CODE" \ - test/electron/index.js "$@" + test/unit/electron/index.js "$@" else cd $ROOT ; \ ELECTRON_ENABLE_LOGGING=1 \ "$CODE" \ - test/electron/index.js --no-sandbox "$@" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox. + test/unit/electron/index.js --no-sandbox "$@" # Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox. fi diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index a8144075dd5..752e4ab63ed 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -14,7 +14,7 @@ const nlsConfig = bootstrap.setupNLS(); // Bootstrap: Loader loader.config({ - baseUrl: bootstrap.uriFromPath(__dirname), + baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), catchError: true, nodeRequire: require, nodeMain: __filename, diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index e0c6cf34c56..97a4d9934cd 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -7,12 +7,16 @@ 'use strict'; const bootstrap = require('./bootstrap'); +const bootstrapNode = require('./bootstrap-node'); + +// Remove global paths from the node module lookup +bootstrapNode.removeGlobalNodeModuleLookupPaths(); // Enable ASAR in our forked processes bootstrap.enableASARSupport(); if (process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']) { - bootstrap.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); + bootstrapNode.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); } // Configure: pipe logging to parent process @@ -36,6 +40,7 @@ configureCrashReporter(); // Load AMD entry point require('./bootstrap-amd').load(process.env['AMD_ENTRYPOINT']); + //#region Helpers function pipeLoggingToParent() { @@ -46,8 +51,6 @@ function pipeLoggingToParent() { const seen = []; const argsArray = []; - let res; - // Massage some arguments with special treatment if (args.length) { for (let i = 0; i < args.length; i++) { @@ -82,7 +85,7 @@ function pipeLoggingToParent() { } try { - res = JSON.stringify(argsArray, function (key, value) { + const res = JSON.stringify(argsArray, function (key, value) { // Objects get special treatment to prevent circles if (isObject(value) || Array.isArray(value)) { @@ -95,17 +98,20 @@ function pipeLoggingToParent() { return value; }); + + if (res.length > MAX_LENGTH) { + return 'Output omitted for a large object that exceeds the limits'; + } + + return res; } catch (error) { - return 'Output omitted for an object that cannot be inspected (' + error.toString() + ')'; + return `Output omitted for an object that cannot be inspected ('${error.toString()}')`; } - - if (res && res.length > MAX_LENGTH) { - return 'Output omitted for a large object that exceeds the limits'; - } - - return res; } + /** + * @param {{ type: string; severity: string; arguments: string; }} arg + */ function safeSend(arg) { try { process.send(arg); @@ -114,6 +120,9 @@ function pipeLoggingToParent() { } } + /** + * @param {unknown} obj + */ function isObject(obj) { return typeof obj === 'object' && obj !== null @@ -139,13 +148,11 @@ function pipeLoggingToParent() { function handleExceptions() { // Handle uncaught exceptions - // @ts-ignore process.on('uncaughtException', function (err) { console.error('Uncaught Exception: ', err); }); // Handle unhandled promise rejections - // @ts-ignore process.on('unhandledRejection', function (reason) { console.error('Unhandled Promise Rejection: ', reason); }); diff --git a/src/bootstrap-node.js b/src/bootstrap-node.js new file mode 100644 index 00000000000..2686249978d --- /dev/null +++ b/src/bootstrap-node.js @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +/** + * Add support for redirecting the loading of node modules + * + * @param {string} injectPath + */ +exports.injectNodeModuleLookupPath = function (injectPath) { + if (!injectPath) { + throw new Error('Missing injectPath'); + } + + const Module = require('module'); + const path = require('path'); + + const nodeModulesPath = path.join(__dirname, '../node_modules'); + + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; + + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); + if (Array.isArray(paths)) { + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === nodeModulesPath) { + paths.splice(i, 0, injectPath); + break; + } + } + } + + return paths; + }; +}; + +exports.removeGlobalNodeModuleLookupPaths = function () { + const Module = require('module'); + // @ts-ignore + const globalPaths = Module.globalPaths; + + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; + + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); + let commonSuffixLength = 0; + while (commonSuffixLength < paths.length && paths[paths.length - 1 - commonSuffixLength] === globalPaths[globalPaths.length - 1 - commonSuffixLength]) { + commonSuffixLength++; + } + return paths.slice(0, paths.length - commonSuffixLength); + }; +}; diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 0336f9eb700..3fc0f419b97 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -3,216 +3,260 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + //@ts-check 'use strict'; -const bootstrap = require('./bootstrap'); +// Simple module style to support node.js and browser environments +(function (globalThis, factory) { -/** - * @param {object} destination - * @param {object} source - * @returns {object} - */ -exports.assign = function assign(destination, source) { - return Object.keys(source).reduce(function (r, key) { r[key] = source[key]; return r; }, destination); -}; + // Node.js + if (typeof exports === 'object') { + module.exports = factory(); + } -/** - * - * @param {string[]} modulePaths - * @param {(result, configuration: object) => any} resultCallback - * @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options - */ -exports.load = function (modulePaths, resultCallback, options) { + // Browser + else { + globalThis.MonacoBootstrapWindow = factory(); + } +}(this, function () { + const bootstrapLib = bootstrap(); + const preloadGlobals = globals(); + const sandbox = preloadGlobals.context.sandbox; + const webFrame = preloadGlobals.webFrame; + const safeProcess = sandbox ? preloadGlobals.process : process; - // @ts-ignore - const webFrame = require('electron').webFrame; - const path = require('path'); - - const args = parseURLQueryArgs(); /** - * // configuration: IWindowConfiguration - * @type {{ - * zoomLevel?: number, - * extensionDevelopmentPath?: string[], - * extensionTestsPath?: string, - * userEnv?: { [key: string]: string | undefined }, - * appRoot?: string, - * nodeCachedDataDir?: string - * }} */ - const configuration = JSON.parse(args['config'] || '{}') || {}; + * @param {string[]} modulePaths + * @param {(result, configuration: object) => any} resultCallback + * @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options + */ + function load(modulePaths, resultCallback, options) { + const args = parseURLQueryArgs(); + /** + * // configuration: INativeWindowConfiguration + * @type {{ + * zoomLevel?: number, + * extensionDevelopmentPath?: string[], + * extensionTestsPath?: string, + * userEnv?: { [key: string]: string | undefined }, + * appRoot?: string, + * nodeCachedDataDir?: string + * }} */ + const configuration = JSON.parse(args['config'] || '{}') || {}; - // Apply zoom level early to avoid glitches - const zoomLevel = configuration.zoomLevel; - if (typeof zoomLevel === 'number' && zoomLevel !== 0) { - webFrame.setZoomLevel(zoomLevel); + // Apply zoom level early to avoid glitches + const zoomLevel = configuration.zoomLevel; + if (typeof zoomLevel === 'number' && zoomLevel !== 0) { + webFrame.setZoomLevel(zoomLevel); + } + + // Error handler + safeProcess.on('uncaughtException', function (error) { + onUnexpectedError(error, enableDeveloperTools); + }); + + // Developer tools + const enableDeveloperTools = (safeProcess.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; + let developerToolsUnbind; + if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { + developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + } + + // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) + if (!sandbox) { + Object.assign(safeProcess.env, configuration.userEnv); + } + + // Enable ASAR support (TODO@sandbox non-sandboxed only) + if (!sandbox) { + globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); + } + + if (options && typeof options.canModifyDOM === 'function') { + options.canModifyDOM(configuration); + } + + // Get the nls configuration into the process.env as early as possible (TODO@sandbox non-sandboxed only) + const nlsConfig = sandbox ? { availableLanguages: {} } : globalThis.MonacoBootstrap.setupNLS(); + + let locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } + + window.document.documentElement.setAttribute('lang', locale); + + // do not advertise AMD to avoid confusing UMD modules loaded with nodejs + if (!sandbox) { + window['define'] = undefined; + } + + // replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only) + if (!sandbox) { + require.define('fs', ['original-fs'], function (originalFS) { return originalFS; }); + } + + window['MonacoEnvironment'] = {}; + + const loaderConfig = { + baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`, + 'vs/nls': nlsConfig + }; + + // Enable loading of node modules: + // - sandbox: we list paths of webpacked modules to help the loader + // - non-sandbox: we signal that any module that does not begin with + // `vs/` should be loaded using node.js require() + if (sandbox) { + loaderConfig.paths = { + 'vscode-textmate': `../node_modules/vscode-textmate/release/main`, + 'vscode-oniguruma': `../node_modules/vscode-oniguruma/release/main`, + 'xterm': `../node_modules/xterm/lib/xterm.js`, + 'xterm-addon-search': `../node_modules/xterm-addon-search/lib/xterm-addon-search.js`, + 'xterm-addon-unicode11': `../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`, + 'xterm-addon-webgl': `../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`, + 'iconv-lite-umd': `../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`, + 'jschardet': `../node_modules/jschardet/dist/jschardet.min.js`, + }; + } else { + loaderConfig.amdModulesPattern = /^vs\//; + } + + // cached data config + if (configuration.nodeCachedDataDir) { + loaderConfig.nodeCachedData = { + path: configuration.nodeCachedDataDir, + seed: modulePaths.join('') + }; + } + + if (options && typeof options.beforeLoaderConfig === 'function') { + options.beforeLoaderConfig(configuration, loaderConfig); + } + + require.config(loaderConfig); + + if (nlsConfig.pseudo) { + require(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); + } + + if (options && typeof options.beforeRequire === 'function') { + options.beforeRequire(); + } + + require(modulePaths, result => { + try { + const callbackResult = resultCallback(result, configuration); + if (callbackResult && typeof callbackResult.then === 'function') { + callbackResult.then(() => { + if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { + developerToolsUnbind(); + } + }, error => { + onUnexpectedError(error, enableDeveloperTools); + }); + } + } catch (error) { + onUnexpectedError(error, enableDeveloperTools); + } + }, onUnexpectedError); } - // Error handler - // @ts-ignore - process.on('uncaughtException', function (error) { - onUnexpectedError(error, enableDeveloperTools); - }); + /** + * @returns {{[param: string]: string }} + */ + function parseURLQueryArgs() { + const search = window.location.search || ''; - // Developer tools - const enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; - let developerToolsUnbind; - if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { - developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); } - // Correctly inherit the parent's environment - exports.assign(process.env, configuration.userEnv); + /** + * @param {boolean} disallowReloadKeybinding + * @returns {() => void} + */ + function registerDeveloperKeybindings(disallowReloadKeybinding) { + const ipcRenderer = preloadGlobals.ipcRenderer; - // Enable ASAR support - bootstrap.enableASARSupport(path.join(configuration.appRoot, 'node_modules')); + const extractKey = function (e) { + return [ + e.ctrlKey ? 'ctrl-' : '', + e.metaKey ? 'meta-' : '', + e.altKey ? 'alt-' : '', + e.shiftKey ? 'shift-' : '', + e.keyCode + ].join(''); + }; - if (options && typeof options.canModifyDOM === 'function') { - options.canModifyDOM(configuration); - } + // Devtools & reload support + const TOGGLE_DEV_TOOLS_KB = (safeProcess.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I + const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 + const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R - // Get the nls configuration into the process.env as early as possible. - const nlsConfig = bootstrap.setupNLS(); + let listener = function (e) { + const key = extractKey(e); + if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { + ipcRenderer.send('vscode:toggleDevTools'); + } else if (key === RELOAD_KB && !disallowReloadKeybinding) { + ipcRenderer.send('vscode:reloadWindow'); + } + }; - let locale = nlsConfig.availableLanguages['*'] || 'en'; - if (locale === 'zh-tw') { - locale = 'zh-Hant'; - } else if (locale === 'zh-cn') { - locale = 'zh-Hans'; - } + window.addEventListener('keydown', listener); - window.document.documentElement.setAttribute('lang', locale); - - // Load the loader - const amdLoader = require(configuration.appRoot + '/out/vs/loader.js'); - const amdRequire = amdLoader.require; - const amdDefine = amdLoader.require.define; - const nodeRequire = amdLoader.require.nodeRequire; - - window['nodeRequire'] = nodeRequire; - window['require'] = amdRequire; - - // replace the patched electron fs with the original node fs for all AMD code - amdDefine('fs', ['original-fs'], function (originalFS) { return originalFS; }); - - window['MonacoEnvironment'] = {}; - - const loaderConfig = { - baseUrl: bootstrap.uriFromPath(configuration.appRoot) + '/out', - 'vs/nls': nlsConfig, - nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] - }; - - // cached data config - if (configuration.nodeCachedDataDir) { - loaderConfig.nodeCachedData = { - path: configuration.nodeCachedDataDir, - seed: modulePaths.join('') + return function () { + if (listener) { + window.removeEventListener('keydown', listener); + listener = undefined; + } }; } - if (options && typeof options.beforeLoaderConfig === 'function') { - options.beforeLoaderConfig(configuration, loaderConfig); - } - - amdRequire.config(loaderConfig); - - if (nlsConfig.pseudo) { - amdRequire(['vs/nls'], function (nlsPlugin) { - nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); - }); - } - - if (options && typeof options.beforeRequire === 'function') { - options.beforeRequire(); - } - - amdRequire(modulePaths, result => { - try { - const callbackResult = resultCallback(result, configuration); - if (callbackResult && typeof callbackResult.then === 'function') { - callbackResult.then(() => { - if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { - developerToolsUnbind(); - } - }, error => { - onUnexpectedError(error, enableDeveloperTools); - }); - } - } catch (error) { - onUnexpectedError(error, enableDeveloperTools); + /** + * @param {string | Error} error + * @param {boolean} [enableDeveloperTools] + */ + function onUnexpectedError(error, enableDeveloperTools) { + if (enableDeveloperTools) { + const ipcRenderer = preloadGlobals.ipcRenderer; + ipcRenderer.send('vscode:openDevTools'); } - }, onUnexpectedError); -}; -/** - * @returns {{[param: string]: string }} - */ -function parseURLQueryArgs() { - const search = window.location.search || ''; + console.error(`[uncaught exception]: ${error}`); - return search.split(/[?&]/) - .filter(function (param) { return !!param; }) - .map(function (param) { return param.split('='); }) - .filter(function (param) { return param.length === 2; }) - .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); -} - -/** - * @param {boolean} disallowReloadKeybinding - * @returns {() => void} - */ -function registerDeveloperKeybindings(disallowReloadKeybinding) { - - // @ts-ignore - const ipc = require('electron').ipcRenderer; - - const extractKey = function (e) { - return [ - e.ctrlKey ? 'ctrl-' : '', - e.metaKey ? 'meta-' : '', - e.altKey ? 'alt-' : '', - e.shiftKey ? 'shift-' : '', - e.keyCode - ].join(''); - }; - - // Devtools & reload support - const TOGGLE_DEV_TOOLS_KB = (process.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I - const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 - const RELOAD_KB = (process.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R - - let listener = function (e) { - const key = extractKey(e); - if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { - ipc.send('vscode:toggleDevTools'); - } else if (key === RELOAD_KB && !disallowReloadKeybinding) { - ipc.send('vscode:reloadWindow'); + if (error && typeof error !== 'string' && error.stack) { + console.error(error.stack); } - }; - - window.addEventListener('keydown', listener); - - return function () { - if (listener) { - window.removeEventListener('keydown', listener); - listener = undefined; - } - }; -} - -function onUnexpectedError(error, enableDeveloperTools) { - - // @ts-ignore - const ipc = require('electron').ipcRenderer; - - if (enableDeveloperTools) { - ipc.send('vscode:openDevTools'); } - console.error('[uncaught exception]: ' + error); - - if (error.stack) { - console.error(error.stack); + /** + * @return {{ fileUriFromPath: (path: string, config: { isWindows?: boolean, scheme?: string, fallbackAuthority?: string }) => string; }} + */ + function bootstrap() { + // @ts-ignore (defined in bootstrap.js) + return window.MonacoBootstrap; } -} + + /** + * @return {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals')} + */ + function globals() { + // @ts-ignore (defined in globals.js) + return window.vscode; + } + + return { + load, + globals + }; +})); diff --git a/src/bootstrap.js b/src/bootstrap.js index b035adc9f41..0cb6466aeaf 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -6,286 +6,269 @@ //@ts-check 'use strict'; -//#region global bootstrapping +// Simple module style to support node.js and browser environments +(function (globalThis, factory) { -// increase number of stack frames(from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) -Error.stackTraceLimit = 100; - -// Workaround for Electron not installing a handler to ignore SIGPIPE -// (https://github.com/electron/electron/issues/13254) -// @ts-ignore -process.on('SIGPIPE', () => { - console.error(new Error('Unexpected SIGPIPE')); -}); - -//#endregion - -//#region Add support for redirecting the loading of node modules - -exports.injectNodeModuleLookupPath = function (injectPath) { - if (!injectPath) { - throw new Error('Missing injectPath'); + // Node.js + if (typeof exports === 'object') { + module.exports = factory(); } - // @ts-ignore - const Module = require('module'); - const path = require('path'); - - const nodeModulesPath = path.join(__dirname, '../node_modules'); - - // @ts-ignore - const originalResolveLookupPaths = Module._resolveLookupPaths; - - // @ts-ignore - Module._resolveLookupPaths = function (moduleName, parent) { - const paths = originalResolveLookupPaths(moduleName, parent); - if (Array.isArray(paths)) { - for (let i = 0, len = paths.length; i < len; i++) { - if (paths[i] === nodeModulesPath) { - paths.splice(i, 0, injectPath); - break; - } - } - } - - return paths; - }; -}; -//#endregion - -//#region Add support for using node_modules.asar -/** - * @param {string=} nodeModulesPath - */ -exports.enableASARSupport = function (nodeModulesPath) { - - // @ts-ignore - const Module = require('module'); - const path = require('path'); - - let NODE_MODULES_PATH = nodeModulesPath; - if (!NODE_MODULES_PATH) { - NODE_MODULES_PATH = path.join(__dirname, '../node_modules'); + // Browser + else { + globalThis.MonacoBootstrap = factory(); } +}(this, function () { + const Module = typeof require === 'function' ? require('module') : undefined; + const path = typeof require === 'function' ? require('path') : undefined; + const fs = typeof require === 'function' ? require('fs') : undefined; - const NODE_MODULES_ASAR_PATH = NODE_MODULES_PATH + '.asar'; + //#region global bootstrapping - // @ts-ignore - const originalResolveLookupPaths = Module._resolveLookupPaths; + // increase number of stack frames(from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + Error.stackTraceLimit = 100; - // @ts-ignore - Module._resolveLookupPaths = function (request, parent) { - const paths = originalResolveLookupPaths(request, parent); - if (Array.isArray(paths)) { - for (let i = 0, len = paths.length; i < len; i++) { - if (paths[i] === NODE_MODULES_PATH) { - paths.splice(i, 0, NODE_MODULES_ASAR_PATH); - break; - } - } - } - - return paths; - }; -}; -//#endregion - -//#region URI helpers -/** - * @param {string} _path - * @returns {string} - */ -exports.uriFromPath = function (_path) { - const path = require('path'); - - let pathName = path.resolve(_path).replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { - pathName = '/' + pathName; - } - - /** @type {string} */ - let uri; - if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths - uri = encodeURI('file:' + pathName); - } else { - uri = encodeURI('file://' + pathName); - } - - return uri.replace(/#/g, '%23'); -}; -//#endregion - -//#region FS helpers -/** - * @param {string} file - * @returns {Promise} - */ -exports.readFile = function (file) { - const fs = require('fs'); - - return new Promise(function (resolve, reject) { - fs.readFile(file, 'utf8', function (err, data) { - if (err) { - reject(err); - return; - } - resolve(data); + // Workaround for Electron not installing a handler to ignore SIGPIPE + // (https://github.com/electron/electron/issues/13254) + if (typeof process !== 'undefined') { + process.on('SIGPIPE', () => { + console.error(new Error('Unexpected SIGPIPE')); }); - }); -}; - -/** - * @param {string} file - * @param {string} content - * @returns {Promise} - */ -exports.writeFile = function (file, content) { - const fs = require('fs'); - - return new Promise(function (resolve, reject) { - fs.writeFile(file, content, 'utf8', function (err) { - if (err) { - reject(err); - return; - } - resolve(); - }); - }); -}; - -/** - * @param {string} dir - * @returns {Promise} - */ -exports.mkdirp = function mkdirp(dir) { - const fs = require('fs'); - - return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); -}; -//#endregion - -//#region NLS helpers -/** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} - */ -exports.setupNLS = function () { - const path = require('path'); - - // Get the nls configuration into the process.env as early as possible. - let nlsConfig = { availableLanguages: {} }; - if (process.env['VSCODE_NLS_CONFIG']) { - try { - nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); - } catch (e) { - // Ignore - } } - if (nlsConfig._resolvedLanguagePackCoreLocation) { - const bundles = Object.create(null); + //#endregion - nlsConfig.loadBundle = function (bundle, language, cb) { - const result = bundles[bundle]; - if (result) { - cb(undefined, result); - return; + //#region Add support for using node_modules.asar + + /** + * @param {string} appRoot + */ + function enableASARSupport(appRoot) { + if (!path || !Module) { + console.warn('enableASARSupport() is only available in node.js environments'); + return; + } + + let NODE_MODULES_PATH = appRoot ? path.join(appRoot, 'node_modules') : undefined; + if (!NODE_MODULES_PATH) { + NODE_MODULES_PATH = path.join(__dirname, '../node_modules'); + } else { + // use the drive letter casing of __dirname + if (process.platform === 'win32') { + NODE_MODULES_PATH = __dirname.substr(0, 1) + NODE_MODULES_PATH.substr(1); } + } - const bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, bundle.replace(/\//g, '!') + '.nls.json'); - exports.readFile(bundleFile).then(function (content) { - const json = JSON.parse(content); - bundles[bundle] = json; + const NODE_MODULES_ASAR_PATH = `${NODE_MODULES_PATH}.asar`; - cb(undefined, json); - }).catch((error) => { - try { - if (nlsConfig._corruptedFile) { - exports.writeFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); }); + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; + + // @ts-ignore + Module._resolveLookupPaths = function (request, parent) { + const paths = originalResolveLookupPaths(request, parent); + if (Array.isArray(paths)) { + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === NODE_MODULES_PATH) { + paths.splice(i, 0, NODE_MODULES_ASAR_PATH); + break; } - } finally { - cb(error, undefined); } - }); + } + + return paths; }; } - return nlsConfig; -}; -//#endregion + //#endregion -//#region Portable helpers -/** - * @returns {{ portableDataPath: string, isPortable: boolean }} - */ -exports.configurePortable = function () { - // @ts-ignore - const product = require('../product.json'); - const path = require('path'); - const fs = require('fs'); - const appRoot = path.dirname(__dirname); + //#region URI helpers - function getApplicationPath() { - if (process.env['VSCODE_DEV']) { - return appRoot; + /** + * @param {string} path + * @param {{ isWindows?: boolean, scheme?: string, fallbackAuthority?: string }} config + * @returns {string} + */ + function fileUriFromPath(path, config) { + + // Since we are building a URI, we normalize any backlsash + // to slashes and we ensure that the path begins with a '/'. + let pathName = path.replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = `/${pathName}`; } - if (process.platform === 'darwin') { - return path.dirname(path.dirname(path.dirname(appRoot))); + /** @type {string} */ + let uri; + + // Windows: in order to support UNC paths (which start with '//') + // that have their own authority, we do not use the provided authority + // but rather preserve it. + if (config.isWindows && pathName.startsWith('//')) { + uri = encodeURI(`${config.scheme || 'file'}:${pathName}`); } - return path.dirname(path.dirname(appRoot)); + // Otherwise we optionally add the provided authority if specified + else { + uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`); + } + + return uri.replace(/#/g, '%23'); } - function getPortableDataPath() { - if (process.env['VSCODE_PORTABLE']) { - return process.env['VSCODE_PORTABLE']; + //#endregion + + + //#region NLS helpers + + /** + * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} + */ + function setupNLS() { + if (!path || !fs) { + console.warn('setupNLS() is only available in node.js environments'); + return; } - if (process.platform === 'win32' || process.platform === 'linux') { - return path.join(getApplicationPath(), 'data'); + // Get the nls configuration into the process.env as early as possible. + let nlsConfig = { availableLanguages: {} }; + if (process.env['VSCODE_NLS_CONFIG']) { + try { + nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); + } catch (e) { + // Ignore + } } - const portableDataName = product.portable || `${product.applicationName}-portable-data`; - return path.join(path.dirname(getApplicationPath()), portableDataName); + if (nlsConfig._resolvedLanguagePackCoreLocation) { + const bundles = Object.create(null); + + nlsConfig.loadBundle = function (bundle, language, cb) { + const result = bundles[bundle]; + if (result) { + cb(undefined, result); + + return; + } + + const bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`); + fs.promises.readFile(bundleFile, 'utf8').then(function (content) { + const json = JSON.parse(content); + bundles[bundle] = json; + + cb(undefined, json); + }).catch((error) => { + try { + if (nlsConfig._corruptedFile) { + fs.promises.writeFile(nlsConfig._corruptedFile, 'corrupted', 'utf8').catch(function (error) { console.error(error); }); + } + } finally { + cb(error, undefined); + } + }); + }; + } + + return nlsConfig; } - const portableDataPath = getPortableDataPath(); - const isPortable = !('target' in product) && fs.existsSync(portableDataPath); - const portableTempPath = path.join(portableDataPath, 'tmp'); - const isTempPortable = isPortable && fs.existsSync(portableTempPath); + //#endregion - if (isPortable) { - process.env['VSCODE_PORTABLE'] = portableDataPath; - } else { - delete process.env['VSCODE_PORTABLE']; - } - if (isTempPortable) { - if (process.platform === 'win32') { - process.env['TMP'] = portableTempPath; - process.env['TEMP'] = portableTempPath; + //#region Portable helpers + + /** + * @param {{ portable: string; applicationName: string; }} product + * @returns {{ portableDataPath: string; isPortable: boolean; }} + */ + function configurePortable(product) { + if (!path || !fs) { + console.warn('configurePortable() is only available in node.js environments'); + return; + } + + const appRoot = path.dirname(__dirname); + + function getApplicationPath() { + if (process.env['VSCODE_DEV']) { + return appRoot; + } + + if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } + + return path.dirname(path.dirname(appRoot)); + } + + function getPortableDataPath() { + if (process.env['VSCODE_PORTABLE']) { + return process.env['VSCODE_PORTABLE']; + } + + if (process.platform === 'win32' || process.platform === 'linux') { + return path.join(getApplicationPath(), 'data'); + } + + // @ts-ignore + const portableDataName = product.portable || `${product.applicationName}-portable-data`; + return path.join(path.dirname(getApplicationPath()), portableDataName); + } + + const portableDataPath = getPortableDataPath(); + const isPortable = !('target' in product) && fs.existsSync(portableDataPath); + const portableTempPath = path.join(portableDataPath, 'tmp'); + const isTempPortable = isPortable && fs.existsSync(portableTempPath); + + if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; } else { - process.env['TMPDIR'] = portableTempPath; + delete process.env['VSCODE_PORTABLE']; } + + if (isTempPortable) { + if (process.platform === 'win32') { + process.env['TMP'] = portableTempPath; + process.env['TEMP'] = portableTempPath; + } else { + process.env['TMPDIR'] = portableTempPath; + } + } + + return { + portableDataPath, + isPortable + }; } + //#endregion + + + //#region ApplicationInsights + + // Prevents appinsights from monkey patching modules. + // This should be called before importing the applicationinsights module + function avoidMonkeyPatchFromAppInsights() { + if (typeof process === 'undefined') { + console.warn('avoidMonkeyPatchFromAppInsights() is only available in node.js environments'); + return; + } + + // @ts-ignore + process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] = true; // Skip monkey patching of 3rd party modules by appinsights + global['diagnosticsSource'] = {}; // Prevents diagnostic channel (which patches "require") from initializing entirely + } + + //#endregion + + return { - portableDataPath, - isPortable + enableASARSupport, + avoidMonkeyPatchFromAppInsights, + configurePortable, + setupNLS, + fileUriFromPath }; -}; -//#endregion - -//#region ApplicationInsights -/** - * Prevents appinsights from monkey patching modules. - * This should be called before importing the applicationinsights module - */ -exports.avoidMonkeyPatchFromAppInsights = function () { - // @ts-ignore - process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] = true; // Skip monkey patching of 3rd party modules by appinsights - global['diagnosticsSource'] = {}; // Prevents diagnostic channel (which patches "require") from initializing entirely -}; -//#endregion +})); diff --git a/src/buildfile.js b/src/buildfile.js index 2df9bf24e73..f6d1c647d9d 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -16,6 +16,7 @@ exports.base = [{ }]; exports.workerExtensionHost = [entrypoint('vs/workbench/services/extensions/worker/extensionHostWorker')]; +exports.workerNotebook = [entrypoint('vs/workbench/contrib/notebook/common/services/notebookSimpleWorker')]; exports.workbenchDesktop = require('./vs/workbench/buildfile.desktop').collectModules(); exports.workbenchWeb = require('./vs/workbench/buildfile.web').collectModules(); diff --git a/src/cli.js b/src/cli.js index 4be0bad61d8..872cd66733d 100644 --- a/src/cli.js +++ b/src/cli.js @@ -7,15 +7,16 @@ 'use strict'; const bootstrap = require('./bootstrap'); +const product = require('../product.json'); // Avoid Monkey Patches from Application Insights bootstrap.avoidMonkeyPatchFromAppInsights(); // Enable portable support -bootstrap.configurePortable(); +bootstrap.configurePortable(product); // Enable ASAR support bootstrap.enableASARSupport(); // Load CLI through AMD loader -require('./bootstrap-amd').load('vs/code/node/cli'); \ No newline at end of file +require('./bootstrap-amd').load('vs/code/node/cli'); diff --git a/src/main.js b/src/main.js index 7242b0cb156..2a9756ab2fa 100644 --- a/src/main.js +++ b/src/main.js @@ -16,13 +16,16 @@ const fs = require('fs'); const os = require('os'); const bootstrap = require('./bootstrap'); const paths = require('./paths'); -// @ts-ignore +/** @type {any} */ const product = require('../product.json'); -// @ts-ignore -const { app, protocol } = require('electron'); +const { app, protocol, crashReporter } = require('electron'); + +// Disable render process reuse, we still have +// non-context aware native modules in the renderer. +app.allowRendererProcessReuse = false; // Enable portable support -const portable = bootstrap.configurePortable(); +const portable = bootstrap.configurePortable(product); // Enable ASAR support bootstrap.enableASARSupport(); @@ -32,6 +35,74 @@ const args = parseCLIArgs(); const userDataPath = getUserDataPath(args); app.setPath('userData', userDataPath); +// Configure static command line arguments +const argvConfig = configureCommandlineSwitchesSync(args); + +// If a crash-reporter-directory is specified we store the crash reports +// in the specified directory and don't upload them to the crash server. +let crashReporterDirectory = args['crash-reporter-directory']; +let submitURL = ''; +if (crashReporterDirectory) { + crashReporterDirectory = path.normalize(crashReporterDirectory); + + if (!path.isAbsolute(crashReporterDirectory)) { + console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory must be absolute.`); + app.exit(1); + } + + if (!fs.existsSync(crashReporterDirectory)) { + try { + fs.mkdirSync(crashReporterDirectory); + } catch (error) { + console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory does not seem to exist or cannot be created.`); + app.exit(1); + } + } + + // Crashes are stored in the crashDumps directory by default, so we + // need to change that directory to the provided one + console.log(`Found --crash-reporter-directory argument. Setting crashDumps directory to be '${crashReporterDirectory}'`); + app.setPath('crashDumps', crashReporterDirectory); +} else { + const appCenter = product.appCenter; + // Disable Appcenter crash reporting if + // * --crash-reporter-directory is specified + // * enable-crash-reporter runtime argument is set to 'false' + // * --disable-crash-reporter command line parameter is set + if (appCenter && argvConfig['enable-crash-reporter'] && !args['disable-crash-reporter']) { + const isWindows = (process.platform === 'win32'); + const isLinux = (process.platform === 'linux'); + const crashReporterId = argvConfig['crash-reporter-id']; + const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + if (uuidPattern.test(crashReporterId)) { + submitURL = isWindows ? appCenter[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? appCenter[`linux-x64`] : appCenter.darwin; + submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId); + // Send the id for child node process that are explicitly starting crash reporter. + // For vscode this is ExtensionHost process currently. + const argv = process.argv; + const endOfArgsMarkerIndex = argv.indexOf('--'); + if (endOfArgsMarkerIndex === -1) { + argv.push('--crash-reporter-id', crashReporterId); + } else { + // if the we have an argument "--" (end of argument marker) + // we cannot add arguments at the end. rather, we add + // arguments before the "--" marker. + argv.splice(endOfArgsMarkerIndex, 0, '--crash-reporter-id', crashReporterId); + } + } + } +} + +// Start crash reporter for all processes +const productName = (product.crashReporter ? product.crashReporter.productName : undefined) || product.nameShort; +const companyName = (product.crashReporter ? product.crashReporter.companyName : undefined) || 'Microsoft'; +crashReporter.start({ + companyName: companyName, + productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName, + submitURL, + uploadToServer: !crashReporterDirectory +}); + // Set logs path before app 'ready' event if running portable // to ensure that no 'logs' folder is created on disk at a // location outside of the portable directory @@ -45,7 +116,23 @@ setCurrentWorkingDirectory(); // Register custom schemes with privileges protocol.registerSchemesAsPrivileged([ - { scheme: 'vscode-resource', privileges: { secure: true, supportFetchAPI: true, corsEnabled: true } } + { + scheme: 'vscode-webview', + privileges: { + standard: true, + secure: true, + supportFetchAPI: true, + corsEnabled: true, + } + }, { + scheme: 'vscode-webview-resource', + privileges: { + secure: true, + standard: true, + supportFetchAPI: true, + corsEnabled: true, + } + }, ]); // Global app listeners @@ -54,9 +141,6 @@ registerListeners(); // Cached data const nodeCachedDataDir = getNodeCachedDir(); -// Configure static command line arguments -const argvConfig = configureCommandlineSwitchesSync(args); - /** * Support user defined locale: load it early before app('ready') * to have more things running in parallel. @@ -74,7 +158,6 @@ if (locale) { // Load our code once ready app.once('ready', function () { if (args['trace']) { - // @ts-ignore const contentTracing = require('electron').contentTracing; const traceOptions = { @@ -82,7 +165,7 @@ app.once('ready', function () { traceOptions: args['trace-options'] || 'record-until-full,enable-sampling' }; - contentTracing.startRecording(traceOptions, () => onReady()); + contentTracing.startRecording(traceOptions).finally(() => onReady()); } else { onReady(); } @@ -120,9 +203,9 @@ async function onReady() { } /** - * @typedef {{ [arg: string]: any; '--'?: string[]; _: string[]; }} ParsedArgs + * @typedef {{ [arg: string]: any; '--'?: string[]; _: string[]; }} NativeParsedArgs * - * @param {ParsedArgs} cliArgs + * @param {NativeParsedArgs} cliArgs */ function configureCommandlineSwitchesSync(cliArgs) { const SUPPORTED_ELECTRON_SWITCHES = [ @@ -131,24 +214,58 @@ function configureCommandlineSwitchesSync(cliArgs) { 'disable-hardware-acceleration', // provided by Electron - 'disable-color-correct-rendering' + 'disable-color-correct-rendering', + + // override for the color profile to use + 'force-color-profile' + ]; + + if (process.platform === 'linux') { + + // Force enable screen readers on Linux via this flag + SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility'); + } + + const SUPPORTED_MAIN_PROCESS_SWITCHES = [ + + // Persistently enable proposed api via argv.json: https://github.com/microsoft/vscode/issues/99775 + 'enable-proposed-api' ]; // Read argv config const argvConfig = readArgvConfigSync(); - // Append each flag to Electron Object.keys(argvConfig).forEach(argvKey => { - if (SUPPORTED_ELECTRON_SWITCHES.indexOf(argvKey) === -1) { - return; // unsupported argv key + const argvValue = argvConfig[argvKey]; + + // Append Electron flags to Electron + if (SUPPORTED_ELECTRON_SWITCHES.indexOf(argvKey) !== -1) { + + // Color profile + if (argvKey === 'force-color-profile') { + if (argvValue) { + app.commandLine.appendSwitch(argvKey, argvValue); + } + } + + // Others + else if (argvValue === true || argvValue === 'true') { + if (argvKey === 'disable-hardware-acceleration') { + app.disableHardwareAcceleration(); // needs to be called explicitly + } else { + app.commandLine.appendSwitch(argvKey); + } + } } - const argvValue = argvConfig[argvKey]; - if (argvValue === true || argvValue === 'true') { - if (argvKey === 'disable-hardware-acceleration') { - app.disableHardwareAcceleration(); // needs to be called explicitly - } else { - app.commandLine.appendSwitch(argvKey); + // Append main process flags to process.argv + else if (SUPPORTED_MAIN_PROCESS_SWITCHES.indexOf(argvKey) !== -1) { + if (argvKey === 'enable-proposed-api') { + if (Array.isArray(argvValue)) { + argvValue.forEach(id => id && typeof id === 'string' && process.argv.push('--enable-proposed-api', id)); + } else { + console.error(`Unexpected value for \`enable-proposed-api\` in argv.json. Expected array of extension ids.`); + } } } }); @@ -180,7 +297,7 @@ function readArgvConfigSync() { // Fallback to default if (!argvConfig) { argvConfig = { - 'disable-color-correct-rendering': true // Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) + 'disable-color-correct-rendering': true // Force pre-Chrome-60 color profile handling (for https://github.com/microsoft/vscode/issues/51791) }; } @@ -199,21 +316,10 @@ function createDefaultArgvConfigSync(argvConfigPath) { fs.mkdirSync(argvConfigPathDirname); } - // Migrate over legacy locale - const localeConfigPath = path.join(userDataPath, 'User', 'locale.json'); - const legacyLocale = getLegacyUserDefinedLocaleSync(localeConfigPath); - if (legacyLocale) { - try { - fs.unlinkSync(localeConfigPath); - } catch (error) { - //ignore - } - } - // Default argv content const defaultArgvConfigContent = [ '// This configuration file allows you to pass permanent command line arguments to VS Code.', - '// Only a subset of arguments is currently supported to reduce the likelyhood of breaking', + '// Only a subset of arguments is currently supported to reduce the likelihood of breaking', '// the installation.', '//', '// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT', @@ -225,20 +331,11 @@ function createDefaultArgvConfigSync(argvConfigPath) { ' // "disable-hardware-acceleration": true,', '', ' // Enabled by default by VS Code to resolve color issues in the renderer', - ' // See https://github.com/Microsoft/vscode/issues/51791 for details', - ' "disable-color-correct-rendering": true' + ' // See https://github.com/microsoft/vscode/issues/51791 for details', + ' "disable-color-correct-rendering": true', + '}' ]; - if (legacyLocale) { - defaultArgvConfigContent[defaultArgvConfigContent.length - 1] = `${defaultArgvConfigContent[defaultArgvConfigContent.length - 1]},`; // append trailing "," - - defaultArgvConfigContent.push(''); - defaultArgvConfigContent.push(' // Display language of VS Code'); - defaultArgvConfigContent.push(` "locale": "${legacyLocale}"`); - } - - defaultArgvConfigContent.push('}'); - // Create initial argv.json with default content fs.writeFileSync(argvConfigPath, defaultArgvConfigContent.join('\n')); } catch (error) { @@ -261,7 +358,7 @@ function getArgvConfigPath() { } /** - * @param {ParsedArgs} cliArgs + * @param {NativeParsedArgs} cliArgs * @returns {string} */ function getJSFlags(cliArgs) { @@ -281,7 +378,7 @@ function getJSFlags(cliArgs) { } /** - * @param {ParsedArgs} cliArgs + * @param {NativeParsedArgs} cliArgs * * @returns {string} */ @@ -294,17 +391,18 @@ function getUserDataPath(cliArgs) { } /** - * @returns {ParsedArgs} + * @returns {NativeParsedArgs} */ function parseCLIArgs() { - const minimist = require('vscode-minimist'); + const minimist = require('minimist'); return minimist(process.argv, { string: [ 'user-data-dir', 'locale', 'js-flags', - 'max-memory' + 'max-memory', + 'crash-reporter-directory' ] }); } @@ -325,7 +423,7 @@ function setCurrentWorkingDirectory() { function registerListeners() { /** - * Mac: when someone drops a file to the not-yet running VSCode, the open-file event fires even before + * macOS: when someone drops a file to the not-yet running VSCode, the open-file event fires even before * the app-ready event. We listen very early for open-file and remember this upon startup as path to open. * * @type {string[]} @@ -337,7 +435,7 @@ function registerListeners() { }); /** - * React to open-url requests. + * macOS: react to open-url requests. * * @type {string[]} */ @@ -371,7 +469,7 @@ function getNodeCachedDir() { async ensureExists() { try { - await bootstrap.mkdirp(this.value); + await mkdirp(this.value); return this.value; } catch (error) { @@ -400,7 +498,20 @@ function getNodeCachedDir() { }; } +/** + * @param {string} dir + * @returns {Promise} + */ +function mkdirp(dir) { + const fs = require('fs'); + + return new Promise((resolve, reject) => { + fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? reject(err) : resolve(dir)); + }); +} + //#region NLS Support + /** * Resolve the NLS configuration * @@ -482,18 +593,4 @@ function getUserDefinedLocale(argvConfig) { return argvConfig.locale && typeof argvConfig.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined; } -/** - * @param {string} localeConfigPath - * @returns {string | undefined} - */ -function getLegacyUserDefinedLocaleSync(localeConfigPath) { - try { - const content = stripComments(fs.readFileSync(localeConfigPath).toString()); - - const value = JSON.parse(content).locale; - return value && typeof value === 'string' ? value.toLowerCase() : undefined; - } catch (error) { - // ignore - } -} //#endregion diff --git a/src/paths.js b/src/paths.js index 33c691bf72b..a88927d200b 100644 --- a/src/paths.js +++ b/src/paths.js @@ -6,7 +6,6 @@ //@ts-check 'use strict'; -// @ts-ignore const pkg = require('../package.json'); const path = require('path'); const os = require('os'); @@ -33,4 +32,4 @@ function getDefaultUserDataPath(platform) { } exports.getAppDataPath = getAppDataPath; -exports.getDefaultUserDataPath = getDefaultUserDataPath; \ No newline at end of file +exports.getDefaultUserDataPath = getDefaultUserDataPath; diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index 44595cf5246..6decaae056e 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -2,21 +2,26 @@ "compilerOptions": { "module": "amd", "moduleResolution": "node", - "noImplicitAny": true, "experimentalDecorators": true, "noImplicitReturns": true, "noUnusedLocals": true, - "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, + "allowUnreachableCode": false, + "strict": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "vs/*": [ "./vs/*" ] - } + }, + "lib": [ + "ES2015", + "ES2016.Array.Include", + "ES2017.String", + "ES2018.Promise", + "DOM", + "DOM.Iterable", + "WebWorker.ImportScripts" + ] } } diff --git a/src/tsconfig.json b/src/tsconfig.json index b8cc1caf2cd..5e81e53802a 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -4,26 +4,19 @@ "removeComments": false, "preserveConstEnums": true, "sourceMap": false, - "outDir": "../out", + "outDir": "../out/vs", "target": "es2017", - "lib": [ - "dom", - "es5", - "es2015.iterable" - ], "types": [ "keytar", "mocha", "semver", "sinon", - "winreg" + "winreg", + "trusted-types" ] }, "include": [ "./typings", "./vs" - ], - "exclude": [ - "./typings/es6-promise.d.ts" ] } diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index a6430a44ccb..a36bbe6f1f2 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -2,31 +2,27 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true, - "types": [], + "types": [ + "trusted-types" + ], "paths": {}, "module": "amd", "moduleResolution": "classic", "removeComments": false, "preserveConstEnums": true, - "target": "es5", + "target": "es6", "sourceMap": false, "declaration": true }, "include": [ "typings/require.d.ts", "typings/thenable.d.ts", - "typings/es6-promise.d.ts", - "typings/lib.es2018.promise.d.ts", - "typings/lib.array-ext.d.ts", - "typings/lib.ie11_safe_es6.d.ts", "vs/css.d.ts", "vs/monaco.d.ts", "vs/nls.d.ts", "vs/editor/*", "vs/base/common/*", "vs/base/browser/*", - "vs/base/parts/tree/*", - "vs/base/parts/quickopen/*", "vs/platform/*/common/*", "vs/platform/*/browser/*" ], diff --git a/src/typings/cgmanifest.json b/src/typings/cgmanifest.json deleted file mode 100644 index 6e529a79f23..00000000000 --- a/src/typings/cgmanifest.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "definitelytyped", - "repositoryUrl": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "commitHash": "69e3ac6bec3008271f76bbfa7cf69aa9198c4ff0" - } - }, - "license": "MIT" - } - ], - "version": 1 -} diff --git a/src/typings/es2015-proxy.d.ts b/src/typings/es2015-proxy.d.ts deleted file mode 100644 index 00f7c2b0642..00000000000 --- a/src/typings/es2015-proxy.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// from TypeScript: lib.es2015.proxy.d.ts - -interface ProxyHandler { - getPrototypeOf?(target: T): object | null; - setPrototypeOf?(target: T, v: any): boolean; - isExtensible?(target: T): boolean; - preventExtensions?(target: T): boolean; - getOwnPropertyDescriptor?(target: T, p: PropertyKey): PropertyDescriptor | undefined; - has?(target: T, p: PropertyKey): boolean; - get?(target: T, p: PropertyKey, receiver: any): any; - set?(target: T, p: PropertyKey, value: any, receiver: any): boolean; - deleteProperty?(target: T, p: PropertyKey): boolean; - defineProperty?(target: T, p: PropertyKey, attributes: PropertyDescriptor): boolean; - enumerate?(target: T): PropertyKey[]; - ownKeys?(target: T): PropertyKey[]; - apply?(target: T, thisArg: any, argArray?: any): any; - construct?(target: T, argArray: any, newTarget?: any): object; -} - -interface ProxyConstructor { - revocable(target: T, handler: ProxyHandler): { proxy: T; revoke: () => void; }; - new (target: T, handler: ProxyHandler): T; -} -declare var Proxy: ProxyConstructor; diff --git a/src/typings/es6-promise.d.ts b/src/typings/es6-promise.d.ts deleted file mode 100644 index 2d3271e2848..00000000000 --- a/src/typings/es6-promise.d.ts +++ /dev/null @@ -1,89 +0,0 @@ -// Type definitions for es6-promise -// Project: https://github.com/jakearchibald/ES6-Promise -// Definitions by: François de Campredon , vvakame -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -interface Thenable { - then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; - then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => void): Thenable; -} - -declare class Promise implements Thenable { - /** - * If you call resolve in the body of the callback passed to the constructor, - * your promise is fulfilled with result object passed to resolve. - * If you call reject your promise is rejected with the object passed to reject. - * For consistency and debugging (eg stack traces), obj should be an instanceof Error. - * Any errors thrown in the constructor callback will be implicitly passed to reject(). - */ - constructor(callback: (resolve: (value?: T | Thenable) => void, reject: (error?: any) => void) => void); - - /** - * onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects. - * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called. - * Both callbacks have a single parameter , the fulfillment value or rejection reason. - * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve. - * If an error is thrown in the callback, the returned promise rejects with that error. - * - * @param onFulfilled called when/if "promise" resolves - * @param onRejected called when/if "promise" rejects - */ - then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => U | Thenable): Promise; - then(onFulfilled?: (value: T) => U | Thenable, onRejected?: (error: any) => void): Promise; - - /** - * Sugar for promise.then(undefined, onRejected) - * - * @param onRejected called when/if "promise" rejects - */ - catch(onRejected?: (error: any) => U | Thenable): Promise; -} - -declare namespace Promise { - /** - * Make a new promise from the thenable. - * A thenable is promise-like in as far as it has a "then" method. - */ - function resolve(value: T | Thenable): Promise; - - /** - * - */ - function resolve(): Promise; - - /** - * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error - */ - function reject(error: any): Promise; - function reject(error: T): Promise; - - /** - * Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects. - * the array passed to all can be a mixture of promise-like objects and other objects. - * The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value. - */ - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable, T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable, T10 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable, T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable, T9 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable, T5 | Thenable, T6 | Thenable, T7 | Thenable, T8 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable, T5 | Thenable, T6 | Thenable, T7 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable, T5 | Thenable, T6 | Thenable]): Promise<[T1, T2, T3, T4, T5, T6]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable, T5 | Thenable]): Promise<[T1, T2, T3, T4, T5]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable, T4 | Thenable]): Promise<[T1, T2, T3, T4]>; - function all(values: [T1 | Thenable, T2 | Thenable, T3 | Thenable]): Promise<[T1, T2, T3]>; - function all(values: [T1 | Thenable, T2 | Thenable]): Promise<[T1, T2]>; - function all(values: (T | Thenable)[]): Promise; - - /** - * Make a Promise that fulfills when any item fulfills, and rejects if any item rejects. - */ - function race(promises: (T | Thenable)[]): Promise; -} - -declare module 'es6-promise' { - var foo: typeof Promise; // Temp variable to reference Promise in local context - namespace rsvp { - export var Promise: typeof foo; - export function polyfill(): void; - } - export = rsvp; -} diff --git a/src/typings/lib.es2018.promise.d.ts b/src/typings/lib.es2018.promise.d.ts deleted file mode 100644 index 9f7b2d38cb2..00000000000 --- a/src/typings/lib.es2018.promise.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ - -/** - * Represents the completion of an asynchronous operation - */ -interface Promise { - /** - * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The - * resolved value cannot be modified from the callback. - * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). - * @returns A Promise for the completion of the callback. - */ - finally(onfinally?: (() => void) | undefined | null): Promise; -} diff --git a/src/typings/lib.ie11_safe_es6.d.ts b/src/typings/lib.ie11_safe_es6.d.ts deleted file mode 100644 index 4d54d3c08ef..00000000000 --- a/src/typings/lib.ie11_safe_es6.d.ts +++ /dev/null @@ -1,821 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// Defined a subset of ES6 built ins that run in IE11 -// CHECK WITH http://kangax.github.io/compat-table/es6/#ie11 - -interface Map { - clear(): void; - delete(key: K): boolean; - forEach(callbackfn: (value: V, index: K, map: Map) => void, thisArg?: any): void; - get(key: K): V | undefined; - has(key: K): boolean; - set(key: K, value: V): Map; - readonly size: number; - - // not supported on IE11: - // entries(): IterableIterator<[K, V]>; - // keys(): IterableIterator; - // values(): IterableIterator; - // [Symbol.iterator]():IterableIterator<[K,V]>; - // [Symbol.toStringTag]: string; -} - -interface MapConstructor { - new (): Map; - readonly prototype: Map; - - // not supported on IE11: - // new (iterable: Iterable<[K, V]>): Map; -} -declare var Map: MapConstructor; - - -interface Set { - add(value: T): Set; - clear(): void; - delete(value: T): boolean; - forEach(callbackfn: (value: T, index: T, set: Set) => void, thisArg?: any): void; - has(value: T): boolean; - readonly size: number; - - // not supported on IE11: - // entries(): IterableIterator<[T, T]>; - // keys(): IterableIterator; - // values(): IterableIterator; - // [Symbol.iterator]():IterableIterator; - // [Symbol.toStringTag]: string; -} - -interface SetConstructor { - new (): Set; - readonly prototype: Set; - - // not supported on IE11: - // new (iterable: Iterable): Set; -} -declare var Set: SetConstructor; - - -interface WeakMap { - delete(key: K): boolean; - get(key: K): V | undefined; - has(key: K): boolean; - // IE11 doesn't return this - // set(key: K, value?: V): this; - set(key: K, value?: V): undefined; -} - -interface WeakMapConstructor { - new(): WeakMap; - new (): WeakMap; - // new (entries?: [K, V][]): WeakMap; - readonly prototype: WeakMap; -} -declare var WeakMap: WeakMapConstructor; - - -// /** -// * Represents a raw buffer of binary data, which is used to store data for the -// * different typed arrays. ArrayBuffers cannot be read from or written to directly, -// * but can be passed to a typed array or DataView Object to interpret the raw -// * buffer as needed. -// */ -// interface ArrayBuffer { -// /** -// * Read-only. The length of the ArrayBuffer (in bytes). -// */ -// readonly byteLength: number; - -// /** -// * Returns a section of an ArrayBuffer. -// */ -// slice(begin: number, end?: number): ArrayBuffer; -// } - -// interface ArrayBufferConstructor { -// readonly prototype: ArrayBuffer; -// new (byteLength: number): ArrayBuffer; -// isView(arg: any): arg is ArrayBufferView; -// } -// declare const ArrayBuffer: ArrayBufferConstructor; - -// interface ArrayBufferView { -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// byteOffset: number; -// } - -// interface DataView { -// readonly buffer: ArrayBuffer; -// readonly byteLength: number; -// readonly byteOffset: number; -// /** -// * Gets the Float32 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getFloat32(byteOffset: number, littleEndian?: boolean): number; - -// /** -// * Gets the Float64 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getFloat64(byteOffset: number, littleEndian?: boolean): number; - -// /** -// * Gets the Int8 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getInt8(byteOffset: number): number; - -// /** -// * Gets the Int16 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getInt16(byteOffset: number, littleEndian?: boolean): number; -// /** -// * Gets the Int32 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getInt32(byteOffset: number, littleEndian?: boolean): number; - -// /** -// * Gets the Uint8 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getUint8(byteOffset: number): number; - -// /** -// * Gets the Uint16 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getUint16(byteOffset: number, littleEndian?: boolean): number; - -// /** -// * Gets the Uint32 value at the specified byte offset from the start of the view. There is -// * no alignment constraint; multi-byte values may be fetched from any offset. -// * @param byteOffset The place in the buffer at which the value should be retrieved. -// */ -// getUint32(byteOffset: number, littleEndian?: boolean): number; - -// /** -// * Stores an Float32 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// * @param littleEndian If false or undefined, a big-endian value should be written, -// * otherwise a little-endian value should be written. -// */ -// setFloat32(byteOffset: number, value: number, littleEndian?: boolean): void; - -// /** -// * Stores an Float64 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// * @param littleEndian If false or undefined, a big-endian value should be written, -// * otherwise a little-endian value should be written. -// */ -// setFloat64(byteOffset: number, value: number, littleEndian?: boolean): void; - -// /** -// * Stores an Int8 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// */ -// setInt8(byteOffset: number, value: number): void; - -// /** -// * Stores an Int16 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// * @param littleEndian If false or undefined, a big-endian value should be written, -// * otherwise a little-endian value should be written. -// */ -// setInt16(byteOffset: number, value: number, littleEndian?: boolean): void; - -// /** -// * Stores an Int32 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// * @param littleEndian If false or undefined, a big-endian value should be written, -// * otherwise a little-endian value should be written. -// */ -// setInt32(byteOffset: number, value: number, littleEndian?: boolean): void; - -// /** -// * Stores an Uint8 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// */ -// setUint8(byteOffset: number, value: number): void; - -// /** -// * Stores an Uint16 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// * @param littleEndian If false or undefined, a big-endian value should be written, -// * otherwise a little-endian value should be written. -// */ -// setUint16(byteOffset: number, value: number, littleEndian?: boolean): void; - -// /** -// * Stores an Uint32 value at the specified byte offset from the start of the view. -// * @param byteOffset The place in the buffer at which the value should be set. -// * @param value The value to set. -// * @param littleEndian If false or undefined, a big-endian value should be written, -// * otherwise a little-endian value should be written. -// */ -// setUint32(byteOffset: number, value: number, littleEndian?: boolean): void; -// } - -// interface DataViewConstructor { -// new (buffer: ArrayBuffer, byteOffset?: number, byteLength?: number): DataView; -// } -// declare const DataView: DataViewConstructor; - - -// /** -// * A typed array of 8-bit integer values. The contents are initialized to 0. If the requested -// * number of bytes could not be allocated an exception is raised. -// */ -// interface Int8Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } -// interface Int8ArrayConstructor { -// readonly prototype: Int8Array; -// new (length: number): Int8Array; -// new (array: ArrayLike): Int8Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int8Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// } -// declare const Int8Array: Int8ArrayConstructor; - -// /** -// * A typed array of 8-bit unsigned integer values. The contents are initialized to 0. If the -// * requested number of bytes could not be allocated an exception is raised. -// */ -// interface Uint8Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Uint8ArrayConstructor { -// readonly prototype: Uint8Array; -// new (length: number): Uint8Array; -// new (array: ArrayLike): Uint8Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint8Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// } -// declare const Uint8Array: Uint8ArrayConstructor; - - -// /** -// * A typed array of 16-bit signed integer values. The contents are initialized to 0. If the -// * requested number of bytes could not be allocated an exception is raised. -// */ -// interface Int16Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Int16ArrayConstructor { -// readonly prototype: Int16Array; -// new (length: number): Int16Array; -// new (array: ArrayLike): Int16Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int16Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// } -// declare const Int16Array: Int16ArrayConstructor; - -// /** -// * A typed array of 16-bit unsigned integer values. The contents are initialized to 0. If the -// * requested number of bytes could not be allocated an exception is raised. -// */ -// interface Uint16Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Uint16ArrayConstructor { -// readonly prototype: Uint16Array; -// new (length: number): Uint16Array; -// new (array: ArrayLike): Uint16Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint16Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// } -// declare const Uint16Array: Uint16ArrayConstructor; -// /** -// * A typed array of 32-bit signed integer values. The contents are initialized to 0. If the -// * requested number of bytes could not be allocated an exception is raised. -// */ -// interface Int32Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Int32ArrayConstructor { -// readonly prototype: Int32Array; -// new (length: number): Int32Array; -// new (array: ArrayLike): Int32Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Int32Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; -// } - -// declare const Int32Array: Int32ArrayConstructor; - -// /** -// * A typed array of 32-bit unsigned integer values. The contents are initialized to 0. If the -// * requested number of bytes could not be allocated an exception is raised. -// */ -// interface Uint32Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Uint32ArrayConstructor { -// readonly prototype: Uint32Array; -// new (length: number): Uint32Array; -// new (array: ArrayLike): Uint32Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Uint32Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; -// } - -// declare const Uint32Array: Uint32ArrayConstructor; - -// /** -// * A typed array of 32-bit float values. The contents are initialized to 0. If the requested number -// * of bytes could not be allocated an exception is raised. -// */ -// interface Float32Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Float32ArrayConstructor { -// readonly prototype: Float32Array; -// new (length: number): Float32Array; -// new (array: ArrayLike): Float32Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Float32Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// } -// declare const Float32Array: Float32ArrayConstructor; - -// /** -// * A typed array of 64-bit float values. The contents are initialized to 0. If the requested -// * number of bytes could not be allocated an exception is raised. -// */ -// interface Float64Array { -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; - -// /** -// * The ArrayBuffer instance referenced by the array. -// */ -// readonly buffer: ArrayBuffer; - -// /** -// * The length in bytes of the array. -// */ -// readonly byteLength: number; - -// /** -// * The offset in bytes of the array. -// */ -// readonly byteOffset: number; - -// /** -// * The length of the array. -// */ -// readonly length: number; - -// /** -// * Sets a value or an array of values. -// * @param index The index of the location to set. -// * @param value The value to set. -// */ -// set(index: number, value: number): void; - -// /** -// * Sets a value or an array of values. -// * @param array A typed or untyped array of values to set. -// * @param offset The index in the current array at which the values are to be written. -// */ -// set(array: ArrayLike, offset?: number): void; - -// /** -// * Converts a number to a string by using the current locale. -// */ -// toLocaleString(): string; - -// /** -// * Returns a string representation of an array. -// */ -// toString(): string; - -// [index: number]: number; -// } - -// interface Float64ArrayConstructor { -// readonly prototype: Float64Array; -// new (length: number): Float64Array; -// new (array: ArrayLike): Float64Array; -// new (buffer: ArrayBuffer, byteOffset?: number, length?: number): Float64Array; - -// /** -// * The size in bytes of each element in the array. -// */ -// readonly BYTES_PER_ELEMENT: number; -// } - -// declare const Float64Array: Float64ArrayConstructor; diff --git a/src/typings/lib.webworker.importscripts.d.ts b/src/typings/lib.webworker.importscripts.d.ts deleted file mode 100644 index e84f717c9a4..00000000000 --- a/src/typings/lib.webworker.importscripts.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ - - - - -///////////////////////////// -/// WorkerGlobalScope APIs -///////////////////////////// -// These are only available in a Web Worker -declare function importScripts(...urls: string[]): void; diff --git a/src/typings/node.processEnv-ext.d.ts b/src/typings/node.processEnv-ext.d.ts deleted file mode 100644 index fec557ff2a7..00000000000 --- a/src/typings/node.processEnv-ext.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare namespace NodeJS { - - export interface Process { - - /** - * The lazy enviroment is a promise that resolves to `process.env` - * once the process is resolved. The use-case is VS Code running - * on Linux/macOS when being launched via a launcher. Then the env - * (as defined in .bashrc etc) isn't properly set and needs to be - * resolved lazy. - */ - lazyEnv: Thenable | undefined; - } -} diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index dfffac43e56..dc6c6bd10fd 100644 --- a/src/typings/require.d.ts +++ b/src/typings/require.d.ts @@ -41,12 +41,16 @@ declare const define: { }; interface NodeRequire { + /** + * @deprecated use `FileAccess.asFileUri()` for node.js contexts or `FileAccess.asBrowserUri` for browser contexts. + */ toUrl(path: string): string; (dependencies: string[], callback: (...args: any[]) => any, errorback?: (err: any) => void): any; config(data: any): any; onError: Function; __$__nodeRequire(moduleName: string): T; getStats(): ReadonlyArray; + define(amdModuleId: string, dependencies: string[], callback: (...args: any[]) => any): any; } declare var require: NodeRequire; diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index c7570602cdf..6b369dd3df5 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -28,7 +28,7 @@ class WindowManager { } this._zoomLevel = zoomLevel; - // See https://github.com/Microsoft/vscode/issues/26151 + // See https://github.com/microsoft/vscode/issues/26151 this._lastZoomLevelChangeTime = isTrusted ? 0 : Date.now(); this._onDidChangeZoomLevel.fire(this._zoomLevel); } @@ -110,16 +110,13 @@ export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscree const userAgent = navigator.userAgent; -export const isIE = (userAgent.indexOf('Trident') >= 0); export const isEdge = (userAgent.indexOf('Edge/') >= 0); -export const isEdgeOrIE = isIE || isEdge; - export const isOpera = (userAgent.indexOf('Opera') >= 0); export const isFirefox = (userAgent.indexOf('Firefox') >= 0); export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0); export const isChrome = (userAgent.indexOf('Chrome') >= 0); export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0)); export const isWebkitWebView = (!isChrome && !isSafari && isWebKit); -export const isIPad = (userAgent.indexOf('iPad') >= 0); +export const isIPad = (userAgent.indexOf('iPad') >= 0 || (isSafari && navigator.maxTouchPoints > 0)); export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0); export const isStandalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches); diff --git a/src/vs/base/browser/canIUse.ts b/src/vs/base/browser/canIUse.ts index cf8211e919e..3f5391410a7 100644 --- a/src/vs/base/browser/canIUse.ts +++ b/src/vs/base/browser/canIUse.ts @@ -27,10 +27,6 @@ export const BrowserFeatures = { || !!(navigator && navigator.clipboard && navigator.clipboard.readText) ), richText: (() => { - if (browser.isIE) { - return false; - } - if (browser.isEdge) { let index = navigator.userAgent.indexOf('Edge/'); let version = parseInt(navigator.userAgent.substring(index + 5, navigator.userAgent.indexOf('.', index)), 10); @@ -55,6 +51,8 @@ export const BrowserFeatures = { return KeyboardSupport.None; })(), - touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0, - pointerEvents: window.PointerEvent && ('ontouchstart' in window || window.navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0) + // 'ontouchstart' in window always evaluates to true with typescript's modern typings. This causes `window` to be + // `never` later in `window.navigator`. That's why we need the explicit `window as Window` cast + touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0, + pointerEvents: window.PointerEvent && ('ontouchstart' in window || (window as Window).navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0) }; diff --git a/src/vs/base/browser/codicons.ts b/src/vs/base/browser/codicons.ts new file mode 100644 index 00000000000..cf55c1a9b92 --- /dev/null +++ b/src/vs/base/browser/codicons.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; + +const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi; + +export function renderCodicons(text: string): Array { + const elements = new Array(); + let match: RegExpMatchArray | null; + + let textStart = 0, textStop = 0; + while ((match = renderCodiconsRegex.exec(text)) !== null) { + textStop = match.index || 0; + elements.push(text.substring(textStart, textStop)); + textStart = (match.index || 0) + match[0].length; + + const [, escaped, codicon, name, animation] = match; + elements.push(escaped ? `$(${codicon})` : renderCodicon(name, animation)); + } + + if (textStart < text.length) { + elements.push(text.substring(textStart)); + } + return elements; +} + +export function renderCodicon(name: string, animation: string): HTMLSpanElement { + return dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`); +} diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index caf1d299038..2119700f859 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { SubmenuAction } from 'vs/base/browser/ui/menu/menu'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; export interface IContextMenuEvent { @@ -16,15 +14,9 @@ export interface IContextMenuEvent { readonly metaKey?: boolean; } -export class ContextSubMenu extends SubmenuAction { - constructor(label: string, public entries: Array) { - super(label, entries, 'contextsubmenu'); - } -} - export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): ReadonlyArray; + getActions(): IAction[]; getCheckedActionsRepresentation?(action: IAction): 'radio' | 'checkbox'; getActionViewItem?(action: IAction): IActionViewItem | undefined; getActionsContext?(event?: IContextMenuEvent): any; @@ -34,4 +26,9 @@ export interface IContextMenuDelegate { actionRunner?: IActionRunner; autoSelectFirstItem?: boolean; anchorAlignment?: AnchorAlignment; + domForShadowRoot?: HTMLElement; +} + +export interface IContextMenuProvider { + showContextMenu(delegate: IContextMenuDelegate): void; } diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 4c2d05b208e..430ef80930f 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -8,15 +8,15 @@ import { domEvent } from 'vs/base/browser/event'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { TimeoutTimer } from 'vs/base/common/async'; -import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { coalesce } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; -import { Schemas, RemoteAuthorities } from 'vs/base/common/network'; +import { FileAccess, RemoteAuthorities } from 'vs/base/common/network'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { insane, InsaneOptions } from 'vs/base/common/insane/insane'; +import { KeyCode } from 'vs/base/common/keyCodes'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -24,12 +24,6 @@ export function clearNode(node: HTMLElement): void { } } -export function removeNode(node: HTMLElement): void { - if (node.parentNode) { - node.parentNode.removeChild(node); - } -} - export function isInDOM(node: Node | null): boolean { while (node) { if (node === document.body) { @@ -40,167 +34,6 @@ export function isInDOM(node: Node | null): boolean { return false; } -interface IDomClassList { - hasClass(node: HTMLElement | SVGElement, className: string): boolean; - addClass(node: HTMLElement | SVGElement, className: string): void; - addClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void; - removeClass(node: HTMLElement | SVGElement, className: string): void; - removeClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void; - toggleClass(node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean): void; -} - -const _manualClassList = new class implements IDomClassList { - - private _lastStart: number = -1; - private _lastEnd: number = -1; - - private _findClassName(node: HTMLElement, className: string): void { - - let classes = node.className; - if (!classes) { - this._lastStart = -1; - return; - } - - className = className.trim(); - - let classesLen = classes.length, - classLen = className.length; - - if (classLen === 0) { - this._lastStart = -1; - return; - } - - if (classesLen < classLen) { - this._lastStart = -1; - return; - } - - if (classes === className) { - this._lastStart = 0; - this._lastEnd = classesLen; - return; - } - - let idx = -1, - idxEnd: number; - - while ((idx = classes.indexOf(className, idx + 1)) >= 0) { - - idxEnd = idx + classLen; - - // a class that is followed by another class - if ((idx === 0 || classes.charCodeAt(idx - 1) === CharCode.Space) && classes.charCodeAt(idxEnd) === CharCode.Space) { - this._lastStart = idx; - this._lastEnd = idxEnd + 1; - return; - } - - // last class - if (idx > 0 && classes.charCodeAt(idx - 1) === CharCode.Space && idxEnd === classesLen) { - this._lastStart = idx - 1; - this._lastEnd = idxEnd; - return; - } - - // equal - duplicate of cmp above - if (idx === 0 && idxEnd === classesLen) { - this._lastStart = 0; - this._lastEnd = idxEnd; - return; - } - } - - this._lastStart = -1; - } - - hasClass(node: HTMLElement, className: string): boolean { - this._findClassName(node, className); - return this._lastStart !== -1; - } - - addClasses(node: HTMLElement, ...classNames: string[]): void { - classNames.forEach(nameValue => nameValue.split(' ').forEach(name => this.addClass(node, name))); - } - - addClass(node: HTMLElement, className: string): void { - if (!node.className) { // doesn't have it for sure - node.className = className; - } else { - this._findClassName(node, className); // see if it's already there - if (this._lastStart === -1) { - node.className = node.className + ' ' + className; - } - } - } - - removeClass(node: HTMLElement, className: string): void { - this._findClassName(node, className); - if (this._lastStart === -1) { - return; // Prevent styles invalidation if not necessary - } else { - node.className = node.className.substring(0, this._lastStart) + node.className.substring(this._lastEnd); - } - } - - removeClasses(node: HTMLElement, ...classNames: string[]): void { - classNames.forEach(nameValue => nameValue.split(' ').forEach(name => this.removeClass(node, name))); - } - - toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void { - this._findClassName(node, className); - if (this._lastStart !== -1 && (shouldHaveIt === undefined || !shouldHaveIt)) { - this.removeClass(node, className); - } - if (this._lastStart === -1 && (shouldHaveIt === undefined || shouldHaveIt)) { - this.addClass(node, className); - } - } -}; - -const _nativeClassList = new class implements IDomClassList { - hasClass(node: HTMLElement, className: string): boolean { - return Boolean(className) && node.classList && node.classList.contains(className); - } - - addClasses(node: HTMLElement, ...classNames: string[]): void { - classNames.forEach(nameValue => nameValue.split(' ').forEach(name => this.addClass(node, name))); - } - - addClass(node: HTMLElement, className: string): void { - if (className && node.classList) { - node.classList.add(className); - } - } - - removeClass(node: HTMLElement, className: string): void { - if (className && node.classList) { - node.classList.remove(className); - } - } - - removeClasses(node: HTMLElement, ...classNames: string[]): void { - classNames.forEach(nameValue => nameValue.split(' ').forEach(name => this.removeClass(node, name))); - } - - toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void { - if (node.classList) { - node.classList.toggle(className, shouldHaveIt); - } - } -}; - -// In IE11 there is only partial support for `classList` which makes us keep our -// custom implementation. Otherwise use the native implementation, see: http://caniuse.com/#search=classlist -const _classList: IDomClassList = browser.isIE ? _manualClassList : _nativeClassList; -export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList); -export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList); -export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList); -export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList); -export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList); -export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList); - class DomListener implements IDisposable { private _handler: (e: any) => void; @@ -273,6 +106,11 @@ export let addStandardDisposableGenericMouseDownListner = function addStandardDi return addDisposableGenericMouseDownListner(node, wrapHandler, useCapture); }; +export let addStandardDisposableGenericMouseUpListner = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { + let wrapHandler = _wrapAsStandardMouseEvent(handler); + + return addDisposableGenericMouseUpListner(node, wrapHandler, useCapture); +}; export function addDisposableGenericMouseDownListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_DOWN : EventType.MOUSE_DOWN, handler, useCapture); } @@ -511,9 +349,9 @@ export function getClientArea(element: HTMLElement): Dimension { } // If visual view port exits and it's on mobile, it should be used instead of window innerWidth / innerHeight, or document.body.clientWidth / document.body.clientHeight - if (platform.isIOS && (window).visualViewport) { - const width = (window).visualViewport.width; - const height = (window).visualViewport.height - ( + if (platform.isIOS && window.visualViewport) { + const width = window.visualViewport.width; + const height = window.visualViewport.height - ( browser.isStandalone // in PWA mode, the visual viewport always includes the safe-area-inset-bottom (which is for the home indicator) // even when you are using the onscreen monitor, the visual viewport will include the area between system statusbar and the onscreen keyboard @@ -606,13 +444,38 @@ class SizeUtils { // ---------------------------------------------------------------------------------------- // Position & Dimension -export class Dimension { +export interface IDimension { + readonly width: number; + readonly height: number; +} + +export class Dimension implements IDimension { constructor( public readonly width: number, public readonly height: number, ) { } + with(width: number = this.width, height: number = this.height): Dimension { + if (width !== this.width || height !== this.height) { + return new Dimension(width, height); + } else { + return this; + } + } + + static is(obj: unknown): obj is IDimension { + return typeof obj === 'object' && typeof (obj).height === 'number' && typeof (obj).width === 'number'; + } + + static lift(obj: IDimension): Dimension { + if (obj instanceof Dimension) { + return obj; + } else { + return new Dimension(obj.width, obj.height); + } + } + static equals(a: Dimension | undefined, b: Dimension | undefined): boolean { if (a === b) { return true; @@ -800,13 +663,13 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null { while (node && node.nodeType === node.ELEMENT_NODE) { - if (hasClass(node, clazz)) { + if (node.classList.contains(clazz)) { return node; } if (stopAtClazzOrNode) { if (typeof stopAtClazzOrNode === 'string') { - if (hasClass(node, stopAtClazzOrNode)) { + if (node.classList.contains(stopAtClazzOrNode)) { return null; } } else { @@ -847,6 +710,16 @@ export function getShadowRoot(domNode: Node): ShadowRoot | null { return isShadowRoot(domNode) ? domNode : null; } +export function getActiveElement(): Element | null { + let result = document.activeElement; + + while (result?.shadowRoot) { + result = result.shadowRoot.activeElement; + } + + return result; +} + export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement { let style = document.createElement('style'); style.type = 'text/css'; @@ -870,11 +743,11 @@ function getSharedStyleSheet(): HTMLStyleElement { } function getDynamicStyleSheetRules(style: any) { - if (style && style.sheet && style.sheet.rules) { + if (style?.sheet?.rules) { // Chrome, IE return style.sheet.rules; } - if (style && style.sheet && style.sheet.cssRules) { + if (style?.sheet?.cssRules) { // FF return style.sheet.cssRules; } @@ -918,6 +791,7 @@ export function isHTMLElement(o: any): o is HTMLElement { export const EventType = { // Mouse CLICK: 'click', + AUXCLICK: 'auxclick', DBLCLICK: 'dblclick', MOUSE_UP: 'mouseup', MOUSE_DOWN: 'mousedown', @@ -926,6 +800,7 @@ export const EventType = { MOUSE_OUT: 'mouseout', MOUSE_ENTER: 'mouseenter', MOUSE_LEAVE: 'mouseleave', + MOUSE_WHEEL: browser.isEdge ? 'mousewheel' : 'wheel', POINTER_UP: 'pointerup', POINTER_DOWN: 'pointerdown', POINTER_MOVE: 'pointermove', @@ -1080,6 +955,11 @@ export function trackFocus(element: HTMLElement | Window): IFocusTracker { return new FocusTracker(element); } +export function after(sibling: HTMLElement, child: T): T { + sibling.after(child); + return child; +} + export function append(parent: HTMLElement, ...children: T[]): T { children.forEach(child => parent.appendChild(child)); return children[children.length - 1]; @@ -1090,7 +970,29 @@ export function prepend(parent: HTMLElement, child: T): T { return child; } -const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/; + +/** + * Removes all children from `parent` and appends `children` + */ +export function reset(parent: HTMLElement, ...children: Array): void { + parent.innerText = ''; + appendChildren(parent, ...children); +} + +/** + * Appends `children` to `parent` + */ +export function appendChildren(parent: HTMLElement, ...children: Array): void { + for (const child of children) { + if (child instanceof Node) { + parent.appendChild(child); + } else if (typeof child === 'string') { + parent.appendChild(document.createTextNode(child)); + } + } +} + +const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/; export enum Namespace { HTML = 'http://www.w3.org/1999/xhtml', @@ -1141,14 +1043,13 @@ function _$(namespace: Namespace, description: string, attrs? } }); - coalesce(children) - .forEach(child => { - if (child instanceof Node) { - result.appendChild(child); - } else { - result.appendChild(document.createTextNode(child as string)); - } - }); + for (const child of children) { + if (child instanceof Node) { + result.appendChild(child); + } else if (typeof child === 'string') { + result.appendChild(document.createTextNode(child)); + } + } return result as T; } @@ -1261,7 +1162,7 @@ export function computeScreenAwareSize(cssPx: number): number { } /** - * See https://github.com/Microsoft/monaco-editor/issues/601 + * See https://github.com/microsoft/monaco-editor/issues/601 * To protect against malicious code in the linked site, particularly phishing attempts, * the window.opener should be set to null to prevent the linked site from having access * to change the location of the current page. @@ -1270,7 +1171,7 @@ export function computeScreenAwareSize(cssPx: number): number { export function windowOpenNoOpener(url: string): void { if (platform.isNative || browser.isEdgeWebView) { // In VSCode, window.open() always returns null... - // The same is true for a WebView (see https://github.com/Microsoft/monaco-editor/issues/628) + // The same is true for a WebView (see https://github.com/microsoft/monaco-editor/issues/628) window.open(url); } else { let newTab = window.open(); @@ -1293,16 +1194,6 @@ export function animate(fn: () => void): IDisposable { RemoteAuthorities.setPreferredWebSchema(/^https:/.test(window.location.href) ? 'https' : 'http'); -export function asDomUri(uri: URI): URI { - if (!uri) { - return uri; - } - if (Schemas.vscodeRemote === uri.scheme) { - return RemoteAuthorities.rewrite(uri); - } - return uri; -} - /** * returns url('...') */ @@ -1310,10 +1201,24 @@ export function asCSSUrl(uri: URI): string { if (!uri) { return `url('')`; } - return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`; + return `url('${FileAccess.asBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`; } -export function triggerDownload(uri: URI, name: string): void { +export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void { + + // If the data is provided as Buffer, we create a + // blog URL out of it to produce a valid link + let url: string; + if (URI.isUri(dataOrUri)) { + url = dataOrUri.toString(true); + } else { + const blob = new Blob([dataOrUri]); + url = URL.createObjectURL(blob); + + // Ensure to free the data from DOM eventually + setTimeout(() => URL.revokeObjectURL(url)); + } + // In order to download from the browser, the only way seems // to be creating a element with download attribute that // points to the file to download. @@ -1321,9 +1226,330 @@ export function triggerDownload(uri: URI, name: string): void { const anchor = document.createElement('a'); document.body.appendChild(anchor); anchor.download = name; - anchor.href = uri.toString(true); + anchor.href = url; anchor.click(); // Ensure to remove the element from DOM eventually setTimeout(() => document.body.removeChild(anchor)); } + +export enum DetectedFullscreenMode { + + /** + * The document is fullscreen, e.g. because an element + * in the document requested to be fullscreen. + */ + DOCUMENT = 1, + + /** + * The browser is fullsreen, e.g. because the user enabled + * native window fullscreen for it. + */ + BROWSER +} + +export interface IDetectedFullscreen { + + /** + * Figure out if the document is fullscreen or the browser. + */ + mode: DetectedFullscreenMode; + + /** + * Wether we know for sure that we are in fullscreen mode or + * it is a guess. + */ + guess: boolean; +} + +export function detectFullscreen(): IDetectedFullscreen | null { + + // Browser fullscreen: use DOM APIs to detect + if (document.fullscreenElement || (document).webkitFullscreenElement || (document).webkitIsFullScreen) { + return { mode: DetectedFullscreenMode.DOCUMENT, guess: false }; + } + + // There is no standard way to figure out if the browser + // is using native fullscreen. Via checking on screen + // height and comparing that to window height, we can guess + // it though. + + if (window.innerHeight === screen.height) { + // if the height of the window matches the screen height, we can + // safely assume that the browser is fullscreen because no browser + // chrome is taking height away (e.g. like toolbars). + return { mode: DetectedFullscreenMode.BROWSER, guess: false }; + } + + if (platform.isMacintosh || platform.isLinux) { + // macOS and Linux do not properly report `innerHeight`, only Windows does + if (window.outerHeight === screen.height && window.outerWidth === screen.width) { + // if the height of the browser matches the screen height, we can + // only guess that we are in fullscreen. It is also possible that + // the user has turned off taskbars in the OS and the browser is + // simply able to span the entire size of the screen. + return { mode: DetectedFullscreenMode.BROWSER, guess: true }; + } + } + + // Not in fullscreen + return null; +} + +// -- sanitize and trusted html + +function _extInsaneOptions(opts: InsaneOptions, allowedAttributesForAll: string[]): InsaneOptions { + + let allowedAttributes: Record = opts.allowedAttributes ?? {}; + + if (opts.allowedTags) { + for (let tag of opts.allowedTags) { + let array = allowedAttributes[tag]; + if (!array) { + array = allowedAttributesForAll; + } else { + array = array.concat(allowedAttributesForAll); + } + allowedAttributes[tag] = array; + } + } + + return { ...opts, allowedAttributes }; +} + +const _ttpSafeInnerHtml = window.trustedTypes?.createPolicy('safeInnerHtml', { + createHTML(value, options: InsaneOptions) { + return insane(value, options); + } +}); + +/** + * Sanitizes the given `value` and reset the given `node` with it. + */ +export function safeInnerHtml(node: HTMLElement, value: string): void { + + const options = _extInsaneOptions({ + allowedTags: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'], + allowedAttributes: { + 'a': ['href'], + 'button': ['data-href'], + 'input': ['type', 'placeholder', 'checked', 'required'], + 'label': ['for'], + 'select': ['required'], + 'span': ['data-command', 'role'], + 'textarea': ['name', 'placeholder', 'required'], + }, + allowedSchemes: ['http', 'https', 'command'] + }, ['class', 'id', 'role', 'tabindex']); + + const html = _ttpSafeInnerHtml?.createHTML(value, options) ?? insane(value, options); + node.innerHTML = html as unknown as string; +} + +/** + * Convert a Unicode string to a string in which each 16-bit unit occupies only one byte + * + * From https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa + */ +function toBinary(str: string): string { + const codeUnits = new Uint16Array(str.length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = str.charCodeAt(i); + } + return String.fromCharCode(...new Uint8Array(codeUnits.buffer)); +} + +/** + * Version of the global `btoa` function that handles multi-byte characters instead + * of throwing an exception. + */ +export function multibyteAwareBtoa(str: string): string { + return btoa(toBinary(str)); +} + +/** + * Typings for the https://wicg.github.io/file-system-access + * + * Use `supported(window)` to find out if the browser supports this kind of API. + */ +export namespace WebFileSystemAccess { + + // https://wicg.github.io/file-system-access/#dom-window-showdirectorypicker + export interface FileSystemAccess { + showDirectoryPicker: () => Promise; + } + + // https://wicg.github.io/file-system-access/#api-filesystemdirectoryhandle + export interface FileSystemDirectoryHandle { + readonly kind: 'directory', + readonly name: string, + + getFileHandle: (name: string, options?: { create?: boolean }) => Promise; + getDirectoryHandle: (name: string, options?: { create?: boolean }) => Promise; + } + + // https://wicg.github.io/file-system-access/#api-filesystemfilehandle + export interface FileSystemFileHandle { + readonly kind: 'file', + readonly name: string, + + createWritable: (options?: { keepExistingData?: boolean }) => Promise; + } + + // https://wicg.github.io/file-system-access/#api-filesystemwritablefilestream + export interface FileSystemWritableFileStream { + write: (buffer: Uint8Array) => Promise; + close: () => Promise; + } + + export function supported(obj: any & Window): obj is FileSystemAccess { + const candidate = obj as FileSystemAccess; + if (typeof candidate?.showDirectoryPicker === 'function') { + return true; + } + + return false; + } +} + +type ModifierKey = 'alt' | 'ctrl' | 'shift' | 'meta'; + +export interface IModifierKeyStatus { + altKey: boolean; + shiftKey: boolean; + ctrlKey: boolean; + metaKey: boolean; + lastKeyPressed?: ModifierKey; + lastKeyReleased?: ModifierKey; + event?: KeyboardEvent; +} + +export class ModifierKeyEmitter extends Emitter { + + private readonly _subscriptions = new DisposableStore(); + private _keyStatus: IModifierKeyStatus; + private static instance: ModifierKeyEmitter; + + private constructor() { + super(); + + this._keyStatus = { + altKey: false, + shiftKey: false, + ctrlKey: false, + metaKey: false + }; + + this._subscriptions.add(domEvent(document.body, 'keydown', true)(e => { + const event = new StandardKeyboardEvent(e); + + if (e.altKey && !this._keyStatus.altKey) { + this._keyStatus.lastKeyPressed = 'alt'; + } else if (e.ctrlKey && !this._keyStatus.ctrlKey) { + this._keyStatus.lastKeyPressed = 'ctrl'; + } else if (e.metaKey && !this._keyStatus.metaKey) { + this._keyStatus.lastKeyPressed = 'meta'; + } else if (e.shiftKey && !this._keyStatus.shiftKey) { + this._keyStatus.lastKeyPressed = 'shift'; + } else if (event.keyCode !== KeyCode.Alt) { + this._keyStatus.lastKeyPressed = undefined; + } else { + return; + } + + this._keyStatus.altKey = e.altKey; + this._keyStatus.ctrlKey = e.ctrlKey; + this._keyStatus.metaKey = e.metaKey; + this._keyStatus.shiftKey = e.shiftKey; + + if (this._keyStatus.lastKeyPressed) { + this._keyStatus.event = e; + this.fire(this._keyStatus); + } + })); + + this._subscriptions.add(domEvent(document.body, 'keyup', true)(e => { + if (!e.altKey && this._keyStatus.altKey) { + this._keyStatus.lastKeyReleased = 'alt'; + } else if (!e.ctrlKey && this._keyStatus.ctrlKey) { + this._keyStatus.lastKeyReleased = 'ctrl'; + } else if (!e.metaKey && this._keyStatus.metaKey) { + this._keyStatus.lastKeyReleased = 'meta'; + } else if (!e.shiftKey && this._keyStatus.shiftKey) { + this._keyStatus.lastKeyReleased = 'shift'; + } else { + this._keyStatus.lastKeyReleased = undefined; + } + + if (this._keyStatus.lastKeyPressed !== this._keyStatus.lastKeyReleased) { + this._keyStatus.lastKeyPressed = undefined; + } + + this._keyStatus.altKey = e.altKey; + this._keyStatus.ctrlKey = e.ctrlKey; + this._keyStatus.metaKey = e.metaKey; + this._keyStatus.shiftKey = e.shiftKey; + + if (this._keyStatus.lastKeyReleased) { + this._keyStatus.event = e; + this.fire(this._keyStatus); + } + })); + + this._subscriptions.add(domEvent(document.body, 'mousedown', true)(e => { + this._keyStatus.lastKeyPressed = undefined; + })); + + this._subscriptions.add(domEvent(document.body, 'mouseup', true)(e => { + this._keyStatus.lastKeyPressed = undefined; + })); + + this._subscriptions.add(domEvent(document.body, 'mousemove', true)(e => { + if (e.buttons) { + this._keyStatus.lastKeyPressed = undefined; + } + })); + + this._subscriptions.add(domEvent(window, 'blur')(e => { + this.resetKeyStatus(); + })); + } + + get keyStatus(): IModifierKeyStatus { + return this._keyStatus; + } + + get isModifierPressed(): boolean { + return this._keyStatus.altKey || this._keyStatus.ctrlKey || this._keyStatus.metaKey || this._keyStatus.shiftKey; + } + + /** + * Allows to explicitly reset the key status based on more knowledge (#109062) + */ + resetKeyStatus(): void { + this.doResetKeyStatus(); + this.fire(this._keyStatus); + } + + private doResetKeyStatus(): void { + this._keyStatus = { + altKey: false, + shiftKey: false, + ctrlKey: false, + metaKey: false + }; + } + + static getInstance() { + if (!ModifierKeyEmitter.instance) { + ModifierKeyEmitter.instance = new ModifierKeyEmitter(); + } + + return ModifierKeyEmitter.instance; + } + + dispose() { + super.dispose(); + this._subscriptions.dispose(); + } +} diff --git a/src/vs/base/browser/fastDomNode.ts b/src/vs/base/browser/fastDomNode.ts index 12886f27e79..38ed7798266 100644 --- a/src/vs/base/browser/fastDomNode.ts +++ b/src/vs/base/browser/fastDomNode.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as dom from 'vs/base/browser/dom'; - export class FastDomNode { public readonly domNode: T; @@ -28,6 +26,7 @@ export class FastDomNode { private _backgroundColor: string; private _layerHint: boolean; private _contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint'; + private _boxShadow: string; constructor(domNode: T) { this.domNode = domNode; @@ -51,6 +50,7 @@ export class FastDomNode { this._backgroundColor = ''; this._layerHint = false; this._contain = 'none'; + this._boxShadow = ''; } public setMaxWidth(maxWidth: number): void { @@ -174,7 +174,7 @@ export class FastDomNode { } public toggleClassName(className: string, shouldHaveIt?: boolean): void { - dom.toggleClass(this.domNode, className, shouldHaveIt); + this.domNode.classList.toggle(className, shouldHaveIt); this._className = this.domNode.className; } @@ -218,6 +218,14 @@ export class FastDomNode { this.domNode.style.transform = this._layerHint ? 'translate3d(0px, 0px, 0px)' : ''; } + public setBoxShadow(boxShadow: string): void { + if (this._boxShadow === boxShadow) { + return; + } + this._boxShadow = boxShadow; + this.domNode.style.boxShadow = boxShadow; + } + public setContain(contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint'): void { if (this._contain === contain) { return; @@ -234,11 +242,11 @@ export class FastDomNode { this.domNode.removeAttribute(name); } - public appendChild(child: FastDomNode): void { + public appendChild(child: FastDomNode): void { this.domNode.appendChild(child.domNode); } - public removeChild(child: FastDomNode): void { + public removeChild(child: FastDomNode): void { this.domNode.removeChild(child.domNode); } } diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index 328ecaee03d..a25d8b4a1f7 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import * as platform from 'vs/base/common/platform'; import { IframeUtils } from 'vs/base/browser/iframe'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; export interface IStandardMouseMoveEventData { leftButton: boolean; @@ -90,8 +88,8 @@ export class GlobalMouseMoveMonitor implements I this._onStopCallback = onStopCallback; const windowChain = IframeUtils.getSameOriginWindowChain(); - const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove'; - const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup'; + const mouseMove = 'mousemove'; + const mouseUp = 'mouseup'; const listenTo: (Document | ShadowRoot)[] = windowChain.map(element => element.window.document); const shadowRoot = dom.getShadowRoot(initialElement); diff --git a/src/vs/base/browser/iframe.ts b/src/vs/base/browser/iframe.ts index 7868cafbba2..151fd23e25b 100644 --- a/src/vs/base/browser/iframe.ts +++ b/src/vs/base/browser/iframe.ts @@ -14,7 +14,7 @@ export interface IWindowChainElement { /** * The iframe element inside the window.parent corresponding to window */ - iframeElement: HTMLIFrameElement | null; + iframeElement: Element | null; } let hasDifferentOriginAncestorFlag: boolean = false; @@ -29,9 +29,11 @@ function getParentWindowIfSameOrigin(w: Window): Window | null { try { let location = w.location; let parentLocation = w.parent.location; - if (location.protocol !== parentLocation.protocol || location.hostname !== parentLocation.hostname || location.port !== parentLocation.port) { - hasDifferentOriginAncestorFlag = true; - return null; + if (location.origin !== 'null' && parentLocation.origin !== 'null') { + if (location.protocol !== parentLocation.protocol || location.hostname !== parentLocation.hostname || location.port !== parentLocation.port) { + hasDifferentOriginAncestorFlag = true; + return null; + } } } catch (e) { hasDifferentOriginAncestorFlag = true; @@ -41,18 +43,6 @@ function getParentWindowIfSameOrigin(w: Window): Window | null { return w.parent; } -function findIframeElementInParentWindow(parentWindow: Window, childWindow: Window): HTMLIFrameElement | null { - let parentWindowIframes = parentWindow.document.getElementsByTagName('iframe'); - let iframe: HTMLIFrameElement; - for (let i = 0, len = parentWindowIframes.length; i < len; i++) { - iframe = parentWindowIframes[i]; - if (iframe.contentWindow === childWindow) { - return iframe; - } - } - return null; -} - export class IframeUtils { /** @@ -70,7 +60,7 @@ export class IframeUtils { if (parent) { sameOriginWindowChainCache.push({ window: w, - iframeElement: findIframeElementInParentWindow(parent, w) + iframeElement: w.frameElement || null }); } else { sameOriginWindowChainCache.push({ @@ -98,7 +88,7 @@ export class IframeUtils { /** * Returns the position of `childWindow` relative to `ancestorWindow` */ - public static getPositionOfChildWindowRelativeToAncestorWindow(childWindow: Window, ancestorWindow: any) { + public static getPositionOfChildWindowRelativeToAncestorWindow(childWindow: Window, ancestorWindow: Window | null) { if (!ancestorWindow || childWindow === ancestorWindow) { return { @@ -113,6 +103,9 @@ export class IframeUtils { for (const windowChainEl of windowChain) { + top += windowChainEl.window.scrollY; + left += windowChainEl.window.scrollX; + if (windowChainEl.window === ancestorWindow) { break; } diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 03bdffc95ed..83e1ba0ad59 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -145,9 +145,7 @@ let INVERSE_KEY_CODE_MAP: KeyCode[] = new Array(KeyCode.MAX_VALUE); */ define(229, KeyCode.KEY_IN_COMPOSITION); - if (browser.isIE) { - define(91, KeyCode.Meta); - } else if (browser.isFirefox) { + if (browser.isFirefox) { define(59, KeyCode.US_SEMICOLON); define(107, KeyCode.US_EQUAL); define(109, KeyCode.US_MINUS); @@ -207,6 +205,40 @@ const altKeyMod = KeyMod.Alt; const shiftKeyMod = KeyMod.Shift; const metaKeyMod = (platform.isMacintosh ? KeyMod.CtrlCmd : KeyMod.WinCtrl); +export function printKeyboardEvent(e: KeyboardEvent): string { + let modifiers: string[] = []; + if (e.ctrlKey) { + modifiers.push(`ctrl`); + } + if (e.shiftKey) { + modifiers.push(`shift`); + } + if (e.altKey) { + modifiers.push(`alt`); + } + if (e.metaKey) { + modifiers.push(`meta`); + } + return `modifiers: [${modifiers.join(',')}], code: ${e.code}, keyCode: ${e.keyCode}, key: ${e.key}`; +} + +export function printStandardKeyboardEvent(e: StandardKeyboardEvent): string { + let modifiers: string[] = []; + if (e.ctrlKey) { + modifiers.push(`ctrl`); + } + if (e.shiftKey) { + modifiers.push(`shift`); + } + if (e.altKey) { + modifiers.push(`alt`); + } + if (e.metaKey) { + modifiers.push(`meta`); + } + return `modifiers: [${modifiers.join(',')}], code: ${e.code}, keyCode: ${e.keyCode} ('${KeyCodeUtils.toString(e.keyCode)}')`; +} + export class StandardKeyboardEvent implements IKeyboardEvent { readonly _standardKeyboardEventBrand = true; diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 2918133f5e5..46494dc1230 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -9,23 +9,42 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import * as marked from 'vs/base/common/marked/marked'; -import { insane } from 'vs/base/common/insane/insane'; +import { insane, InsaneOptions } from 'vs/base/common/insane/insane'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; -import { renderCodicons, markdownEscapeEscapedCodicons } from 'vs/base/common/codicons'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { markdownEscapeEscapedCodicons } from 'vs/base/common/codicons'; +import { resolvePath } from 'vs/base/common/resources'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { renderCodicons } from 'vs/base/browser/codicons'; +import { Event } from 'vs/base/common/event'; +import { domEvent } from 'vs/base/browser/event'; -export interface MarkdownRenderOptions extends FormattedTextRenderOptions { - codeBlockRenderer?: (modeId: string, value: string) => Promise; - codeBlockRenderCallback?: () => void; +export interface MarkedOptions extends marked.MarkedOptions { + baseUrl?: never; } +export interface MarkdownRenderOptions extends FormattedTextRenderOptions { + codeBlockRenderer?: (modeId: string, value: string) => Promise; + codeBlockRenderCallback?: () => void; + baseUrl?: URI; +} + +const _ttpInsane = window.trustedTypes?.createPolicy('insane', { + createHTML(value, options: InsaneOptions): string { + return insane(value, options); + } +}); + /** - * Create html nodes for the given content element. + * Low-level way create a html element from a markdown string. + * + * **Note** that for most cases you should be using [`MarkdownRenderer`](./src/vs/editor/browser/core/markdownRenderer.ts) + * which comes with support for pretty code block rendering and which uses the default way of handling links. */ -export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}): HTMLElement { +export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}, markedOptions: MarkedOptions = {}): HTMLElement { const element = createElement(options); const _uriMassage = function (part: string): string { @@ -58,12 +77,16 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende return href; // no tranformation performed } if (isDomUri) { - uri = DOM.asDomUri(uri); + // this URI will end up as "src"-attribute of a dom node + // and because of that special rewriting needs to be done + // so that the URI uses a protocol that's understood by + // browsers (like http or https) + return FileAccess.asBrowserUri(uri).toString(true); } if (uri.query) { uri = uri.with({ query: _uriMassage(uri.query) }); } - return uri.toString(true); + return uri.toString(); }; // signal to code-block render that the @@ -78,6 +101,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende if (href) { ({ href, dimensions } = parseHrefAndDimensions(href)); href = _href(href, true); + try { + const hrefAsUri = URI.parse(href); + if (options.baseUrl && hrefAsUri.scheme === Schemas.file) { // absolute or relative local path, or file: uri + href = resolvePath(options.baseUrl, href).toString(); + } + } catch (err) { } + attributes.push(`src="${href}"`); } if (text) { @@ -97,6 +127,12 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende text = removeMarkdownEscapes(text); } href = _href(href, false); + if (options.baseUrl) { + const hasScheme = /^\w[\w\d+.-]*:/.test(href); + if (!hasScheme) { + href = resolvePath(options.baseUrl, href).toString(); + } + } title = removeMarkdownEscapes(title); href = removeMarkdownEscapes(href); if ( @@ -119,7 +155,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende } }; renderer.paragraph = (text): string => { - return `

${markdown.supportThemeIcons ? renderCodicons(text) : text}

`; + if (markdown.supportThemeIcons) { + const elements = renderCodicons(text); + text = elements.map(e => typeof e === 'string' ? e : e.outerHTML).join(''); + } + return `

${text}

`; }; if (options.codeBlockRenderer) { @@ -129,12 +169,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // but update the node with the real result later. const id = defaultGenerator.nextId(); const promise = Promise.all([value, withInnerHTML]).then(values => { - const strValue = values[0]; - const span = element.querySelector(`div[data-code="${id}"]`); + const span = element.querySelector(`div[data-code="${id}"]`); if (span) { - span.innerHTML = strValue; + DOM.reset(span, values[0]); } - }).catch(err => { + }).catch(_err => { // ignore }); @@ -146,10 +185,14 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende }; } - const actionHandler = options.actionHandler; - if (actionHandler) { - actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { - let target: HTMLElement | null = event.target; + if (options.actionHandler) { + options.actionHandler.disposeables.add(Event.any(domEvent(element, 'click'), domEvent(element, 'auxclick'))(e => { + const mouseEvent = new StandardMouseEvent(e); + if (!mouseEvent.leftButton && !mouseEvent.middleButton) { + return; + } + + let target: HTMLElement | null = mouseEvent.target; if (target.tagName !== 'A') { target = target.parentElement; if (!target || target.tagName !== 'A') { @@ -159,45 +202,106 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende try { const href = target.dataset['href']; if (href) { - actionHandler.callback(href, event); + options.actionHandler!.callback(href, mouseEvent); } } catch (err) { onUnexpectedError(err); } finally { - event.preventDefault(); + mouseEvent.preventDefault(); } })); } - const markedOptions: marked.MarkedOptions = { - sanitize: true, - renderer + // Use our own sanitizer so that we can let through only spans. + // Otherwise, we'd be letting all html be rendered. + // If we want to allow markdown permitted tags, then we can delete sanitizer and sanitize. + // We always pass the output through insane after this so that we don't rely on + // marked for sanitization. + markedOptions.sanitizer = (html: string): string => { + const match = markdown.isTrusted ? html.match(/^()|(<\/\s*span>)$/) : undefined; + return match ? html : ''; }; + markedOptions.sanitize = true; + markedOptions.silent = true; - const allowedSchemes = [Schemas.http, Schemas.https, Schemas.mailto, Schemas.data, Schemas.file, Schemas.vscodeRemote, Schemas.vscodeRemoteResource]; - if (markdown.isTrusted) { - allowedSchemes.push(Schemas.command); + markedOptions.renderer = renderer; + + // values that are too long will freeze the UI + let value = markdown.value ?? ''; + if (value.length > 100_000) { + value = `${value.substr(0, 100_000)}…`; + } + // escape theme icons + if (markdown.supportThemeIcons) { + value = markdownEscapeEscapedCodicons(value); } - const renderedMarkdown = marked.parse( - markdown.supportThemeIcons - ? markdownEscapeEscapedCodicons(markdown.value) - : markdown.value, - markedOptions - ); + const renderedMarkdown = marked.parse(value, markedOptions); - element.innerHTML = insane(renderedMarkdown, { - allowedSchemes, - allowedAttributes: { - 'a': ['href', 'name', 'target', 'data-href'], - 'iframe': ['allowfullscreen', 'frameborder', 'src'], - 'img': ['src', 'title', 'alt', 'width', 'height'], - 'div': ['class', 'data-code'], - 'span': ['class'] - } - }); + // sanitize with insane + element.innerHTML = sanitizeRenderedMarkdown(markdown, renderedMarkdown); + // signal that async code blocks can be now be inserted signalInnerHTML!(); return element; } + +function sanitizeRenderedMarkdown( + options: { isTrusted?: boolean }, + renderedMarkdown: string, +): string { + const insaneOptions = getInsaneOptions(options); + if (_ttpInsane) { + return _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string; + } else { + return insane(renderedMarkdown, insaneOptions); + } +} + +function getInsaneOptions(options: { readonly isTrusted?: boolean }): InsaneOptions { + const allowedSchemes = [ + Schemas.http, + Schemas.https, + Schemas.mailto, + Schemas.data, + Schemas.file, + Schemas.vscodeRemote, + Schemas.vscodeRemoteResource, + ]; + + if (options.isTrusted) { + allowedSchemes.push(Schemas.command); + } + + return { + allowedSchemes, + // allowedTags should included everything that markdown renders to. + // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure. + // HTML tags that can result from markdown are from reading https://spec.commonmark.org/0.29/ + // HTML table tags that can result from markdown are from https://github.github.com/gfm/#tables-extension- + allowedTags: ['ul', 'li', 'p', 'code', 'blockquote', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'em', 'pre', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'div', 'del', 'a', 'strong', 'br', 'img', 'span'], + allowedAttributes: { + 'a': ['href', 'name', 'target', 'data-href'], + 'img': ['src', 'title', 'alt', 'width', 'height'], + 'div': ['class', 'data-code'], + 'span': ['class', 'style'], + // https://github.com/microsoft/vscode/issues/95937 + 'th': ['align'], + 'td': ['align'] + }, + filter(token: { tag: string; attrs: { readonly [key: string]: string; }; }): boolean { + if (token.tag === 'span' && options.isTrusted && (Object.keys(token.attrs).length === 1)) { + if (token.attrs['style']) { + return !!token.attrs['style'].match(/^(color\:#[0-9a-fA-F]+;)?(background-color\:#[0-9a-fA-F]+;)?$/); + } else if (token.attrs['class']) { + // The class should match codicon rendering in src\vs\base\common\codicons.ts + return !!token.attrs['class'].match(/^codicon codicon-[a-z\-]+( codicon-animation-[a-z\-]+)?$/); + } + return false; + } + return true; + } + }; +} + diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index 25e73b113d6..a78d4324356 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -80,15 +80,11 @@ export class StandardMouseEvent implements IMouseEvent { } public preventDefault(): void { - if (this.browserEvent.preventDefault) { - this.browserEvent.preventDefault(); - } + this.browserEvent.preventDefault(); } public stopPropagation(): void { - if (this.browserEvent.stopPropagation) { - this.browserEvent.stopPropagation(); - } + this.browserEvent.stopPropagation(); } } @@ -171,7 +167,11 @@ export class StandardWheelEvent { if (ev.deltaMode === ev.DOM_DELTA_LINE) { // the deltas are expressed in lines - this.deltaY = -e.deltaY; + if (browser.isFirefox && !platform.isMacintosh) { + this.deltaY = -e.deltaY / 3; + } else { + this.deltaY = -e.deltaY; + } } else { this.deltaY = -e.deltaY / 40; } @@ -193,7 +193,11 @@ export class StandardWheelEvent { if (ev.deltaMode === ev.DOM_DELTA_LINE) { // the deltas are expressed in lines - this.deltaX = -e.deltaX; + if (browser.isFirefox && !platform.isMacintosh) { + this.deltaX = -e.deltaX / 3; + } else { + this.deltaX = -e.deltaX; + } } else { this.deltaX = -e.deltaX / 40; } @@ -208,17 +212,13 @@ export class StandardWheelEvent { public preventDefault(): void { if (this.browserEvent) { - if (this.browserEvent.preventDefault) { - this.browserEvent.preventDefault(); - } + this.browserEvent.preventDefault(); } } public stopPropagation(): void { if (this.browserEvent) { - if (this.browserEvent.stopPropagation) { - this.browserEvent.stopPropagation(); - } + this.browserEvent.stopPropagation(); } } } diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 9956bc9112b..7166c4ec3a9 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -90,9 +90,9 @@ export class Gesture extends Disposable { this.targets = []; this.ignoreTargets = []; this._lastSetTapCountTime = 0; - this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e))); + this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e), { passive: false })); this._register(DomUtils.addDisposableListener(document, 'touchend', (e: TouchEvent) => this.onTouchEnd(e))); - this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e))); + this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e), { passive: false })); } public static addTarget(element: HTMLElement): IDisposable { @@ -131,7 +131,9 @@ export class Gesture extends Disposable { @memoize private static isTouchDevice(): boolean { - return 'ontouchstart' in window as any || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0; + // `'ontouchstart' in window` always evaluates to true with typescript's modern typings. This causes `window` to be + // `never` later in `window.navigator`. That's why we need the explicit `window as Window` cast + return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0; } public dispose(): void { @@ -247,7 +249,7 @@ export class Gesture extends Disposable { } private newGestureEvent(type: string, initialTarget?: EventTarget): GestureEvent { - let event = (document.createEvent('CustomEvent')); + let event = document.createEvent('CustomEvent') as unknown as GestureEvent; event.initEvent(type, false, true); event.initialTarget = initialTarget; event.tapCount = 0; diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts new file mode 100644 index 00000000000..0968da8a678 --- /dev/null +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./actionbar'; +import * as platform from 'vs/base/common/platform'; +import * as nls from 'vs/nls'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { SelectBox, ISelectOptionItem, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; +import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, Separator, IActionViewItem } from 'vs/base/common/actions'; +import * as types from 'vs/base/common/types'; +import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { DataTransfers } from 'vs/base/browser/dnd'; +import { isFirefox } from 'vs/base/browser/browser'; +import { $, addDisposableListener, append, EventHelper, EventLike, EventType, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; + +export interface IBaseActionViewItemOptions { + draggable?: boolean; + isMenu?: boolean; + useEventAsContext?: boolean; +} + +export class BaseActionViewItem extends Disposable implements IActionViewItem { + + element: HTMLElement | undefined; + + _context: any; + _action: IAction; + + private _actionRunner: IActionRunner | undefined; + + constructor(context: any, action: IAction, protected options: IBaseActionViewItemOptions = {}) { + super(); + + this._context = context || this; + this._action = action; + + if (action instanceof Action) { + this._register(action.onDidChange(event => { + if (!this.element) { + // we have not been rendered yet, so there + // is no point in updating the UI + return; + } + + this.handleActionChangeEvent(event); + })); + } + } + + private handleActionChangeEvent(event: IActionChangeEvent): void { + if (event.enabled !== undefined) { + this.updateEnabled(); + } + + if (event.checked !== undefined) { + this.updateChecked(); + } + + if (event.class !== undefined) { + this.updateClass(); + } + + if (event.label !== undefined) { + this.updateLabel(); + this.updateTooltip(); + } + + if (event.tooltip !== undefined) { + this.updateTooltip(); + } + } + + get actionRunner(): IActionRunner { + if (!this._actionRunner) { + this._actionRunner = this._register(new ActionRunner()); + } + + return this._actionRunner; + } + + set actionRunner(actionRunner: IActionRunner) { + this._actionRunner = actionRunner; + } + + getAction(): IAction { + return this._action; + } + + isEnabled(): boolean { + return this._action.enabled; + } + + setActionContext(newContext: unknown): void { + this._context = newContext; + } + + render(container: HTMLElement): void { + const element = this.element = container; + this._register(Gesture.addTarget(container)); + + const enableDragging = this.options && this.options.draggable; + if (enableDragging) { + container.draggable = true; + + if (isFirefox) { + // Firefox: requires to set a text data transfer to get going + this._register(addDisposableListener(container, EventType.DRAG_START, e => e.dataTransfer?.setData(DataTransfers.TEXT, this._action.label))); + } + } + + this._register(addDisposableListener(element, TouchEventType.Tap, e => this.onClick(e))); + + this._register(addDisposableListener(element, EventType.MOUSE_DOWN, e => { + if (!enableDragging) { + EventHelper.stop(e, true); // do not run when dragging is on because that would disable it + } + + if (this._action.enabled && e.button === 0) { + element.classList.add('active'); + } + })); + + if (platform.isMacintosh) { + // macOS: allow to trigger the button when holding Ctrl+key and pressing the + // main mouse button. This is for scenarios where e.g. some interaction forces + // the Ctrl+key to be pressed and hold but the user still wants to interact + // with the actions (for example quick access in quick navigation mode). + this._register(addDisposableListener(element, EventType.CONTEXT_MENU, e => { + if (e.button === 0 && e.ctrlKey === true) { + this.onClick(e); + } + })); + } + + this._register(addDisposableListener(element, EventType.CLICK, e => { + EventHelper.stop(e, true); + + // menus do not use the click event + if (!(this.options && this.options.isMenu)) { + platform.setImmediate(() => this.onClick(e)); + } + })); + + this._register(addDisposableListener(element, EventType.DBLCLICK, e => { + EventHelper.stop(e, true); + })); + + [EventType.MOUSE_UP, EventType.MOUSE_OUT].forEach(event => { + this._register(addDisposableListener(element, event, e => { + EventHelper.stop(e); + element.classList.remove('active'); + })); + }); + } + + onClick(event: EventLike): void { + EventHelper.stop(event, true); + + const context = types.isUndefinedOrNull(this._context) ? this.options?.useEventAsContext ? event : undefined : this._context; + this.actionRunner.run(this._action, context); + } + + focus(): void { + if (this.element) { + this.element.focus(); + this.element.classList.add('focused'); + } + } + + blur(): void { + if (this.element) { + this.element.blur(); + this.element.classList.remove('focused'); + } + } + + protected updateEnabled(): void { + // implement in subclass + } + + protected updateLabel(): void { + // implement in subclass + } + + protected updateTooltip(): void { + // implement in subclass + } + + protected updateClass(): void { + // implement in subclass + } + + protected updateChecked(): void { + // implement in subclass + } + + dispose(): void { + if (this.element) { + this.element.remove(); + this.element = undefined; + } + + super.dispose(); + } +} + +export interface IActionViewItemOptions extends IBaseActionViewItemOptions { + icon?: boolean; + label?: boolean; + keybinding?: string | null; +} + +export class ActionViewItem extends BaseActionViewItem { + + protected label: HTMLElement | undefined; + protected options: IActionViewItemOptions; + + private cssClass?: string; + + constructor(context: unknown, action: IAction, options: IActionViewItemOptions = {}) { + super(context, action, options); + + this.options = options; + this.options.icon = options.icon !== undefined ? options.icon : false; + this.options.label = options.label !== undefined ? options.label : true; + this.cssClass = ''; + } + + render(container: HTMLElement): void { + super.render(container); + + if (this.element) { + this.label = append(this.element, $('a.action-label')); + } + + if (this.label) { + if (this._action.id === Separator.ID) { + this.label.setAttribute('role', 'presentation'); // A separator is a presentation item + } else { + if (this.options.isMenu) { + this.label.setAttribute('role', 'menuitem'); + } else { + this.label.setAttribute('role', 'button'); + } + } + } + + if (this.options.label && this.options.keybinding && this.element) { + append(this.element, $('span.keybinding')).textContent = this.options.keybinding; + } + + this.updateClass(); + this.updateLabel(); + this.updateTooltip(); + this.updateEnabled(); + this.updateChecked(); + } + + focus(): void { + super.focus(); + + if (this.label) { + this.label.focus(); + } + } + + updateLabel(): void { + if (this.options.label && this.label) { + this.label.textContent = this.getAction().label; + } + } + + updateTooltip(): void { + let title: string | null = null; + + if (this.getAction().tooltip) { + title = this.getAction().tooltip; + + } else if (!this.options.label && this.getAction().label && this.options.icon) { + title = this.getAction().label; + + if (this.options.keybinding) { + title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding); + } + } + + if (title && this.label) { + this.label.title = title; + } + } + + updateClass(): void { + if (this.cssClass && this.label) { + this.label.classList.remove(...this.cssClass.split(' ')); + } + + if (this.options.icon) { + this.cssClass = this.getAction().class; + + if (this.label) { + this.label.classList.add('codicon'); + if (this.cssClass) { + this.label.classList.add(...this.cssClass.split(' ')); + } + } + + this.updateEnabled(); + } else { + if (this.label) { + this.label.classList.remove('codicon'); + } + } + } + + updateEnabled(): void { + if (this.getAction().enabled) { + if (this.label) { + this.label.removeAttribute('aria-disabled'); + this.label.classList.remove('disabled'); + this.label.tabIndex = 0; + } + + if (this.element) { + this.element.classList.remove('disabled'); + } + } else { + if (this.label) { + this.label.setAttribute('aria-disabled', 'true'); + this.label.classList.add('disabled'); + removeTabIndexAndUpdateFocus(this.label); + } + + if (this.element) { + this.element.classList.add('disabled'); + } + } + } + + updateChecked(): void { + if (this.label) { + if (this.getAction().checked) { + this.label.classList.add('checked'); + } else { + this.label.classList.remove('checked'); + } + } + } +} + +export class SelectActionViewItem extends BaseActionViewItem { + protected selectBox: SelectBox; + + constructor(ctx: unknown, action: IAction, options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { + super(ctx, action); + + this.selectBox = new SelectBox(options, selected, contextViewProvider, undefined, selectBoxOptions); + + this._register(this.selectBox); + this.registerListeners(); + } + + setOptions(options: ISelectOptionItem[], selected?: number): void { + this.selectBox.setOptions(options, selected); + } + + select(index: number): void { + this.selectBox.select(index); + } + + private registerListeners(): void { + this._register(this.selectBox.onDidSelect(e => { + this.actionRunner.run(this._action, this.getActionContext(e.selected, e.index)); + })); + } + + protected getActionContext(option: string, index: number) { + return option; + } + + focus(): void { + if (this.selectBox) { + this.selectBox.focus(); + } + } + + blur(): void { + if (this.selectBox) { + this.selectBox.blur(); + } + } + + render(container: HTMLElement): void { + this.selectBox.render(container); + } +} diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css index 623ff4d7421..a88787fd0fa 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.css +++ b/src/vs/base/browser/ui/actionbar/actionbar.css @@ -5,7 +5,6 @@ .monaco-action-bar { text-align: right; - overflow: hidden; white-space: nowrap; } @@ -45,6 +44,11 @@ display: inline-block; } +.monaco-action-bar .action-item .codicon { + display: flex; + align-items: center; +} + .monaco-action-bar .action-label { font-size: 11px; margin-right: 4px; @@ -92,3 +96,15 @@ justify-content: center; margin-right: 10px; } + +.monaco-action-bar .action-item.action-dropdown-item { + display: flex; +} + +.monaco-action-bar .action-item.action-dropdown-item > .action-label { + margin-right: 1px; +} + +.monaco-action-bar .action-item.action-dropdown-item > .monaco-dropdown { + margin-right: 4px; +} diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 28e9c4b55ed..9d206dabe5d 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -4,380 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./actionbar'; -import * as platform from 'vs/base/common/platform'; -import * as nls from 'vs/nls'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { SelectBox, ISelectOptionItem, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; -import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import * as types from 'vs/base/common/types'; -import { EventType, Gesture } from 'vs/base/browser/touch'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; -import { Event, Emitter } from 'vs/base/common/event'; -import { DataTransfers } from 'vs/base/browser/dnd'; -import { isFirefox } from 'vs/base/browser/browser'; - -export interface IActionViewItem extends IDisposable { - actionRunner: IActionRunner; - setActionContext(context: any): void; - render(element: HTMLElement): void; - isEnabled(): boolean; - focus(fromRight?: boolean): void; - blur(): void; -} - -export interface IBaseActionViewItemOptions { - draggable?: boolean; - isMenu?: boolean; -} - -export class BaseActionViewItem extends Disposable implements IActionViewItem { - - element: HTMLElement | undefined; - - _context: any; - _action: IAction; - - private _actionRunner: IActionRunner | undefined; - - constructor(context: any, action: IAction, protected options?: IBaseActionViewItemOptions) { - super(); - - this._context = context || this; - this._action = action; - - if (action instanceof Action) { - this._register(action.onDidChange(event => { - if (!this.element) { - // we have not been rendered yet, so there - // is no point in updating the UI - return; - } - - this.handleActionChangeEvent(event); - })); - } - } - - private handleActionChangeEvent(event: IActionChangeEvent): void { - if (event.enabled !== undefined) { - this.updateEnabled(); - } - - if (event.checked !== undefined) { - this.updateChecked(); - } - - if (event.class !== undefined) { - this.updateClass(); - } - - if (event.label !== undefined) { - this.updateLabel(); - this.updateTooltip(); - } - - if (event.tooltip !== undefined) { - this.updateTooltip(); - } - } - - get actionRunner(): IActionRunner { - if (!this._actionRunner) { - this._actionRunner = this._register(new ActionRunner()); - } - - return this._actionRunner; - } - - set actionRunner(actionRunner: IActionRunner) { - this._actionRunner = actionRunner; - } - - getAction(): IAction { - return this._action; - } - - isEnabled(): boolean { - return this._action.enabled; - } - - setActionContext(newContext: any): void { - this._context = newContext; - } - - render(container: HTMLElement): void { - const element = this.element = container; - this._register(Gesture.addTarget(container)); - - const enableDragging = this.options && this.options.draggable; - if (enableDragging) { - container.draggable = true; - - if (isFirefox) { - // Firefox: requires to set a text data transfer to get going - this._register(DOM.addDisposableListener(container, DOM.EventType.DRAG_START, e => e.dataTransfer?.setData(DataTransfers.TEXT, this._action.label))); - } - } - - this._register(DOM.addDisposableListener(element, EventType.Tap, e => this.onClick(e))); - - this._register(DOM.addDisposableListener(element, DOM.EventType.MOUSE_DOWN, e => { - if (!enableDragging) { - DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it - } - - if (this._action.enabled && e.button === 0) { - DOM.addClass(element, 'active'); - } - })); - - this._register(DOM.addDisposableListener(element, DOM.EventType.CLICK, e => { - DOM.EventHelper.stop(e, true); - // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard - // > Writing to the clipboard - // > You can use the "cut" and "copy" commands without any special - // permission if you are using them in a short-lived event handler - // for a user action (for example, a click handler). - - // => to get the Copy and Paste context menu actions working on Firefox, - // there should be no timeout here - if (this.options && this.options.isMenu) { - this.onClick(e); - } else { - platform.setImmediate(() => this.onClick(e)); - } - })); - - this._register(DOM.addDisposableListener(element, DOM.EventType.DBLCLICK, e => { - DOM.EventHelper.stop(e, true); - })); - - [DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => { - this._register(DOM.addDisposableListener(element, event, e => { - DOM.EventHelper.stop(e); - DOM.removeClass(element, 'active'); - })); - }); - } - - onClick(event: DOM.EventLike): void { - DOM.EventHelper.stop(event, true); - - let context: any; - if (types.isUndefinedOrNull(this._context)) { - context = event; - } else { - context = this._context; - - if (types.isObject(context)) { - context.event = event; - } - } - - this.actionRunner.run(this._action, context); - } - - focus(): void { - if (this.element) { - this.element.focus(); - DOM.addClass(this.element, 'focused'); - } - } - - blur(): void { - if (this.element) { - this.element.blur(); - DOM.removeClass(this.element, 'focused'); - } - } - - protected updateEnabled(): void { - // implement in subclass - } - - protected updateLabel(): void { - // implement in subclass - } - - protected updateTooltip(): void { - // implement in subclass - } - - protected updateClass(): void { - // implement in subclass - } - - protected updateChecked(): void { - // implement in subclass - } - - dispose(): void { - if (this.element) { - DOM.removeNode(this.element); - this.element = undefined; - } - - super.dispose(); - } -} - -export class Separator extends Action { - - static readonly ID = 'vs.actions.separator'; - - constructor(label?: string) { - super(Separator.ID, label, label ? 'separator text' : 'separator'); - this.checked = false; - this.enabled = false; - } -} - -export interface IActionViewItemOptions extends IBaseActionViewItemOptions { - icon?: boolean; - label?: boolean; - keybinding?: string | null; -} - -export class ActionViewItem extends BaseActionViewItem { - - protected label: HTMLElement | undefined; - protected options: IActionViewItemOptions; - - private cssClass?: string; - - constructor(context: any, action: IAction, options: IActionViewItemOptions = {}) { - super(context, action, options); - - this.options = options; - this.options.icon = options.icon !== undefined ? options.icon : false; - this.options.label = options.label !== undefined ? options.label : true; - this.cssClass = ''; - } - - render(container: HTMLElement): void { - super.render(container); - - if (this.element) { - this.label = DOM.append(this.element, DOM.$('a.action-label')); - } - - - if (this.label) { - if (this._action.id === Separator.ID) { - this.label.setAttribute('role', 'presentation'); // A separator is a presentation item - } else { - if (this.options.isMenu) { - this.label.setAttribute('role', 'menuitem'); - } else { - this.label.setAttribute('role', 'button'); - } - } - } - - if (this.options.label && this.options.keybinding && this.element) { - DOM.append(this.element, DOM.$('span.keybinding')).textContent = this.options.keybinding; - } - - this.updateClass(); - this.updateLabel(); - this.updateTooltip(); - this.updateEnabled(); - this.updateChecked(); - } - - focus(): void { - super.focus(); - - if (this.label) { - this.label.focus(); - } - } - - updateLabel(): void { - if (this.options.label && this.label) { - this.label.textContent = this.getAction().label; - } - } - - updateTooltip(): void { - let title: string | null = null; - - if (this.getAction().tooltip) { - title = this.getAction().tooltip; - - } else if (!this.options.label && this.getAction().label && this.options.icon) { - title = this.getAction().label; - - if (this.options.keybinding) { - title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding); - } - } - - if (title && this.label) { - this.label.title = title; - } - } - - updateClass(): void { - if (this.cssClass && this.label) { - DOM.removeClasses(this.label, this.cssClass); - } - - if (this.options.icon) { - this.cssClass = this.getAction().class; - - if (this.label) { - DOM.addClass(this.label, 'codicon'); - if (this.cssClass) { - DOM.addClasses(this.label, this.cssClass); - } - } - - this.updateEnabled(); - } else { - if (this.label) { - DOM.removeClass(this.label, 'codicon'); - } - } - } - - updateEnabled(): void { - if (this.getAction().enabled) { - if (this.label) { - this.label.removeAttribute('aria-disabled'); - DOM.removeClass(this.label, 'disabled'); - this.label.tabIndex = 0; - } - - if (this.element) { - DOM.removeClass(this.element, 'disabled'); - } - } else { - if (this.label) { - this.label.setAttribute('aria-disabled', 'true'); - DOM.addClass(this.label, 'disabled'); - DOM.removeTabIndexAndUpdateFocus(this.label); - } - - if (this.element) { - DOM.addClass(this.element, 'disabled'); - } - } - } - - updateChecked(): void { - if (this.label) { - if (this.getAction().checked) { - DOM.addClass(this.label, 'checked'); - } else { - DOM.removeClass(this.label, 'checked'); - } - } - } -} +import { Emitter } from 'vs/base/common/event'; +import { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export const enum ActionsOrientation { HORIZONTAL, @@ -387,43 +21,39 @@ export const enum ActionsOrientation { } export interface ActionTrigger { - keys: KeyCode[]; + keys?: KeyCode[]; keyDown: boolean; } -export interface IActionViewItemProvider { - (action: IAction): IActionViewItem | undefined; -} - export interface IActionBarOptions { - orientation?: ActionsOrientation; - context?: any; - actionViewItemProvider?: IActionViewItemProvider; - actionRunner?: IActionRunner; - ariaLabel?: string; - animated?: boolean; - triggerKeys?: ActionTrigger; + readonly orientation?: ActionsOrientation; + readonly context?: any; + readonly actionViewItemProvider?: IActionViewItemProvider; + readonly actionRunner?: IActionRunner; + readonly ariaLabel?: string; + readonly animated?: boolean; + readonly triggerKeys?: ActionTrigger; + readonly allowContextMenu?: boolean; + readonly preventLoopNavigation?: boolean; + readonly ignoreOrientationForPreviousAndNextKey?: boolean; } -const defaultOptions: IActionBarOptions = { - orientation: ActionsOrientation.HORIZONTAL, - context: null, - triggerKeys: { - keys: [KeyCode.Enter, KeyCode.Space], - keyDown: false - } -}; - export interface IActionOptions extends IActionViewItemOptions { index?: number; } export class ActionBar extends Disposable implements IActionRunner { - options: IActionBarOptions; + private readonly options: IActionBarOptions; private _actionRunner: IActionRunner; - private _context: any; + private _context: unknown; + private readonly _orientation: ActionsOrientation; + private readonly _triggerKeys: { + keys: KeyCode[]; + keyDown: boolean; + }; + private _actionIds: string[]; // View Items viewItems: IActionViewItem[]; @@ -435,26 +65,28 @@ export class ActionBar extends Disposable implements IActionRunner { protected actionsList: HTMLElement; private _onDidBlur = this._register(new Emitter()); - readonly onDidBlur: Event = this._onDidBlur.event; + readonly onDidBlur = this._onDidBlur.event; - private _onDidCancel = this._register(new Emitter()); - readonly onDidCancel: Event = this._onDidCancel.event; + private _onDidCancel = this._register(new Emitter({ onFirstListenerAdd: () => this.cancelHasListener = true })); + readonly onDidCancel = this._onDidCancel.event; + private cancelHasListener = false; private _onDidRun = this._register(new Emitter()); - readonly onDidRun: Event = this._onDidRun.event; + readonly onDidRun = this._onDidRun.event; private _onDidBeforeRun = this._register(new Emitter()); - readonly onDidBeforeRun: Event = this._onDidBeforeRun.event; + readonly onDidBeforeRun = this._onDidBeforeRun.event; - constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) { + constructor(container: HTMLElement, options: IActionBarOptions = {}) { super(); this.options = options; - this._context = options.context; - - if (!this.options.triggerKeys) { - this.options.triggerKeys = defaultOptions.triggerKeys; - } + this._context = options.context ?? null; + this._orientation = this.options.orientation ?? ActionsOrientation.HORIZONTAL; + this._triggerKeys = { + keyDown: this.options.triggerKeys?.keyDown ?? false, + keys: this.options.triggerKeys?.keys ?? [KeyCode.Enter, KeyCode.Space] + }; if (this.options.actionRunner) { this._actionRunner = this.options.actionRunner; @@ -466,6 +98,7 @@ export class ActionBar extends Disposable implements IActionRunner { this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); + this._actionIds = []; this.viewItems = []; this.focusedItem = undefined; @@ -473,30 +106,30 @@ export class ActionBar extends Disposable implements IActionRunner { this.domNode.className = 'monaco-action-bar'; if (options.animated !== false) { - DOM.addClass(this.domNode, 'animated'); + this.domNode.classList.add('animated'); } - let previousKey: KeyCode; - let nextKey: KeyCode; + let previousKeys: KeyCode[]; + let nextKeys: KeyCode[]; - switch (this.options.orientation) { + switch (this._orientation) { case ActionsOrientation.HORIZONTAL: - previousKey = KeyCode.LeftArrow; - nextKey = KeyCode.RightArrow; + previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow]; + nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow]; break; case ActionsOrientation.HORIZONTAL_REVERSE: - previousKey = KeyCode.RightArrow; - nextKey = KeyCode.LeftArrow; + previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow]; + nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow]; this.domNode.className += ' reverse'; break; case ActionsOrientation.VERTICAL: - previousKey = KeyCode.UpArrow; - nextKey = KeyCode.DownArrow; + previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow]; + nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow]; this.domNode.className += ' vertical'; break; case ActionsOrientation.VERTICAL_REVERSE: - previousKey = KeyCode.DownArrow; - nextKey = KeyCode.UpArrow; + previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow]; + nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow]; this.domNode.className += ' vertical reverse'; break; } @@ -505,15 +138,15 @@ export class ActionBar extends Disposable implements IActionRunner { const event = new StandardKeyboardEvent(e); let eventHandled = true; - if (event.equals(previousKey)) { - this.focusPrevious(); - } else if (event.equals(nextKey)) { - this.focusNext(); - } else if (event.equals(KeyCode.Escape)) { - this.cancel(); + if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) { + eventHandled = this.focusPrevious(); + } else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) { + eventHandled = this.focusNext(); + } else if (event.equals(KeyCode.Escape) && this.cancelHasListener) { + this._onDidCancel.fire(); } else if (this.isTriggerKeyEvent(event)) { // Staying out of the else branch even if not triggered - if (this.options.triggerKeys && this.options.triggerKeys.keyDown) { + if (this._triggerKeys.keyDown) { this.doTrigger(event); } } else { @@ -531,7 +164,7 @@ export class ActionBar extends Disposable implements IActionRunner { // Run action on Enter/Space if (this.isTriggerKeyEvent(event)) { - if (this.options.triggerKeys && !this.options.triggerKeys.keyDown) { + if (!this._triggerKeys.keyDown) { this.doTrigger(event); } @@ -547,7 +180,7 @@ export class ActionBar extends Disposable implements IActionRunner { this.focusTracker = this._register(DOM.trackFocus(this.domNode)); this._register(this.focusTracker.onDidBlur(() => { - if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) { + if (DOM.getActiveElement() === this.domNode || !DOM.isAncestor(DOM.getActiveElement(), this.domNode)) { this._onDidBlur.fire(); this.focusedItem = undefined; } @@ -578,11 +211,9 @@ export class ActionBar extends Disposable implements IActionRunner { private isTriggerKeyEvent(event: StandardKeyboardEvent): boolean { let ret = false; - if (this.options.triggerKeys) { - this.options.triggerKeys.keys.forEach(keyCode => { - ret = ret || event.equals(keyCode); - }); - } + this._triggerKeys.keys.forEach(keyCode => { + ret = ret || event.equals(keyCode); + }); return ret; } @@ -590,7 +221,7 @@ export class ActionBar extends Disposable implements IActionRunner { private updateFocusedItem(): void { for (let i = 0; i < this.actionsList.children.length; i++) { const elem = this.actionsList.children[i]; - if (DOM.isAncestor(document.activeElement, elem)) { + if (DOM.isAncestor(DOM.getActiveElement(), elem)) { this.focusedItem = i; break; } @@ -621,6 +252,10 @@ export class ActionBar extends Disposable implements IActionRunner { return this.domNode; } + hasAction(action: IAction): boolean { + return this._actionIds.includes(action.id); + } + push(arg: IAction | ReadonlyArray, options: IActionOptions = {}): void { const actions: ReadonlyArray = Array.isArray(arg) ? arg : [arg]; @@ -632,10 +267,11 @@ export class ActionBar extends Disposable implements IActionRunner { actionViewItemElement.setAttribute('role', 'presentation'); // Prevent native context menu on actions - this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { - e.preventDefault(); - e.stopPropagation(); - })); + if (!this.options.allowContextMenu) { + this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { + DOM.EventHelper.stop(e, true); + })); + } let item: IActionViewItem | undefined; @@ -654,12 +290,18 @@ export class ActionBar extends Disposable implements IActionRunner { if (index === null || index < 0 || index >= this.actionsList.children.length) { this.actionsList.appendChild(actionViewItemElement); this.viewItems.push(item); + this._actionIds.push(action.id); } else { this.actionsList.insertBefore(actionViewItemElement, this.actionsList.children[index]); this.viewItems.splice(index, 0, item); + this._actionIds.splice(index, 0, action.id); index++; } }); + if (this.focusedItem) { + // After a clear actions might be re-added to simply toggle some actions. We should preserve focus #97128 + this.focus(this.focusedItem); + } } getWidth(index: number): number { @@ -688,11 +330,14 @@ export class ActionBar extends Disposable implements IActionRunner { if (index >= 0 && index < this.viewItems.length) { this.actionsList.removeChild(this.actionsList.childNodes[index]); dispose(this.viewItems.splice(index, 1)); + this._actionIds.splice(index, 1); } } clear(): void { - this.viewItems = dispose(this.viewItems); + dispose(this.viewItems); + this.viewItems = []; + this._actionIds = []; DOM.clearNode(this.actionsList); } @@ -718,9 +363,10 @@ export class ActionBar extends Disposable implements IActionRunner { } if (selectFirst && typeof this.focusedItem === 'undefined') { + const firstEnabled = this.viewItems.findIndex(item => item.isEnabled()); // Focus the first enabled item - this.focusedItem = this.viewItems.length - 1; - this.focusNext(); + this.focusedItem = firstEnabled === -1 ? undefined : firstEnabled; + this.updateFocus(); } else { if (index !== undefined) { this.focusedItem = index; @@ -730,7 +376,7 @@ export class ActionBar extends Disposable implements IActionRunner { } } - protected focusNext(): void { + protected focusNext(): boolean { if (typeof this.focusedItem === 'undefined') { this.focusedItem = this.viewItems.length - 1; } @@ -739,6 +385,11 @@ export class ActionBar extends Disposable implements IActionRunner { let item: IActionViewItem; do { + if (this.options.preventLoopNavigation && this.focusedItem + 1 >= this.viewItems.length) { + this.focusedItem = startIndex; + return false; + } + this.focusedItem = (this.focusedItem + 1) % this.viewItems.length; item = this.viewItems[this.focusedItem]; } while (this.focusedItem !== startIndex && !item.isEnabled()); @@ -748,9 +399,10 @@ export class ActionBar extends Disposable implements IActionRunner { } this.updateFocus(); + return true; } - protected focusPrevious(): void { + protected focusPrevious(): boolean { if (typeof this.focusedItem === 'undefined') { this.focusedItem = 0; } @@ -762,6 +414,11 @@ export class ActionBar extends Disposable implements IActionRunner { this.focusedItem = this.focusedItem - 1; if (this.focusedItem < 0) { + if (this.options.preventLoopNavigation) { + this.focusedItem = startIndex; + return false; + } + this.focusedItem = this.viewItems.length - 1; } @@ -773,6 +430,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this.updateFocus(true); + return true; } protected updateFocus(fromRight?: boolean, preventScroll?: boolean): void { @@ -813,15 +471,7 @@ export class ActionBar extends Disposable implements IActionRunner { } } - private cancel(): void { - if (document.activeElement instanceof HTMLElement) { - document.activeElement.blur(); // remove focus from focused action - } - - this._onDidCancel.fire(); - } - - run(action: IAction, context?: any): Promise { + run(action: IAction, context?: unknown): Promise { return this._actionRunner.run(action, context); } @@ -829,55 +479,58 @@ export class ActionBar extends Disposable implements IActionRunner { dispose(this.viewItems); this.viewItems = []; - DOM.removeNode(this.getContainer()); + this._actionIds = []; + + this.getContainer().remove(); super.dispose(); } } -export class SelectActionViewItem extends BaseActionViewItem { - protected selectBox: SelectBox; - - constructor(ctx: any, action: IAction, options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { - super(ctx, action); - - this.selectBox = new SelectBox(options, selected, contextViewProvider, undefined, selectBoxOptions); - - this._register(this.selectBox); - this.registerListeners(); +export function prepareActions(actions: IAction[]): IAction[] { + if (!actions.length) { + return actions; } - setOptions(options: ISelectOptionItem[], selected?: number): void { - this.selectBox.setOptions(options, selected); + // Clean up leading separators + let firstIndexOfAction = -1; + for (let i = 0; i < actions.length; i++) { + if (actions[i].id === Separator.ID) { + continue; + } + + firstIndexOfAction = i; + break; } - select(index: number): void { - this.selectBox.select(index); + if (firstIndexOfAction === -1) { + return []; } - private registerListeners(): void { - this._register(this.selectBox.onDidSelect(e => { - this.actionRunner.run(this._action, this.getActionContext(e.selected, e.index)); - })); - } + actions = actions.slice(firstIndexOfAction); - protected getActionContext(option: string, index: number) { - return option; - } - - focus(): void { - if (this.selectBox) { - this.selectBox.focus(); + // Clean up trailing separators + for (let h = actions.length - 1; h >= 0; h--) { + const isSeparator = actions[h].id === Separator.ID; + if (isSeparator) { + actions.splice(h, 1); + } else { + break; } } - blur(): void { - if (this.selectBox) { - this.selectBox.blur(); + // Clean up separator duplicates + let foundAction = false; + for (let k = actions.length - 1; k >= 0; k--) { + const isSeparator = actions[k].id === Separator.ID; + if (isSeparator && !foundAction) { + actions.splice(k, 1); + } else if (!isSeparator) { + foundAction = true; + } else if (isSeparator) { + foundAction = false; } } - render(container: HTMLElement): void { - this.selectBox.render(container); - } + return actions; } diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts index fc71827eafa..641e5f339a3 100644 --- a/src/vs/base/browser/ui/aria/aria.ts +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -4,80 +4,92 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./aria'; -import * as nls from 'vs/nls'; import { isMacintosh } from 'vs/base/common/platform'; import * as dom from 'vs/base/browser/dom'; +// Use a max length since we are inserting the whole msg in the DOM and that can cause browsers to freeze for long messages #94233 +const MAX_MESSAGE_LENGTH = 20000; let ariaContainer: HTMLElement; let alertContainer: HTMLElement; +let alertContainer2: HTMLElement; let statusContainer: HTMLElement; +let statusContainer2: HTMLElement; export function setARIAContainer(parent: HTMLElement) { ariaContainer = document.createElement('div'); ariaContainer.className = 'monaco-aria-container'; - alertContainer = document.createElement('div'); - alertContainer.className = 'monaco-alert'; - alertContainer.setAttribute('role', 'alert'); - alertContainer.setAttribute('aria-atomic', 'true'); - ariaContainer.appendChild(alertContainer); + const createAlertContainer = () => { + const element = document.createElement('div'); + element.className = 'monaco-alert'; + element.setAttribute('role', 'alert'); + element.setAttribute('aria-atomic', 'true'); + ariaContainer.appendChild(element); + return element; + }; + alertContainer = createAlertContainer(); + alertContainer2 = createAlertContainer(); - statusContainer = document.createElement('div'); - statusContainer.className = 'monaco-status'; - statusContainer.setAttribute('role', 'status'); - statusContainer.setAttribute('aria-atomic', 'true'); - ariaContainer.appendChild(statusContainer); + const createStatusContainer = () => { + const element = document.createElement('div'); + element.className = 'monaco-status'; + element.setAttribute('role', 'complementary'); + element.setAttribute('aria-live', 'polite'); + element.setAttribute('aria-atomic', 'true'); + ariaContainer.appendChild(element); + return element; + }; + statusContainer = createStatusContainer(); + statusContainer2 = createStatusContainer(); parent.appendChild(ariaContainer); } - /** * Given the provided message, will make sure that it is read as alert to screen readers. */ -export function alert(msg: string, disableRepeat?: boolean): void { - insertMessage(alertContainer, msg, disableRepeat); +export function alert(msg: string): void { + if (!ariaContainer) { + return; + } + + // Use alternate containers such that duplicated messages get read out by screen readers #99466 + if (alertContainer.textContent !== msg) { + dom.clearNode(alertContainer2); + insertMessage(alertContainer, msg); + } else { + dom.clearNode(alertContainer); + insertMessage(alertContainer2, msg); + } } /** * Given the provided message, will make sure that it is read as status to screen readers. */ -export function status(msg: string, disableRepeat?: boolean): void { - if (isMacintosh) { - alert(msg, disableRepeat); // VoiceOver does not seem to support status role - } else { - insertMessage(statusContainer, msg, disableRepeat); - } -} - -let repeatedTimes = 0; -let prevText: string | undefined = undefined; -function insertMessage(target: HTMLElement, msg: string, disableRepeat?: boolean): void { +export function status(msg: string): void { if (!ariaContainer) { return; } - // If the same message should be inserted that is already present, a screen reader would - // not announce this message because it matches the previous one. As a workaround, we - // alter the message with the number of occurences unless this is explicitly disabled - // via the disableRepeat flag. - if (!disableRepeat) { - if (prevText === msg) { - repeatedTimes++; + if (isMacintosh) { + alert(msg); // VoiceOver does not seem to support status role + } else { + if (statusContainer.textContent !== msg) { + dom.clearNode(statusContainer2); + insertMessage(statusContainer, msg); } else { - prevText = msg; - repeatedTimes = 0; - } - - switch (repeatedTimes) { - case 0: break; - case 1: msg = nls.localize('repeated', "{0} (occurred again)", msg); break; - default: msg = nls.localize('repeatedNtimes', "{0} (occurred {1} times)", msg, repeatedTimes); break; + dom.clearNode(statusContainer); + insertMessage(statusContainer2, msg); } } +} +function insertMessage(target: HTMLElement, msg: string): void { dom.clearNode(target); + if (msg.length > MAX_MESSAGE_LENGTH) { + msg = msg.substr(0, MAX_MESSAGE_LENGTH); + } target.textContent = msg; // See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/ target.style.visibility = 'hidden'; target.style.visibility = 'visible'; -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 65b207f0b04..50ad242acef 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -25,7 +25,7 @@ outline: none; } -.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-chevron-right { +.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-breadcrumb-separator { color: inherit; } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 5ce81d72e66..27981af2787 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -11,6 +11,7 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; import 'vs/css!./breadcrumbsWidget'; export abstract class BreadcrumbsItem { @@ -55,6 +56,8 @@ export interface IBreadcrumbsItemEvent { payload: any; } +const breadcrumbSeparatorIcon = registerIcon('breadcrumb-separator', Codicon.chevronRight); + export class BreadcrumbsWidget { private readonly _disposables = new DisposableStore(); @@ -81,7 +84,8 @@ export class BreadcrumbsWidget { private _dimension: dom.Dimension | undefined; constructor( - container: HTMLElement + container: HTMLElement, + horizontalScrollbarSize: number, ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs'; @@ -90,7 +94,7 @@ export class BreadcrumbsWidget { this._scrollable = new DomScrollableElement(this._domNode, { vertical: ScrollbarVisibility.Hidden, horizontal: ScrollbarVisibility.Auto, - horizontalScrollbarSize: 3, + horizontalScrollbarSize, useShadows: false, scrollYToX: true }); @@ -106,9 +110,15 @@ export class BreadcrumbsWidget { this._disposables.add(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true))); } + setHorizontalScrollbarSize(size: number) { + this._scrollable.updateOptions({ + horizontalScrollbarSize: size + }); + } + dispose(): void { this._disposables.dispose(); - dispose(this._pendingLayout); + this._pendingLayout?.dispose(); this._onDidSelectItem.dispose(); this._onDidFocusItem.dispose(); this._onDidChangeFocus.dispose(); @@ -121,9 +131,7 @@ export class BreadcrumbsWidget { if (dim && dom.Dimension.equals(dim, this._dimension)) { return; } - if (this._pendingLayout) { - this._pendingLayout.dispose(); - } + this._pendingLayout?.dispose(); if (dim) { // only measure this._pendingLayout = this._updateDimensions(dim); @@ -170,8 +178,8 @@ export class BreadcrumbsWidget { if (style.breadcrumbsHoverForeground) { content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`; } - if (this._styleElement.innerHTML !== content) { - this._styleElement.innerHTML = content; + if (this._styleElement.innerText !== content) { + this._styleElement.innerText = content; } } @@ -220,10 +228,10 @@ export class BreadcrumbsWidget { for (let i = 0; i < this._nodes.length; i++) { const node = this._nodes[i]; if (i !== nth) { - dom.removeClass(node, 'focused'); + node.classList.remove('focused'); } else { this._focusedItemIdx = i; - dom.addClass(node, 'focused'); + node.classList.add('focused'); node.focus(); } } @@ -264,10 +272,10 @@ export class BreadcrumbsWidget { for (let i = 0; i < this._nodes.length; i++) { const node = this._nodes[i]; if (i !== nth) { - dom.removeClass(node, 'selected'); + node.classList.remove('selected'); } else { this._selectedItemIdx = i; - dom.addClass(node, 'selected'); + node.classList.add('selected'); } } this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx], payload }); @@ -328,8 +336,8 @@ export class BreadcrumbsWidget { item.render(container); container.tabIndex = -1; container.setAttribute('role', 'listitem'); - dom.addClasses(container, 'monaco-breadcrumb-item'); - const iconContainer = dom.$('.codicon.codicon-chevron-right'); + container.classList.add('monaco-breadcrumb-item'); + const iconContainer = dom.$(breadcrumbSeparatorIcon.cssSelector); container.appendChild(iconContainer); } diff --git a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg deleted file mode 100644 index 243be1451cc..00000000000 --- a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg deleted file mode 100644 index 40ba72b7086..00000000000 --- a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg deleted file mode 100644 index 0d746558a4f..00000000000 --- a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css index 69299e332fb..6d682352484 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css @@ -5,12 +5,14 @@ .monaco-text-button { box-sizing: border-box; - display: inline-block; + display: flex; width: 100%; padding: 4px; text-align: center; cursor: pointer; outline-offset: 2px !important; + justify-content: center; + align-items: center; } .monaco-text-button:hover { @@ -21,3 +23,8 @@ opacity: 0.4; cursor: default; } + +.monaco-button > .codicon { + margin: 0 0.2em; + color: inherit !important; +} diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 6e436669cba..a3ca3ac973d 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -4,23 +4,29 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./button'; -import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Gesture, EventType } from 'vs/base/browser/touch'; +import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; +import { renderCodicons } from 'vs/base/browser/codicons'; +import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; export interface IButtonOptions extends IButtonStyles { - title?: boolean; + readonly title?: boolean | string; + readonly supportCodicons?: boolean; + readonly secondary?: boolean; } export interface IButtonStyles { buttonBackground?: Color; buttonHoverBackground?: Color; buttonForeground?: Color; + buttonSecondaryBackground?: Color; + buttonSecondaryHoverBackground?: Color; + buttonSecondaryForeground?: Color; buttonBorder?: Color; } @@ -38,12 +44,15 @@ export class Button extends Disposable { private buttonBackground: Color | undefined; private buttonHoverBackground: Color | undefined; private buttonForeground: Color | undefined; + private buttonSecondaryBackground: Color | undefined; + private buttonSecondaryHoverBackground: Color | undefined; + private buttonSecondaryForeground: Color | undefined; private buttonBorder: Color | undefined; private _onDidClick = this._register(new Emitter()); get onDidClick(): BaseEvent { return this._onDidClick.event; } - private focusTracker: DOM.IFocusTracker; + private focusTracker: IFocusTracker; constructor(container: HTMLElement, options?: IButtonOptions) { super(); @@ -51,13 +60,18 @@ export class Button extends Disposable { this.options = options || Object.create(null); mixin(this.options, defaultOptions, false); + this.buttonForeground = this.options.buttonForeground; this.buttonBackground = this.options.buttonBackground; this.buttonHoverBackground = this.options.buttonHoverBackground; - this.buttonForeground = this.options.buttonForeground; + + this.buttonSecondaryForeground = this.options.buttonSecondaryForeground; + this.buttonSecondaryBackground = this.options.buttonSecondaryBackground; + this.buttonSecondaryHoverBackground = this.options.buttonSecondaryHoverBackground; + this.buttonBorder = this.options.buttonBorder; this._element = document.createElement('a'); - DOM.addClass(this._element, 'monaco-button'); + this._element.classList.add('monaco-button'); this._element.tabIndex = 0; this._element.setAttribute('role', 'button'); @@ -65,10 +79,10 @@ export class Button extends Disposable { this._register(Gesture.addTarget(this._element)); - [DOM.EventType.CLICK, EventType.Tap].forEach(eventType => { - this._register(DOM.addDisposableListener(this._element, eventType, e => { + [EventType.CLICK, TouchEventType.Tap].forEach(eventType => { + this._register(addDisposableListener(this._element, eventType, e => { if (!this.enabled) { - DOM.EventHelper.stop(e); + EventHelper.stop(e); return; } @@ -76,7 +90,7 @@ export class Button extends Disposable { })); }); - this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => { + this._register(addDisposableListener(this._element, EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); let eventHandled = false; if (this.enabled && (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { @@ -88,22 +102,22 @@ export class Button extends Disposable { } if (eventHandled) { - DOM.EventHelper.stop(event, true); + EventHelper.stop(event, true); } })); - this._register(DOM.addDisposableListener(this._element, DOM.EventType.MOUSE_OVER, e => { - if (!DOM.hasClass(this._element, 'disabled')) { + this._register(addDisposableListener(this._element, EventType.MOUSE_OVER, e => { + if (!this._element.classList.contains('disabled')) { this.setHoverBackground(); } })); - this._register(DOM.addDisposableListener(this._element, DOM.EventType.MOUSE_OUT, e => { + this._register(addDisposableListener(this._element, EventType.MOUSE_OUT, e => { this.applyStyles(); // restore standard styles })); // Also set hover background when button is focused for feedback - this.focusTracker = this._register(DOM.trackFocus(this._element)); + this.focusTracker = this._register(trackFocus(this._element)); this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground())); this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles @@ -111,7 +125,12 @@ export class Button extends Disposable { } private setHoverBackground(): void { - const hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null; + let hoverBackground; + if (this.options.secondary) { + hoverBackground = this.buttonSecondaryHoverBackground ? this.buttonSecondaryHoverBackground.toString() : null; + } else { + hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null; + } if (hoverBackground) { this._element.style.backgroundColor = hoverBackground; } @@ -121,6 +140,9 @@ export class Button extends Disposable { this.buttonForeground = styles.buttonForeground; this.buttonBackground = styles.buttonBackground; this.buttonHoverBackground = styles.buttonHoverBackground; + this.buttonSecondaryForeground = styles.buttonSecondaryForeground; + this.buttonSecondaryBackground = styles.buttonSecondaryBackground; + this.buttonSecondaryHoverBackground = styles.buttonSecondaryHoverBackground; this.buttonBorder = styles.buttonBorder; this.applyStyles(); @@ -128,8 +150,15 @@ export class Button extends Disposable { private applyStyles(): void { if (this._element) { - const background = this.buttonBackground ? this.buttonBackground.toString() : ''; - const foreground = this.buttonForeground ? this.buttonForeground.toString() : ''; + let background, foreground; + if (this.options.secondary) { + foreground = this.buttonSecondaryForeground ? this.buttonSecondaryForeground.toString() : ''; + background = this.buttonSecondaryBackground ? this.buttonSecondaryBackground.toString() : ''; + } else { + foreground = this.buttonForeground ? this.buttonForeground.toString() : ''; + background = this.buttonBackground ? this.buttonBackground.toString() : ''; + } + const border = this.buttonBorder ? this.buttonBorder.toString() : ''; this._element.style.color = foreground; @@ -146,38 +175,46 @@ export class Button extends Disposable { } set label(value: string) { - if (!DOM.hasClass(this._element, 'monaco-text-button')) { - DOM.addClass(this._element, 'monaco-text-button'); + this._element.classList.add('monaco-text-button'); + if (this.options.supportCodicons) { + reset(this._element, ...renderCodicons(value)); + } else { + this._element.textContent = value; } - this._element.textContent = value; - if (this.options.title) { + if (typeof this.options.title === 'string') { + this._element.title = this.options.title; + } else if (this.options.title) { this._element.title = value; } } set icon(iconClassName: string) { - DOM.addClass(this._element, iconClassName); + this._element.classList.add(iconClassName); } set enabled(value: boolean) { if (value) { - DOM.removeClass(this._element, 'disabled'); + this._element.classList.remove('disabled'); this._element.setAttribute('aria-disabled', String(false)); this._element.tabIndex = 0; } else { - DOM.addClass(this._element, 'disabled'); + this._element.classList.add('disabled'); this._element.setAttribute('aria-disabled', String(true)); - DOM.removeTabIndexAndUpdateFocus(this._element); + removeTabIndexAndUpdateFocus(this._element); } } get enabled() { - return !DOM.hasClass(this._element, 'disabled'); + return !this._element.classList.contains('disabled'); } focus(): void { this._element.focus(); } + + hasFocus(): boolean { + return this._element === document.activeElement; + } } export class ButtonGroup extends Disposable { @@ -200,7 +237,7 @@ export class ButtonGroup extends Disposable { // Implement keyboard access in buttons if there are multiple if (count > 1) { - this._register(DOM.addDisposableListener(button.element, DOM.EventType.KEY_DOWN, e => { + this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); let eventHandled = true; @@ -216,7 +253,7 @@ export class ButtonGroup extends Disposable { if (eventHandled && typeof buttonIndexToFocus === 'number') { this._buttons[buttonIndexToFocus].focus(); - DOM.EventHelper.stop(e, true); + EventHelper.stop(e, true); } })); diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index fc0141b11ac..52cc64b8a36 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -9,6 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; +import { IBoundarySashes } from 'vs/base/browser/ui/grid/gridview'; export interface CenteredViewState { leftMarginRatio: number; @@ -72,6 +73,19 @@ export class CenteredViewLayout implements IDisposable { get maximumHeight(): number { return this.view.maximumHeight; } get onDidChange(): Event { return this.view.onDidChange; } + private _boundarySashes: IBoundarySashes = {}; + get boundarySashes(): IBoundarySashes { return this._boundarySashes; } + set boundarySashes(boundarySashes: IBoundarySashes) { + this._boundarySashes = boundarySashes; + + if (!this.splitView) { + return; + } + + this.splitView.orthogonalStartSash = boundarySashes.top; + this.splitView.orthogonalEndSash = boundarySashes.bottom; + } + layout(width: number, height: number): void { this.width = width; this.height = height; @@ -119,6 +133,8 @@ export class CenteredViewLayout implements IDisposable { orientation: Orientation.HORIZONTAL, styles: this.style }); + this.splitView.orthogonalStartSash = this.boundarySashes.top; + this.splitView.orthogonalEndSash = this.boundarySashes.bottom; this.splitViewDisposables.add(this.splitView.onDidSashChange(() => { if (this.splitView) { diff --git a/src/vs/base/browser/ui/checkbox/checkbox.css b/src/vs/base/browser/ui/checkbox/checkbox.css index 972e94f7175..c754adc1518 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.css +++ b/src/vs/base/browser/ui/checkbox/checkbox.css @@ -45,6 +45,6 @@ } /* hide check when unchecked */ -.monaco-custom-checkbox.monaco-simple-checkbox.unchecked:not(.checked)::before { - visibility: hidden;; +.monaco-custom-checkbox.monaco-simple-checkbox:not(.checked)::before { + visibility: hidden; } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 372119cfa19..c3b0e243f91 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -10,18 +10,20 @@ import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import * as objects from 'vs/base/common/objects'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Codicon } from 'vs/base/common/codicons'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export interface ICheckboxOpts extends ICheckboxStyles { readonly actionClassName?: string; + readonly icon?: Codicon; readonly title: string; readonly isChecked: boolean; } export interface ICheckboxStyles { inputActiveOptionBorder?: Color; + inputActiveOptionForeground?: Color; inputActiveOptionBackground?: Color; } @@ -33,13 +35,14 @@ export interface ISimpleCheckboxStyles { const defaultOpts = { inputActiveOptionBorder: Color.fromHex('#007ACC00'), + inputActiveOptionForeground: Color.fromHex('#FFFFFF'), inputActiveOptionBackground: Color.fromHex('#0E639C50') }; export class CheckboxActionViewItem extends BaseActionViewItem { - private checkbox: Checkbox | undefined; - private readonly disposables = new DisposableStore(); + protected checkbox: Checkbox | undefined; + protected readonly disposables = new DisposableStore(); render(container: HTMLElement): void { this.element = container; @@ -93,13 +96,25 @@ export class Checkbox extends Widget { constructor(opts: ICheckboxOpts) { super(); - this._opts = objects.deepClone(opts); - objects.mixin(this._opts, defaultOpts, false); + this._opts = { ...defaultOpts, ...opts }; this._checked = this._opts.isChecked; + const classes = ['monaco-custom-checkbox']; + if (this._opts.icon) { + classes.push(this._opts.icon.classNames); + } else { + classes.push('codicon'); // todo@aeschli: remove once codicon fully adopted + } + if (this._opts.actionClassName) { + classes.push(this._opts.actionClassName); + } + if (this._checked) { + classes.push('checked'); + } + this.domNode = document.createElement('div'); this.domNode.title = this._opts.title; - this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'checked' : 'unchecked'); + this.domNode.className = classes.join(' '); this.domNode.tabIndex = 0; this.domNode.setAttribute('role', 'checkbox'); this.domNode.setAttribute('aria-checked', String(this._checked)); @@ -141,12 +156,9 @@ export class Checkbox extends Widget { set checked(newIsChecked: boolean) { this._checked = newIsChecked; + this.domNode.setAttribute('aria-checked', String(this._checked)); - if (this._checked) { - this.domNode.classList.add('checked'); - } else { - this.domNode.classList.remove('checked'); - } + this.domNode.classList.toggle('checked', this._checked); this.applyStyles(); } @@ -159,6 +171,9 @@ export class Checkbox extends Widget { if (styles.inputActiveOptionBorder) { this._opts.inputActiveOptionBorder = styles.inputActiveOptionBorder; } + if (styles.inputActiveOptionForeground) { + this._opts.inputActiveOptionForeground = styles.inputActiveOptionForeground; + } if (styles.inputActiveOptionBackground) { this._opts.inputActiveOptionBackground = styles.inputActiveOptionBackground; } @@ -168,6 +183,7 @@ export class Checkbox extends Widget { protected applyStyles(): void { if (this.domNode) { this.domNode.style.borderColor = this._checked && this._opts.inputActiveOptionBorder ? this._opts.inputActiveOptionBorder.toString() : 'transparent'; + this.domNode.style.color = this._checked && this._opts.inputActiveOptionForeground ? this._opts.inputActiveOptionForeground.toString() : 'inherit'; this.domNode.style.backgroundColor = this._checked && this._opts.inputActiveOptionBackground ? this._opts.inputActiveOptionBackground.toString() : 'transparent'; } } @@ -192,7 +208,7 @@ export class SimpleCheckbox extends Widget { constructor(private title: string, private isChecked: boolean) { super(); - this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, actionClassName: 'monaco-simple-checkbox codicon-check' }); + this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, icon: Codicon.check, actionClassName: 'monaco-simple-checkbox' }); this.domNode = this.checkbox.domNode; @@ -213,6 +229,14 @@ export class SimpleCheckbox extends Widget { this.applyStyles(); } + focus(): void { + this.domNode.focus(); + } + + hasFocus(): boolean { + return this.domNode === document.activeElement; + } + style(styles: ISimpleCheckboxStyles): void { this.styles = styles; diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css deleted file mode 100644 index c017682c7f6..00000000000 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ /dev/null @@ -1,416 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -@font-face { - font-family: "codicon"; - src: url("./codicon.ttf?be537a78617db0869caa4b4cc683a24a") format("truetype"); -} - -.codicon[class*='codicon-'] { - font: normal normal normal 16px/1 codicon; - display: inline-block; - text-decoration: none; - text-rendering: auto; - text-align: center; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - user-select: none; - -webkit-user-select: none; - -ms-user-select: none; -} - - -.codicon-add:before { content: "\ea60" } -.codicon-plus:before { content: "\ea60" } -.codicon-gist-new:before { content: "\ea60" } -.codicon-repo-create:before { content: "\ea60" } -.codicon-lightbulb:before { content: "\ea61" } -.codicon-light-bulb:before { content: "\ea61" } -.codicon-repo:before { content: "\ea62" } -.codicon-repo-delete:before { content: "\ea62" } -.codicon-gist-fork:before { content: "\ea63" } -.codicon-repo-forked:before { content: "\ea63" } -.codicon-git-pull-request:before { content: "\ea64" } -.codicon-git-pull-request-abandoned:before { content: "\ea64" } -.codicon-record-keys:before { content: "\ea65" } -.codicon-keyboard:before { content: "\ea65" } -.codicon-tag:before { content: "\ea66" } -.codicon-tag-add:before { content: "\ea66" } -.codicon-tag-remove:before { content: "\ea66" } -.codicon-person:before { content: "\ea67" } -.codicon-person-add:before { content: "\ea67" } -.codicon-person-follow:before { content: "\ea67" } -.codicon-person-outline:before { content: "\ea67" } -.codicon-person-filled:before { content: "\ea67" } -.codicon-git-branch:before { content: "\ea68" } -.codicon-git-branch-create:before { content: "\ea68" } -.codicon-git-branch-delete:before { content: "\ea68" } -.codicon-source-control:before { content: "\ea68" } -.codicon-mirror:before { content: "\ea69" } -.codicon-mirror-public:before { content: "\ea69" } -.codicon-star:before { content: "\ea6a" } -.codicon-star-add:before { content: "\ea6a" } -.codicon-star-delete:before { content: "\ea6a" } -.codicon-star-empty:before { content: "\ea6a" } -.codicon-comment:before { content: "\ea6b" } -.codicon-comment-add:before { content: "\ea6b" } -.codicon-alert:before { content: "\ea6c" } -.codicon-warning:before { content: "\ea6c" } -.codicon-search:before { content: "\ea6d" } -.codicon-search-save:before { content: "\ea6d" } -.codicon-log-out:before { content: "\ea6e" } -.codicon-sign-out:before { content: "\ea6e" } -.codicon-log-in:before { content: "\ea6f" } -.codicon-sign-in:before { content: "\ea6f" } -.codicon-eye:before { content: "\ea70" } -.codicon-eye-unwatch:before { content: "\ea70" } -.codicon-eye-watch:before { content: "\ea70" } -.codicon-circle-filled:before { content: "\ea71" } -.codicon-primitive-dot:before { content: "\ea71" } -.codicon-close-dirty:before { content: "\ea71" } -.codicon-debug-breakpoint:before { content: "\ea71" } -.codicon-debug-breakpoint-disabled:before { content: "\ea71" } -.codicon-debug-hint:before { content: "\ea71" } -.codicon-primitive-square:before { content: "\ea72" } -.codicon-edit:before { content: "\ea73" } -.codicon-pencil:before { content: "\ea73" } -.codicon-info:before { content: "\ea74" } -.codicon-issue-opened:before { content: "\ea74" } -.codicon-gist-private:before { content: "\ea75" } -.codicon-git-fork-private:before { content: "\ea75" } -.codicon-lock:before { content: "\ea75" } -.codicon-mirror-private:before { content: "\ea75" } -.codicon-close:before { content: "\ea76" } -.codicon-remove-close:before { content: "\ea76" } -.codicon-x:before { content: "\ea76" } -.codicon-repo-sync:before { content: "\ea77" } -.codicon-sync:before { content: "\ea77" } -.codicon-clone:before { content: "\ea78" } -.codicon-desktop-download:before { content: "\ea78" } -.codicon-beaker:before { content: "\ea79" } -.codicon-microscope:before { content: "\ea79" } -.codicon-vm:before { content: "\ea7a" } -.codicon-device-desktop:before { content: "\ea7a" } -.codicon-file:before { content: "\ea7b" } -.codicon-file-text:before { content: "\ea7b" } -.codicon-more:before { content: "\ea7c" } -.codicon-ellipsis:before { content: "\ea7c" } -.codicon-kebab-horizontal:before { content: "\ea7c" } -.codicon-mail-reply:before { content: "\ea7d" } -.codicon-reply:before { content: "\ea7d" } -.codicon-organization:before { content: "\ea7e" } -.codicon-organization-filled:before { content: "\ea7e" } -.codicon-organization-outline:before { content: "\ea7e" } -.codicon-new-file:before { content: "\ea7f" } -.codicon-file-add:before { content: "\ea7f" } -.codicon-new-folder:before { content: "\ea80" } -.codicon-file-directory-create:before { content: "\ea80" } -.codicon-trash:before { content: "\ea81" } -.codicon-trashcan:before { content: "\ea81" } -.codicon-history:before { content: "\ea82" } -.codicon-clock:before { content: "\ea82" } -.codicon-folder:before { content: "\ea83" } -.codicon-file-directory:before { content: "\ea83" } -.codicon-symbol-folder:before { content: "\ea83" } -.codicon-logo-github:before { content: "\ea84" } -.codicon-mark-github:before { content: "\ea84" } -.codicon-github:before { content: "\ea84" } -.codicon-terminal:before { content: "\ea85" } -.codicon-console:before { content: "\ea85" } -.codicon-repl:before { content: "\ea85" } -.codicon-zap:before { content: "\ea86" } -.codicon-symbol-event:before { content: "\ea86" } -.codicon-error:before { content: "\ea87" } -.codicon-stop:before { content: "\ea87" } -.codicon-variable:before { content: "\ea88" } -.codicon-symbol-variable:before { content: "\ea88" } -.codicon-array:before { content: "\ea8a" } -.codicon-symbol-array:before { content: "\ea8a" } -.codicon-symbol-module:before { content: "\ea8b" } -.codicon-symbol-package:before { content: "\ea8b" } -.codicon-symbol-namespace:before { content: "\ea8b" } -.codicon-symbol-object:before { content: "\ea8b" } -.codicon-symbol-method:before { content: "\ea8c" } -.codicon-symbol-function:before { content: "\ea8c" } -.codicon-symbol-constructor:before { content: "\ea8c" } -.codicon-symbol-boolean:before { content: "\ea8f" } -.codicon-symbol-null:before { content: "\ea8f" } -.codicon-symbol-numeric:before { content: "\ea90" } -.codicon-symbol-number:before { content: "\ea90" } -.codicon-symbol-structure:before { content: "\ea91" } -.codicon-symbol-struct:before { content: "\ea91" } -.codicon-symbol-parameter:before { content: "\ea92" } -.codicon-symbol-type-parameter:before { content: "\ea92" } -.codicon-symbol-key:before { content: "\ea93" } -.codicon-symbol-text:before { content: "\ea93" } -.codicon-symbol-reference:before { content: "\ea94" } -.codicon-go-to-file:before { content: "\ea94" } -.codicon-symbol-enum:before { content: "\ea95" } -.codicon-symbol-value:before { content: "\ea95" } -.codicon-symbol-ruler:before { content: "\ea96" } -.codicon-symbol-unit:before { content: "\ea96" } -.codicon-activate-breakpoints:before { content: "\ea97" } -.codicon-archive:before { content: "\ea98" } -.codicon-arrow-both:before { content: "\ea99" } -.codicon-arrow-down:before { content: "\ea9a" } -.codicon-arrow-left:before { content: "\ea9b" } -.codicon-arrow-right:before { content: "\ea9c" } -.codicon-arrow-small-down:before { content: "\ea9d" } -.codicon-arrow-small-left:before { content: "\ea9e" } -.codicon-arrow-small-right:before { content: "\ea9f" } -.codicon-arrow-small-up:before { content: "\eaa0" } -.codicon-arrow-up:before { content: "\eaa1" } -.codicon-bell:before { content: "\eaa2" } -.codicon-bold:before { content: "\eaa3" } -.codicon-book:before { content: "\eaa4" } -.codicon-bookmark:before { content: "\eaa5" } -.codicon-debug-breakpoint-conditional-unverified:before { content: "\eaa6" } -.codicon-debug-breakpoint-conditional:before { content: "\eaa7" } -.codicon-debug-breakpoint-conditional-disabled:before { content: "\eaa7" } -.codicon-debug-breakpoint-data-unverified:before { content: "\eaa8" } -.codicon-debug-breakpoint-data:before { content: "\eaa9" } -.codicon-debug-breakpoint-data-disabled:before { content: "\eaa9" } -.codicon-debug-breakpoint-log-unverified:before { content: "\eaaa" } -.codicon-debug-breakpoint-log:before { content: "\eaab" } -.codicon-debug-breakpoint-log-disabled:before { content: "\eaab" } -.codicon-briefcase:before { content: "\eaac" } -.codicon-broadcast:before { content: "\eaad" } -.codicon-browser:before { content: "\eaae" } -.codicon-bug:before { content: "\eaaf" } -.codicon-calendar:before { content: "\eab0" } -.codicon-case-sensitive:before { content: "\eab1" } -.codicon-check:before { content: "\eab2" } -.codicon-checklist:before { content: "\eab3" } -.codicon-chevron-down:before { content: "\eab4" } -.codicon-chevron-left:before { content: "\eab5" } -.codicon-chevron-right:before { content: "\eab6" } -.codicon-chevron-up:before { content: "\eab7" } -.codicon-chrome-close:before { content: "\eab8" } -.codicon-chrome-maximize:before { content: "\eab9" } -.codicon-chrome-minimize:before { content: "\eaba" } -.codicon-chrome-restore:before { content: "\eabb" } -.codicon-circle-outline:before { content: "\eabc" } -.codicon-debug-breakpoint-unverified:before { content: "\eabc" } -.codicon-circle-slash:before { content: "\eabd" } -.codicon-circuit-board:before { content: "\eabe" } -.codicon-clear-all:before { content: "\eabf" } -.codicon-clippy:before { content: "\eac0" } -.codicon-close-all:before { content: "\eac1" } -.codicon-cloud-download:before { content: "\eac2" } -.codicon-cloud-upload:before { content: "\eac3" } -.codicon-code:before { content: "\eac4" } -.codicon-collapse-all:before { content: "\eac5" } -.codicon-color-mode:before { content: "\eac6" } -.codicon-comment-discussion:before { content: "\eac7" } -.codicon-compare-changes:before { content: "\eac8" } -.codicon-credit-card:before { content: "\eac9" } -.codicon-dash:before { content: "\eacc" } -.codicon-dashboard:before { content: "\eacd" } -.codicon-database:before { content: "\eace" } -.codicon-debug-continue:before { content: "\eacf" } -.codicon-debug-disconnect:before { content: "\ead0" } -.codicon-debug-pause:before { content: "\ead1" } -.codicon-debug-restart:before { content: "\ead2" } -.codicon-debug-start:before { content: "\ead3" } -.codicon-debug-step-into:before { content: "\ead4" } -.codicon-debug-step-out:before { content: "\ead5" } -.codicon-debug-step-over:before { content: "\ead6" } -.codicon-debug-stop:before { content: "\ead7" } -.codicon-debug:before { content: "\ead8" } -.codicon-device-camera-video:before { content: "\ead9" } -.codicon-device-camera:before { content: "\eada" } -.codicon-device-mobile:before { content: "\eadb" } -.codicon-diff-added:before { content: "\eadc" } -.codicon-diff-ignored:before { content: "\eadd" } -.codicon-diff-modified:before { content: "\eade" } -.codicon-diff-removed:before { content: "\eadf" } -.codicon-diff-renamed:before { content: "\eae0" } -.codicon-diff:before { content: "\eae1" } -.codicon-discard:before { content: "\eae2" } -.codicon-editor-layout:before { content: "\eae3" } -.codicon-empty-window:before { content: "\eae4" } -.codicon-exclude:before { content: "\eae5" } -.codicon-extensions:before { content: "\eae6" } -.codicon-eye-closed:before { content: "\eae7" } -.codicon-file-binary:before { content: "\eae8" } -.codicon-file-code:before { content: "\eae9" } -.codicon-file-media:before { content: "\eaea" } -.codicon-file-pdf:before { content: "\eaeb" } -.codicon-file-submodule:before { content: "\eaec" } -.codicon-file-symlink-directory:before { content: "\eaed" } -.codicon-file-symlink-file:before { content: "\eaee" } -.codicon-file-zip:before { content: "\eaef" } -.codicon-files:before { content: "\eaf0" } -.codicon-filter:before { content: "\eaf1" } -.codicon-flame:before { content: "\eaf2" } -.codicon-fold-down:before { content: "\eaf3" } -.codicon-fold-up:before { content: "\eaf4" } -.codicon-fold:before { content: "\eaf5" } -.codicon-folder-active:before { content: "\eaf6" } -.codicon-folder-opened:before { content: "\eaf7" } -.codicon-gear:before { content: "\eaf8" } -.codicon-gift:before { content: "\eaf9" } -.codicon-gist-secret:before { content: "\eafa" } -.codicon-gist:before { content: "\eafb" } -.codicon-git-commit:before { content: "\eafc" } -.codicon-git-compare:before { content: "\eafd" } -.codicon-git-merge:before { content: "\eafe" } -.codicon-github-action:before { content: "\eaff" } -.codicon-github-alt:before { content: "\eb00" } -.codicon-globe:before { content: "\eb01" } -.codicon-grabber:before { content: "\eb02" } -.codicon-graph:before { content: "\eb03" } -.codicon-gripper:before { content: "\eb04" } -.codicon-heart:before { content: "\eb05" } -.codicon-home:before { content: "\eb06" } -.codicon-horizontal-rule:before { content: "\eb07" } -.codicon-hubot:before { content: "\eb08" } -.codicon-inbox:before { content: "\eb09" } -.codicon-issue-closed:before { content: "\eb0a" } -.codicon-issue-reopened:before { content: "\eb0b" } -.codicon-issues:before { content: "\eb0c" } -.codicon-italic:before { content: "\eb0d" } -.codicon-jersey:before { content: "\eb0e" } -.codicon-json:before { content: "\eb0f" } -.codicon-kebab-vertical:before { content: "\eb10" } -.codicon-key:before { content: "\eb11" } -.codicon-law:before { content: "\eb12" } -.codicon-lightbulb-autofix:before { content: "\eb13" } -.codicon-link-external:before { content: "\eb14" } -.codicon-link:before { content: "\eb15" } -.codicon-list-ordered:before { content: "\eb16" } -.codicon-list-unordered:before { content: "\eb17" } -.codicon-live-share:before { content: "\eb18" } -.codicon-loading:before { content: "\eb19" } -.codicon-location:before { content: "\eb1a" } -.codicon-mail-read:before { content: "\eb1b" } -.codicon-mail:before { content: "\eb1c" } -.codicon-markdown:before { content: "\eb1d" } -.codicon-megaphone:before { content: "\eb1e" } -.codicon-mention:before { content: "\eb1f" } -.codicon-milestone:before { content: "\eb20" } -.codicon-mortar-board:before { content: "\eb21" } -.codicon-move:before { content: "\eb22" } -.codicon-multiple-windows:before { content: "\eb23" } -.codicon-mute:before { content: "\eb24" } -.codicon-no-newline:before { content: "\eb25" } -.codicon-note:before { content: "\eb26" } -.codicon-octoface:before { content: "\eb27" } -.codicon-open-preview:before { content: "\eb28" } -.codicon-package:before { content: "\eb29" } -.codicon-paintcan:before { content: "\eb2a" } -.codicon-pin:before { content: "\eb2b" } -.codicon-play:before { content: "\eb2c" } -.codicon-plug:before { content: "\eb2d" } -.codicon-preserve-case:before { content: "\eb2e" } -.codicon-preview:before { content: "\eb2f" } -.codicon-project:before { content: "\eb30" } -.codicon-pulse:before { content: "\eb31" } -.codicon-question:before { content: "\eb32" } -.codicon-quote:before { content: "\eb33" } -.codicon-radio-tower:before { content: "\eb34" } -.codicon-reactions:before { content: "\eb35" } -.codicon-references:before { content: "\eb36" } -.codicon-refresh:before { content: "\eb37" } -.codicon-regex:before { content: "\eb38" } -.codicon-remote-explorer:before { content: "\eb39" } -.codicon-remote:before { content: "\eb3a" } -.codicon-remove:before { content: "\eb3b" } -.codicon-replace-all:before { content: "\eb3c" } -.codicon-replace:before { content: "\eb3d" } -.codicon-repo-clone:before { content: "\eb3e" } -.codicon-repo-force-push:before { content: "\eb3f" } -.codicon-repo-pull:before { content: "\eb40" } -.codicon-repo-push:before { content: "\eb41" } -.codicon-report:before { content: "\eb42" } -.codicon-request-changes:before { content: "\eb43" } -.codicon-rocket:before { content: "\eb44" } -.codicon-root-folder-opened:before { content: "\eb45" } -.codicon-root-folder:before { content: "\eb46" } -.codicon-rss:before { content: "\eb47" } -.codicon-ruby:before { content: "\eb48" } -.codicon-save-all:before { content: "\eb49" } -.codicon-save-as:before { content: "\eb4a" } -.codicon-save:before { content: "\eb4b" } -.codicon-screen-full:before { content: "\eb4c" } -.codicon-screen-normal:before { content: "\eb4d" } -.codicon-search-stop:before { content: "\eb4e" } -.codicon-server:before { content: "\eb50" } -.codicon-settings-gear:before { content: "\eb51" } -.codicon-settings:before { content: "\eb52" } -.codicon-shield:before { content: "\eb53" } -.codicon-smiley:before { content: "\eb54" } -.codicon-sort-precedence:before { content: "\eb55" } -.codicon-split-horizontal:before { content: "\eb56" } -.codicon-split-vertical:before { content: "\eb57" } -.codicon-squirrel:before { content: "\eb58" } -.codicon-star-full:before { content: "\eb59" } -.codicon-star-half:before { content: "\eb5a" } -.codicon-symbol-class:before { content: "\eb5b" } -.codicon-symbol-color:before { content: "\eb5c" } -.codicon-symbol-constant:before { content: "\eb5d" } -.codicon-symbol-enum-member:before { content: "\eb5e" } -.codicon-symbol-field:before { content: "\eb5f" } -.codicon-symbol-file:before { content: "\eb60" } -.codicon-symbol-interface:before { content: "\eb61" } -.codicon-symbol-keyword:before { content: "\eb62" } -.codicon-symbol-misc:before { content: "\eb63" } -.codicon-symbol-operator:before { content: "\eb64" } -.codicon-symbol-property:before { content: "\eb65" } -.codicon-symbol-snippet:before { content: "\eb66" } -.codicon-tasklist:before { content: "\eb67" } -.codicon-telescope:before { content: "\eb68" } -.codicon-text-size:before { content: "\eb69" } -.codicon-three-bars:before { content: "\eb6a" } -.codicon-thumbsdown:before { content: "\eb6b" } -.codicon-thumbsup:before { content: "\eb6c" } -.codicon-tools:before { content: "\eb6d" } -.codicon-triangle-down:before { content: "\eb6e" } -.codicon-triangle-left:before { content: "\eb6f" } -.codicon-triangle-right:before { content: "\eb70" } -.codicon-triangle-up:before { content: "\eb71" } -.codicon-twitter:before { content: "\eb72" } -.codicon-unfold:before { content: "\eb73" } -.codicon-unlock:before { content: "\eb74" } -.codicon-unmute:before { content: "\eb75" } -.codicon-unverified:before { content: "\eb76" } -.codicon-verified:before { content: "\eb77" } -.codicon-versions:before { content: "\eb78" } -.codicon-vm-active:before { content: "\eb79" } -.codicon-vm-outline:before { content: "\eb7a" } -.codicon-vm-running:before { content: "\eb7b" } -.codicon-watch:before { content: "\eb7c" } -.codicon-whitespace:before { content: "\eb7d" } -.codicon-whole-word:before { content: "\eb7e" } -.codicon-window:before { content: "\eb7f" } -.codicon-word-wrap:before { content: "\eb80" } -.codicon-zoom-in:before { content: "\eb81" } -.codicon-zoom-out:before { content: "\eb82" } -.codicon-list-filter:before { content: "\eb83" } -.codicon-list-flat:before { content: "\eb84" } -.codicon-list-selection:before { content: "\eb85" } -.codicon-selection:before { content: "\eb85" } -.codicon-list-tree:before { content: "\eb86" } -.codicon-debug-breakpoint-function-unverified:before { content: "\eb87" } -.codicon-debug-breakpoint-function:before { content: "\eb88" } -.codicon-debug-breakpoint-function-disabled:before { content: "\eb88" } -.codicon-debug-stackframe-active:before { content: "\eb89" } -.codicon-debug-stackframe-dot:before { content: "\eb8a" } -.codicon-debug-stackframe:before { content: "\eb8b" } -.codicon-debug-stackframe-focused:before { content: "\eb8b" } -.codicon-debug-breakpoint-unsupported:before { content: "\eb8c" } -.codicon-symbol-string:before { content: "\eb8d" } -.codicon-debug-reverse-continue:before { content: "\eb8e" } -.codicon-debug-step-back:before { content: "\eb8f" } -.codicon-debug-restart-frame:before { content: "\eb90" } -.codicon-debug-alternate:before { content: "\eb91" } -.codicon-call-incoming:before { content: "\eb92" } -.codicon-call-outgoing:before { content: "\eb93" } -.codicon-menu:before { content: "\eb94" } -.codicon-expand-all:before { content: "\eb95" } -.codicon-feedback:before { content: "\eb96" } -.codicon-group-by-ref-type:before { content: "\eb97" } -.codicon-ungroup-by-ref-type:before { content: "\eb98" } -.codicon-debug-alt:before { content: "\f101" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf deleted file mode 100644 index 90ace76ff74..00000000000 Binary files a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf and /dev/null differ diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css b/src/vs/base/browser/ui/codicons/codicon/codicon-animations.css similarity index 81% rename from src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css rename to src/vs/base/browser/ui/codicons/codicon/codicon-animations.css index abfde40dede..667002f5b7c 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon-animations.css @@ -10,5 +10,6 @@ } .codicon-animation-spin { - animation: codicon-spin 1.5s linear infinite; + /* Use steps to throttle FPS to reduce CPU usage */ + animation: codicon-spin 1.5s steps(30) infinite; } diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.css b/src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css similarity index 88% rename from src/vs/editor/standalone/browser/quickOpen/quickOutline.css rename to src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css index 75309ce3318..950493dd6be 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css @@ -3,6 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-quick-open-widget { - font-size: 13px; +.codicon-wrench-subaction { + opacity: 0.5; } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.css b/src/vs/base/browser/ui/codicons/codicon/codicon.css new file mode 100644 index 00000000000..a0593f7cbff --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codicon/codicon.css @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +@font-face { + font-family: "codicon"; + src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype"); +} + +.codicon[class*='codicon-'] { + font: normal normal normal 16px/1 codicon; + display: inline-block; + text-decoration: none; + text-rendering: auto; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + user-select: none; + -webkit-user-select: none; + -ms-user-select: none; +} + +/* icon rules are dynamically created in codiconStyles */ diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf new file mode 100644 index 00000000000..4c16209d6a9 Binary files /dev/null and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts b/src/vs/base/browser/ui/codicons/codiconLabel.ts similarity index 68% rename from src/vs/base/browser/ui/codiconLabel/codiconLabel.ts rename to src/vs/base/browser/ui/codicons/codiconLabel.ts index ccec1f655bb..e21eb2381a4 100644 --- a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts +++ b/src/vs/base/browser/ui/codicons/codiconLabel.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./codicon/codicon'; -import 'vs/css!./codicon/codicon-animations'; -import { escape } from 'vs/base/common/strings'; -import { renderCodicons } from 'vs/base/common/codicons'; +import { reset } from 'vs/base/browser/dom'; +import { renderCodicons } from 'vs/base/browser/codicons'; export class CodiconLabel { @@ -15,7 +13,7 @@ export class CodiconLabel { ) { } set text(text: string) { - this._container.innerHTML = renderCodicons(escape(text ?? '')); + reset(this._container, ...renderCodicons(text ?? '')); } set title(title: string) { diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts new file mode 100644 index 00000000000..8229f2f1cf1 --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./codicon/codicon'; +import 'vs/css!./codicon/codicon-modifications'; +import 'vs/css!./codicon/codicon-animations'; + +import { Codicon, iconRegistry } from 'vs/base/common/codicons'; + +export const CodiconStyles = new class { + onDidChange = iconRegistry.onDidRegister; + public getCSS(): string { + const rules = []; + for (let c of iconRegistry.all) { + rules.push(formatRule(c)); + } + return rules.join('\n'); + } +}; + +export function formatRule(c: Codicon) { + let def = c.definition; + while (def instanceof Codicon) { + def = def.definition; + } + return `.codicon-${c.id}:before { content: '${def.character}'; }`; +} diff --git a/src/vs/base/browser/ui/contextview/contextview.css b/src/vs/base/browser/ui/contextview/contextview.css index b1b67bbe3c3..bb7ebbcfdb2 100644 --- a/src/vs/base/browser/ui/contextview/contextview.css +++ b/src/vs/base/browser/ui/contextview/contextview.css @@ -7,3 +7,12 @@ position: absolute; z-index: 2500; } + +.context-view.fixed { + all: initial; + font-family: inherit; + font-size: 13px; + position: fixed; + z-index: 2500; + color: inherit; +} diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index eacf37f358a..97e78cefeda 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -10,6 +10,12 @@ import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/ import { Range } from 'vs/base/common/range'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; +export const enum ContextViewDOMPosition { + ABSOLUTE = 1, + FIXED, + FIXED_SHADOW +} + export interface IAnchor { x: number; y: number; @@ -38,7 +44,7 @@ export interface IDelegate { } export interface IContextViewProvider { - showContextView(delegate: IDelegate): void; + showContextView(delegate: IDelegate, container?: HTMLElement): void; hideContextView(): void; layout(): void; } @@ -104,31 +110,61 @@ export class ContextView extends Disposable { private container: HTMLElement | null = null; private view: HTMLElement; + private useFixedPosition: boolean; + private useShadowDOM: boolean; private delegate: IDelegate | null = null; private toDisposeOnClean: IDisposable = Disposable.None; private toDisposeOnSetContainer: IDisposable = Disposable.None; + private shadowRoot: ShadowRoot | null = null; + private shadowRootHostElement: HTMLElement | null = null; - constructor(container: HTMLElement) { + constructor(container: HTMLElement, domPosition: ContextViewDOMPosition) { super(); this.view = DOM.$('.context-view'); + this.useFixedPosition = false; + this.useShadowDOM = false; DOM.hide(this.view); - this.setContainer(container); + this.setContainer(container, domPosition); - this._register(toDisposable(() => this.setContainer(null))); + this._register(toDisposable(() => this.setContainer(null, ContextViewDOMPosition.ABSOLUTE))); } - setContainer(container: HTMLElement | null): void { + setContainer(container: HTMLElement | null, domPosition: ContextViewDOMPosition): void { if (this.container) { this.toDisposeOnSetContainer.dispose(); - this.container.removeChild(this.view); + + if (this.shadowRoot) { + this.shadowRoot.removeChild(this.view); + this.shadowRoot = null; + this.shadowRootHostElement?.remove(); + this.shadowRootHostElement = null; + } else { + this.container.removeChild(this.view); + } + this.container = null; } if (container) { this.container = container; - this.container.appendChild(this.view); + + this.useFixedPosition = domPosition !== ContextViewDOMPosition.ABSOLUTE; + this.useShadowDOM = domPosition === ContextViewDOMPosition.FIXED_SHADOW; + + if (this.useShadowDOM) { + this.shadowRootHostElement = DOM.$('.shadow-root-host'); + this.container.appendChild(this.shadowRootHostElement); + this.shadowRoot = this.shadowRootHostElement.attachShadow({ mode: 'open' }); + const style = document.createElement('style'); + style.textContent = SHADOW_ROOT_CSS; + this.shadowRoot.appendChild(style); + this.shadowRoot.appendChild(this.view); + this.shadowRoot.appendChild(DOM.$('slot')); + } else { + this.container.appendChild(this.view); + } const toDisposeOnSetContainer = new DisposableStore(); @@ -158,6 +194,8 @@ export class ContextView extends Disposable { this.view.className = 'context-view'; this.view.style.top = '0px'; this.view.style.left = '0px'; + this.view.style.zIndex = '2500'; + this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute'; DOM.show(this.view); // Render content @@ -175,6 +213,10 @@ export class ContextView extends Disposable { } } + getViewElement(): HTMLElement { + return this.view; + } + layout(): void { if (!this.isVisible()) { return; @@ -251,13 +293,14 @@ export class ContextView extends Disposable { const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); - DOM.removeClasses(this.view, 'top', 'bottom', 'left', 'right'); - DOM.addClass(this.view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); - DOM.addClass(this.view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); + this.view.classList.remove('top', 'bottom', 'left', 'right'); + this.view.classList.add(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); + this.view.classList.add(anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); + this.view.classList.toggle('fixed', this.useFixedPosition); const containerPosition = DOM.getDomNodePagePosition(this.container!); - this.view.style.top = `${top - containerPosition.top}px`; - this.view.style.left = `${left - containerPosition.left}px`; + this.view.style.top = `${top - (this.useFixedPosition ? DOM.getDomNodePagePosition(this.view).top : containerPosition.top)}px`; + this.view.style.left = `${left - (this.useFixedPosition ? DOM.getDomNodePagePosition(this.view).left : containerPosition.left)}px`; this.view.style.width = 'initial'; } @@ -294,3 +337,49 @@ export class ContextView extends Disposable { super.dispose(); } } + +let SHADOW_ROOT_CSS = /* css */ ` + :host { + all: initial; /* 1st rule so subsequent properties are reset. */ + } + + @font-face { + font-family: "codicon"; + src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype"); + } + + .codicon[class*='codicon-'] { + font: normal normal normal 16px/1 codicon; + display: inline-block; + text-decoration: none; + text-rendering: auto; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + } + + :host { + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", system-ui, "Ubuntu", "Droid Sans", sans-serif; + } + + :host-context(.mac) { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } + :host-context(.mac:lang(zh-Hans)) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } + :host-context(.mac:lang(zh-Hant)) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } + :host-context(.mac:lang(ja)) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } + :host-context(.mac:lang(ko)) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } + + :host-context(.windows) { font-family: "Segoe WPC", "Segoe UI", sans-serif; } + :host-context(.windows:lang(zh-Hans)) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } + :host-context(.windows:lang(zh-Hant)) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } + :host-context(.windows:lang(ja)) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; } + :host-context(.windows:lang(ko)) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } + + :host-context(.linux) { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; } + :host-context(.linux:lang(zh-Hans)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } + :host-context(.linux:lang(zh-Hant)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } + :host-context(.linux:lang(ja)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } + :host-context(.linux:lang(ko)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } +`; diff --git a/src/vs/base/browser/ui/countBadge/countBadge.css b/src/vs/base/browser/ui/countBadge/countBadge.css index 909fa7c8e78..eb0c0837ee9 100644 --- a/src/vs/base/browser/ui/countBadge/countBadge.css +++ b/src/vs/base/browser/ui/countBadge/countBadge.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-count-badge { - padding: 3px 5px; + padding: 3px 6px; border-radius: 11px; font-size: 11px; min-width: 18px; @@ -14,4 +14,11 @@ text-align: center; display: inline-block; box-sizing: border-box; -} \ No newline at end of file +} + +.monaco-count-badge.long { + padding: 2px 3px; + border-radius: 2px; + min-height: auto; + line-height: normal; +} diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 1249a329acf..1f1180b8f3a 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + /** Dialog: Modal Block */ .monaco-dialog-modal-block { position: fixed; @@ -25,7 +26,7 @@ flex-direction: column-reverse; width: min-content; min-width: 500px; - max-width: 90%; + max-width: 90vw; min-height: 75px; padding: 10px; transform: translate3d(0px, 0px, 0px); @@ -46,7 +47,6 @@ margin-left: 4px; } - /** Dialog: Message Row */ .monaco-dialog-box .dialog-message-row { display: flex; @@ -55,7 +55,7 @@ padding: 0 10px; } -.monaco-dialog-box .dialog-message-row > .codicon { +.monaco-dialog-box .dialog-message-row > .dialog-icon.codicon { flex: 0 0 48px; height: 48px; align-self: baseline; @@ -93,7 +93,6 @@ .monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-detail { line-height: 22px; flex: 1; /* let the message always grow */ - opacity: .9; } .monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message a:focus { @@ -101,6 +100,7 @@ outline-style: solid; } +/** Dialog: Checkbox */ .monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-checkbox-row { padding: 15px 0px 0px; display: flex; @@ -113,6 +113,16 @@ -ms-user-select: none; } +/** Dialog: Input */ +.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-input { + padding: 15px 0px 0px; + display: flex; +} + +.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-input .monaco-inputbox { + flex: 1; +} + /** Dialog: Buttons Row */ .monaco-dialog-box > .dialog-buttons-row { display: flex; diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 46190302d98..097e879e30d 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -6,7 +6,7 @@ import 'vs/css!./dialog'; import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; -import { $, hide, show, EventHelper, clearNode, removeClasses, addClass, addClasses, removeNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { $, hide, show, EventHelper, clearNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -17,57 +17,79 @@ import { Action } from 'vs/base/common/actions'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { SimpleCheckbox, ISimpleCheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; + +export interface IDialogInputOptions { + readonly placeholder?: string; + readonly type?: 'text' | 'password'; + readonly value?: string; +} export interface IDialogOptions { - cancelId?: number; - detail?: string; - checkboxLabel?: string; - checkboxChecked?: boolean; - type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending'; - keyEventProcessor?: (event: StandardKeyboardEvent) => void; + readonly cancelId?: number; + readonly detail?: string; + readonly checkboxLabel?: string; + readonly checkboxChecked?: boolean; + readonly type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending'; + readonly inputs?: IDialogInputOptions[]; + readonly keyEventProcessor?: (event: StandardKeyboardEvent) => void; } export interface IDialogResult { - button: number; - checkboxChecked?: boolean; + readonly button: number; + readonly checkboxChecked?: boolean; + readonly values?: string[]; } export interface IDialogStyles extends IButtonStyles, ISimpleCheckboxStyles { - dialogForeground?: Color; - dialogBackground?: Color; - dialogShadow?: Color; - dialogBorder?: Color; + readonly dialogForeground?: Color; + readonly dialogBackground?: Color; + readonly dialogShadow?: Color; + readonly dialogBorder?: Color; + readonly errorIconForeground?: Color; + readonly warningIconForeground?: Color; + readonly infoIconForeground?: Color; + readonly inputBackground?: Color; + readonly inputForeground?: Color; + readonly inputBorder?: Color; } interface ButtonMapEntry { - label: string; - index: number; + readonly label: string; + readonly index: number; } +const dialogErrorIcon = registerIcon('dialog-error', Codicon.error); +const dialogWarningIcon = registerIcon('dialog-warning', Codicon.warning); +const dialogInfoIcon = registerIcon('dialog-info', Codicon.info); +const dialogCloseIcon = registerIcon('dialog-close', Codicon.close); + export class Dialog extends Disposable { - private element: HTMLElement | undefined; - private shadowElement: HTMLElement | undefined; - private modal: HTMLElement | undefined; - private buttonsContainer: HTMLElement | undefined; - private messageDetailElement: HTMLElement | undefined; - private iconElement: HTMLElement | undefined; - private checkbox: SimpleCheckbox | undefined; - private toolbarContainer: HTMLElement | undefined; + private readonly element: HTMLElement; + private readonly shadowElement: HTMLElement; + private modalElement: HTMLElement | undefined; + private readonly buttonsContainer: HTMLElement; + private readonly messageDetailElement: HTMLElement; + private readonly iconElement: HTMLElement; + private readonly checkbox: SimpleCheckbox | undefined; + private readonly toolbarContainer: HTMLElement; private buttonGroup: ButtonGroup | undefined; private styles: IDialogStyles | undefined; private focusToReturn: HTMLElement | undefined; - private checkboxHasFocus: boolean = false; - private buttons: string[]; + private readonly inputs: InputBox[]; + private readonly buttons: string[]; constructor(private container: HTMLElement, private message: string, buttons: string[], private options: IDialogOptions) { super(); - this.modal = this.container.appendChild($(`.monaco-dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`)); - this.shadowElement = this.modal.appendChild($('.dialog-shadow')); + + this.modalElement = this.container.appendChild($(`.monaco-dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`)); + this.shadowElement = this.modalElement.appendChild($('.dialog-shadow')); this.element = this.shadowElement.appendChild($('.monaco-dialog-box')); + this.element.setAttribute('role', 'dialog'); hide(this.element); - // If no button is provided, default to OK - this.buttons = buttons.length ? buttons : [nls.localize('ok', "OK")]; + this.buttons = buttons.length ? buttons : [nls.localize('ok', "OK")]; // If no button is provided, default to OK const buttonsRowElement = this.element.appendChild($('.dialog-buttons-row')); this.buttonsContainer = buttonsRowElement.appendChild($('.dialog-buttons')); @@ -84,6 +106,25 @@ export class Dialog extends Disposable { this.messageDetailElement = messageContainer.appendChild($('.dialog-message-detail')); this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message; + if (this.options.inputs) { + this.inputs = this.options.inputs.map(input => { + const inputRowElement = messageContainer.appendChild($('.dialog-message-input')); + + const inputBox = this._register(new InputBox(inputRowElement, undefined, { + placeholder: input.placeholder, + type: input.type ?? 'text', + })); + + if (input.value) { + inputBox.value = input.value; + } + + return inputBox; + }); + } else { + this.inputs = []; + } + if (this.options.checkboxLabel) { const checkboxRowElement = messageContainer.appendChild($('.dialog-checkbox-row')); @@ -100,76 +141,135 @@ export class Dialog extends Disposable { this.toolbarContainer = toolbarRowElement.appendChild($('.dialog-toolbar')); } - updateMessage(message: string): void { - if (this.messageDetailElement) { - this.messageDetailElement.innerText = message; + private getAriaLabel(): string { + let typeLabel = nls.localize('dialogInfoMessage', 'Info'); + switch (this.options.type) { + case 'error': + nls.localize('dialogErrorMessage', 'Error'); + break; + case 'warning': + nls.localize('dialogWarningMessage', 'Warning'); + break; + case 'pending': + nls.localize('dialogPendingMessage', 'In Progress'); + break; + case 'none': + case 'info': + case 'question': + default: + break; } + + return `${typeLabel}: ${this.message} ${this.options.detail || ''}`; + } + + updateMessage(message: string): void { + this.messageDetailElement.innerText = message; } async show(): Promise { this.focusToReturn = document.activeElement as HTMLElement; return new Promise((resolve) => { - if (!this.element || !this.buttonsContainer || !this.iconElement || !this.toolbarContainer) { - resolve({ button: 0 }); - return; - } - clearNode(this.buttonsContainer); - let focusedButton = 0; - const buttonGroup = this.buttonGroup = new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true }); + const buttonGroup = this.buttonGroup = this._register(new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true })); const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId); - // Set focused button to UI index - buttonMap.forEach((value, index) => { - if (value.index === 0) { - focusedButton = index; - } - }); - + // Handle button clicks buttonGroup.buttons.forEach((button, index) => { button.label = mnemonicButtonLabel(buttonMap[index].label, true); this._register(button.onDidClick(e => { EventHelper.stop(e); - resolve({ button: buttonMap[index].index, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined }); + + resolve({ + button: buttonMap[index].index, + checkboxChecked: this.checkbox ? this.checkbox.checked : undefined, + values: this.inputs.length > 0 ? this.inputs.map(input => input.value) : undefined + }); })); }); + // Handle keyboard events gloably: Tab, Arrow-Left/Right this._register(domEvent(window, 'keydown', true)((e: KeyboardEvent) => { const evt = new StandardKeyboardEvent(e); - if (evt.equals(KeyCode.Enter) || evt.equals(KeyCode.Space)) { - return; + + if (evt.equals(KeyCode.Enter)) { + + // Enter in input field should OK the dialog + if (this.inputs.some(input => input.hasFocus())) { + EventHelper.stop(e); + + resolve({ + button: buttonMap.find(button => button.index !== this.options.cancelId)?.index ?? 0, + checkboxChecked: this.checkbox ? this.checkbox.checked : undefined, + values: this.inputs.length > 0 ? this.inputs.map(input => input.value) : undefined + }); + } + + return; // leave default handling + } + + if (evt.equals(KeyCode.Space)) { + return; // leave default handling } let eventHandled = false; - if (evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { - if (!this.checkboxHasFocus && focusedButton === 0) { - if (this.checkbox) { - this.checkbox.domNode.focus(); + + // Focus: Next / Previous + if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow) || evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { + + // Build a list of focusable elements in their visual order + const focusableElements: { focus: () => void }[] = []; + let focusedIndex = -1; + for (const input of this.inputs) { + focusableElements.push(input); + if (input.hasFocus()) { + focusedIndex = focusableElements.length - 1; } - this.checkboxHasFocus = true; - } else { - focusedButton = (this.checkboxHasFocus ? 0 : focusedButton) + buttonGroup.buttons.length - 1; - focusedButton = focusedButton % buttonGroup.buttons.length; - buttonGroup.buttons[focusedButton].focus(); - this.checkboxHasFocus = false; } - eventHandled = true; - } else if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { - if (!this.checkboxHasFocus && focusedButton === buttonGroup.buttons.length - 1) { - if (this.checkbox) { - this.checkbox.domNode.focus(); + if (this.checkbox) { + focusableElements.push(this.checkbox); + if (this.checkbox.hasFocus()) { + focusedIndex = focusableElements.length - 1; } - this.checkboxHasFocus = true; - } else { - focusedButton = this.checkboxHasFocus ? 0 : focusedButton + 1; - focusedButton = focusedButton % buttonGroup.buttons.length; - buttonGroup.buttons[focusedButton].focus(); - this.checkboxHasFocus = false; } + + if (this.buttonGroup) { + for (const button of this.buttonGroup.buttons) { + focusableElements.push(button); + if (button.hasFocus()) { + focusedIndex = focusableElements.length - 1; + } + } + } + + // Focus next element (with wrapping) + if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { + if (focusedIndex === -1) { + focusedIndex = 0; // default to focus first element if none have focus + } + + const newFocusedIndex = (focusedIndex + 1) % focusableElements.length; + focusableElements[newFocusedIndex].focus(); + } + + // Focus previous element (with wrapping) + else { + if (focusedIndex === -1) { + focusedIndex = focusableElements.length; // default to focus last element if none have focus + } + + let newFocusedIndex = focusedIndex - 1; + if (newFocusedIndex === -1) { + newFocusedIndex = focusableElements.length - 1; + } + + focusableElements[newFocusedIndex].focus(); + } + eventHandled = true; } @@ -185,10 +285,14 @@ export class Dialog extends Disposable { const evt = new StandardKeyboardEvent(e); if (evt.equals(KeyCode.Escape)) { - resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined }); + resolve({ + button: this.options.cancelId || 0, + checkboxChecked: this.checkbox ? this.checkbox.checked : undefined + }); } })); + // Detect focus out this._register(domEvent(this.element, 'focusout', false)((e: FocusEvent) => { if (!!e.relatedTarget && !!this.element) { if (!isAncestor(e.relatedTarget as HTMLElement, this.element)) { @@ -202,43 +306,53 @@ export class Dialog extends Disposable { } })); - addClass(this.iconElement, 'codicon'); - removeClasses(this.iconElement, 'codicon-alert', 'codicon-warning', 'codicon-info'); + this.iconElement.classList.remove(...dialogErrorIcon.classNamesArray, ...dialogWarningIcon.classNamesArray, ...dialogInfoIcon.classNamesArray, ...Codicon.loading.classNamesArray); switch (this.options.type) { case 'error': - addClass(this.iconElement, 'codicon-error'); + this.iconElement.classList.add(...dialogErrorIcon.classNamesArray); break; case 'warning': - addClass(this.iconElement, 'codicon-warning'); + this.iconElement.classList.add(...dialogWarningIcon.classNamesArray); break; case 'pending': - addClasses(this.iconElement, 'codicon-loading', 'codicon-animation-spin'); + this.iconElement.classList.add(...Codicon.loading.classNamesArray, 'codicon-animation-spin'); break; case 'none': case 'info': case 'question': default: - addClass(this.iconElement, 'codicon-info'); + this.iconElement.classList.add(...dialogInfoIcon.classNamesArray); break; } - const actionBar = new ActionBar(this.toolbarContainer, {}); + const actionBar = this._register(new ActionBar(this.toolbarContainer, {})); - const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), 'codicon codicon-close', true, () => { - resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined }); - return Promise.resolve(); - }); + const action = this._register(new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, async () => { + resolve({ + button: this.options.cancelId || 0, + checkboxChecked: this.checkbox ? this.checkbox.checked : undefined + }); + })); actionBar.push(action, { icon: true, label: false, }); this.applyStyles(); - this.element.setAttribute('aria-label', this.message); + this.element.setAttribute('aria-label', this.getAriaLabel()); show(this.element); - // Focus first element - buttonGroup.buttons[focusedButton].focus(); + // Focus first element (input or button) + if (this.inputs.length > 0) { + this.inputs[0].focus(); + this.inputs[0].select(); + } else { + buttonMap.forEach((value, index) => { + if (value.index === 0) { + buttonGroup.buttons[index].focus(); + } + }); + } }); } @@ -246,41 +360,64 @@ export class Dialog extends Disposable { if (this.styles) { const style = this.styles; - const fgColor = style.dialogForeground ? `${style.dialogForeground}` : ''; - const bgColor = style.dialogBackground ? `${style.dialogBackground}` : ''; + const fgColor = style.dialogForeground; + const bgColor = style.dialogBackground; const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : ''; const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : ''; - if (this.shadowElement) { - this.shadowElement.style.boxShadow = shadowColor; + this.shadowElement.style.boxShadow = shadowColor; + + this.element.style.color = fgColor?.toString() ?? ''; + this.element.style.backgroundColor = bgColor?.toString() ?? ''; + this.element.style.border = border; + + if (this.buttonGroup) { + this.buttonGroup.buttons.forEach(button => button.style(style)); } - if (this.element) { - this.element.style.color = fgColor; - this.element.style.backgroundColor = bgColor; - this.element.style.border = border; + if (this.checkbox) { + this.checkbox.style(style); + } - if (this.buttonGroup) { - this.buttonGroup.buttons.forEach(button => button.style(style)); - } + if (fgColor && bgColor) { + const messageDetailColor = fgColor.transparent(.9); + this.messageDetailElement.style.color = messageDetailColor.makeOpaque(bgColor).toString(); + } - if (this.checkbox) { - this.checkbox.style(style); - } + let color; + switch (this.options.type) { + case 'error': + color = style.errorIconForeground; + break; + case 'warning': + color = style.warningIconForeground; + break; + default: + color = style.infoIconForeground; + break; + } + if (color) { + this.iconElement.style.color = color.toString(); + } + + for (const input of this.inputs) { + input.style(style); } } } style(style: IDialogStyles): void { this.styles = style; + this.applyStyles(); } dispose(): void { super.dispose(); - if (this.modal) { - removeNode(this.modal); - this.modal = undefined; + + if (this.modalElement) { + this.modalElement.remove(); + this.modalElement = undefined; } if (this.focusToReturn && isAncestor(this.focusToReturn, document.body)) { @@ -291,9 +428,10 @@ export class Dialog extends Disposable { private rearrangeButtons(buttons: Array, cancelId: number | undefined): ButtonMapEntry[] { const buttonMap: ButtonMapEntry[] = []; + // Maps each button to its current label and old index so that when we move them around it's not a problem buttons.forEach((button, index) => { - buttonMap.push({ label: button, index: index }); + buttonMap.push({ label: button, index }); }); // macOS/linux: reverse button order diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index c36015f710b..9abc9274c1f 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -5,15 +5,15 @@ import 'vs/css!./dropdown'; import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch'; -import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; -import { BaseActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IContextViewProvider, IAnchor, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; -import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; -import { EventHelper, EventType, removeClass, addClass, append, $, addDisposableListener, addClasses } from 'vs/base/browser/dom'; -import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { EventHelper, EventType, append, $, addDisposableListener, DOMEvent } from 'vs/base/browser/dom'; +import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Emitter } from 'vs/base/common/event'; export interface ILabelRenderer { (container: HTMLElement): IDisposable | null; @@ -29,7 +29,10 @@ export class BaseDropdown extends ActionRunner { private boxContainer?: HTMLElement; private _label?: HTMLElement; private contents?: HTMLElement; + private visible: boolean | undefined; + private _onDidChangeVisibility = new Emitter(); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; constructor(container: HTMLElement, options: IBaseDropdownOptions) { super(); @@ -48,13 +51,13 @@ export class BaseDropdown extends ActionRunner { } for (const event of [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap]) { - this._register(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger + this._register(addDisposableListener(this.element, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger } for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) { this._register(addDisposableListener(this._label, event, e => { if (e instanceof MouseEvent && e.detail > 1) { - return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363) + return; // prevent multiple clicks to open multiple context menus (https://github.com/microsoft/vscode/issues/41363) } if (this.visible) { @@ -68,7 +71,7 @@ export class BaseDropdown extends ActionRunner { this._register(addDisposableListener(this._label, EventType.KEY_UP, e => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - EventHelper.stop(e, true); // https://github.com/Microsoft/vscode/issues/57997 + EventHelper.stop(e, true); // https://github.com/microsoft/vscode/issues/57997 if (this.visible) { this.hide(); @@ -101,18 +104,24 @@ export class BaseDropdown extends ActionRunner { } show(): void { - this.visible = true; + if (!this.visible) { + this.visible = true; + this._onDidChangeVisibility.fire(true); + } } hide(): void { - this.visible = false; + if (this.visible) { + this.visible = false; + this._onDidChangeVisibility.fire(false); + } } isVisible(): boolean { return !!this.visible; } - protected onEvent(e: Event, activeElement: HTMLElement): void { + protected onEvent(e: DOMEvent, activeElement: HTMLElement): void { this.hide(); } @@ -153,7 +162,7 @@ export class Dropdown extends BaseDropdown { show(): void { super.show(); - addClass(this.element, 'active'); + this.element.classList.add('active'); this.contextViewProvider.showContextView({ getAnchor: () => this.getAnchor(), @@ -175,7 +184,7 @@ export class Dropdown extends BaseDropdown { } protected onHide(): void { - removeClass(this.element, 'active'); + this.element.classList.remove('active'); } hide(): void { @@ -191,27 +200,25 @@ export class Dropdown extends BaseDropdown { } } -export interface IContextMenuProvider { - showContextMenu(delegate: IContextMenuDelegate): void; -} - export interface IActionProvider { - getActions(): ReadonlyArray; + getActions(): IAction[]; } export interface IDropdownMenuOptions extends IBaseDropdownOptions { contextMenuProvider: IContextMenuProvider; - actions?: ReadonlyArray; - actionProvider?: IActionProvider; + readonly actions?: IAction[]; + readonly actionProvider?: IActionProvider; menuClassName?: string; + menuAsChild?: boolean; // scope down for #99448 } export class DropdownMenu extends BaseDropdown { private _contextMenuProvider: IContextMenuProvider; private _menuOptions: IMenuOptions | undefined; - private _actions: ReadonlyArray = []; + private _actions: IAction[] = []; private actionProvider?: IActionProvider; private menuClassName: string; + private menuAsChild?: boolean; constructor(container: HTMLElement, options: IDropdownMenuOptions) { super(container, options); @@ -220,6 +227,7 @@ export class DropdownMenu extends BaseDropdown { this.actions = options.actions || []; this.actionProvider = options.actionProvider; this.menuClassName = options.menuClassName || ''; + this.menuAsChild = !!options.menuAsChild; } set menuOptions(options: IMenuOptions | undefined) { @@ -230,7 +238,7 @@ export class DropdownMenu extends BaseDropdown { return this._menuOptions; } - private get actions(): ReadonlyArray { + private get actions(): IAction[] { if (this.actionProvider) { return this.actionProvider.getActions(); } @@ -238,14 +246,14 @@ export class DropdownMenu extends BaseDropdown { return this._actions; } - private set actions(actions: ReadonlyArray) { + private set actions(actions: IAction[]) { this._actions = actions; } show(): void { super.show(); - addClass(this.element, 'active'); + this.element.classList.add('active'); this._contextMenuProvider.showContextMenu({ getAnchor: () => this.element, @@ -256,7 +264,8 @@ export class DropdownMenu extends BaseDropdown { getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined, - anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT + anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT, + domForShadowRoot: this.menuAsChild ? this.element : undefined }); } @@ -266,96 +275,6 @@ export class DropdownMenu extends BaseDropdown { private onHide(): void { this.hide(); - removeClass(this.element, 'active'); - } -} - -export class DropdownMenuActionViewItem extends BaseActionViewItem { - private menuActionsOrProvider: any; - private dropdownMenu: DropdownMenu | undefined; - private contextMenuProvider: IContextMenuProvider; - private actionViewItemProvider?: IActionViewItemProvider; - private keybindings?: (action: IAction) => ResolvedKeybinding | undefined; - private clazz: string | undefined; - private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; - - constructor(action: IAction, menuActions: ReadonlyArray, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, menuActionsOrProvider: ReadonlyArray | IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment) { - super(null, action); - - this.menuActionsOrProvider = menuActionsOrProvider; - this.contextMenuProvider = contextMenuProvider; - this.actionViewItemProvider = actionViewItemProvider; - this.actionRunner = actionRunner; - this.keybindings = keybindings; - this.clazz = clazz; - this.anchorAlignmentProvider = anchorAlignmentProvider; - } - - render(container: HTMLElement): void { - const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { - this.element = append(el, $('a.action-label.codicon')); - if (this.clazz) { - addClasses(this.element, this.clazz); - } - - this.element.tabIndex = 0; - this.element.setAttribute('role', 'button'); - this.element.setAttribute('aria-haspopup', 'true'); - this.element.title = this._action.label || ''; - - return null; - }; - - const options: IDropdownMenuOptions = { - contextMenuProvider: this.contextMenuProvider, - labelRenderer: labelRenderer - }; - - // Render the DropdownMenu around a simple action to toggle it - if (Array.isArray(this.menuActionsOrProvider)) { - options.actions = this.menuActionsOrProvider; - } else { - options.actionProvider = this.menuActionsOrProvider; - } - - this.dropdownMenu = this._register(new DropdownMenu(container, options)); - - this.dropdownMenu.menuOptions = { - actionViewItemProvider: this.actionViewItemProvider, - actionRunner: this.actionRunner, - getKeyBinding: this.keybindings, - context: this._context - }; - - if (this.anchorAlignmentProvider) { - const that = this; - - this.dropdownMenu.menuOptions = { - ...this.dropdownMenu.menuOptions, - get anchorAlignment(): AnchorAlignment { - return that.anchorAlignmentProvider!(); - } - }; - } - } - - setActionContext(newContext: any): void { - super.setActionContext(newContext); - - if (this.dropdownMenu) { - if (this.dropdownMenu.menuOptions) { - this.dropdownMenu.menuOptions.context = newContext; - } else { - this.dropdownMenu.menuOptions = { context: newContext }; - } - } - } - - show(): void { - if (this.dropdownMenu) { - this.dropdownMenu.show(); - } + this.element.classList.remove('active'); } } diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts new file mode 100644 index 00000000000..5713a8a9337 --- /dev/null +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./dropdown'; +import { Action, IAction, IActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { append, $ } from 'vs/base/browser/dom'; +import { Emitter } from 'vs/base/common/event'; +import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; +import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; + +export interface IKeybindingProvider { + (action: IAction): ResolvedKeybinding | undefined; +} + +export interface IAnchorAlignmentProvider { + (): AnchorAlignment; +} + +export interface IDropdownMenuActionViewItemOptions extends IBaseActionViewItemOptions { + readonly actionViewItemProvider?: IActionViewItemProvider; + readonly keybindingProvider?: IKeybindingProvider; + readonly actionRunner?: IActionRunner; + readonly classNames?: string[] | string; + readonly anchorAlignmentProvider?: IAnchorAlignmentProvider; + readonly menuAsChild?: boolean; +} + +export class DropdownMenuActionViewItem extends BaseActionViewItem { + private menuActionsOrProvider: readonly IAction[] | IActionProvider; + private dropdownMenu: DropdownMenu | undefined; + private contextMenuProvider: IContextMenuProvider; + + private _onDidChangeVisibility = this._register(new Emitter()); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + + constructor( + action: IAction, + menuActionsOrProvider: readonly IAction[] | IActionProvider, + contextMenuProvider: IContextMenuProvider, + protected options: IDropdownMenuActionViewItemOptions = {} + ) { + super(null, action, options); + + this.menuActionsOrProvider = menuActionsOrProvider; + this.contextMenuProvider = contextMenuProvider; + + if (this.options.actionRunner) { + this.actionRunner = this.options.actionRunner; + } + } + + render(container: HTMLElement): void { + const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { + this.element = append(el, $('a.action-label')); + + let classNames: string[] = []; + + if (typeof this.options.classNames === 'string') { + classNames = this.options.classNames.split(/\s+/g).filter(s => !!s); + } else if (this.options.classNames) { + classNames = this.options.classNames; + } + + // todo@aeschli: remove codicon, should come through `this.options.classNames` + if (!classNames.find(c => c === 'icon')) { + classNames.push('codicon'); + } + + this.element.classList.add(...classNames); + + this.element.tabIndex = 0; + this.element.setAttribute('role', 'button'); + this.element.setAttribute('aria-haspopup', 'true'); + this.element.setAttribute('aria-expanded', 'false'); + this.element.title = this._action.label || ''; + + return null; + }; + + const isActionsArray = Array.isArray(this.menuActionsOrProvider); + const options: IDropdownMenuOptions = { + contextMenuProvider: this.contextMenuProvider, + labelRenderer: labelRenderer, + menuAsChild: this.options.menuAsChild, + actions: isActionsArray ? this.menuActionsOrProvider as IAction[] : undefined, + actionProvider: isActionsArray ? undefined : this.menuActionsOrProvider as IActionProvider + }; + + this.dropdownMenu = this._register(new DropdownMenu(container, options)); + this._register(this.dropdownMenu.onDidChangeVisibility(visible => { + this.element?.setAttribute('aria-expanded', `${visible}`); + this._onDidChangeVisibility.fire(visible); + })); + + this.dropdownMenu.menuOptions = { + actionViewItemProvider: this.options.actionViewItemProvider, + actionRunner: this.actionRunner, + getKeyBinding: this.options.keybindingProvider, + context: this._context + }; + + if (this.options.anchorAlignmentProvider) { + const that = this; + + this.dropdownMenu.menuOptions = { + ...this.dropdownMenu.menuOptions, + get anchorAlignment(): AnchorAlignment { + return that.options.anchorAlignmentProvider!(); + } + }; + } + } + + setActionContext(newContext: unknown): void { + super.setActionContext(newContext); + + if (this.dropdownMenu) { + if (this.dropdownMenu.menuOptions) { + this.dropdownMenu.menuOptions.context = newContext; + } else { + this.dropdownMenu.menuOptions = { context: newContext }; + } + } + } + + show(): void { + if (this.dropdownMenu) { + this.dropdownMenu.show(); + } + } +} + +export interface IActionWithDropdownActionViewItemOptions extends IActionViewItemOptions { + readonly menuActionsOrProvider: readonly IAction[] | IActionProvider; + readonly menuActionClassNames?: string[]; +} + +export class ActionWithDropdownActionViewItem extends ActionViewItem { + + protected dropdownMenuActionViewItem: DropdownMenuActionViewItem | undefined; + + constructor( + context: unknown, + action: IAction, + options: IActionWithDropdownActionViewItemOptions, + private readonly contextMenuProvider: IContextMenuProvider + ) { + super(context, action, options); + } + + render(container: HTMLElement): void { + super.render(container); + if (this.element) { + this.element.classList.add('action-dropdown-item'); + this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(new Action('dropdownAction', undefined), (this.options).menuActionsOrProvider, this.contextMenuProvider, { classNames: ['dropdown', 'codicon-chevron-down', ...(this.options).menuActionClassNames || []] }); + this.dropdownMenuActionViewItem.render(this.element); + } + } + +} diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 4c5392c87dc..cf7db18c20b 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -35,6 +35,7 @@ export interface IFindInputOptions extends IFindInputStyles { export interface IFindInputStyles extends IInputBoxStyles { inputActiveOptionBorder?: Color; + inputActiveOptionForeground?: Color; inputActiveOptionBackground?: Color; } @@ -51,6 +52,7 @@ export class FindInput extends Widget { private fixFocusOnOptionClickEnabled = true; private inputActiveOptionBorder?: Color; + private inputActiveOptionForeground?: Color; private inputActiveOptionBackground?: Color; private inputBackground?: Color; private inputForeground?: Color; @@ -101,6 +103,7 @@ export class FindInput extends Widget { this.label = options.label || NLS_DEFAULT_LABEL; this.inputActiveOptionBorder = options.inputActiveOptionBorder; + this.inputActiveOptionForeground = options.inputActiveOptionForeground; this.inputActiveOptionBackground = options.inputActiveOptionBackground; this.inputBackground = options.inputBackground; this.inputForeground = options.inputForeground; @@ -125,7 +128,7 @@ export class FindInput extends Widget { const flexibleMaxHeight = options.flexibleMaxHeight; this.domNode = document.createElement('div'); - dom.addClass(this.domNode, 'monaco-findInput'); + this.domNode.classList.add('monaco-findInput'); this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, { placeholder: this.placeholder || '', @@ -155,6 +158,7 @@ export class FindInput extends Widget { appendTitle: appendRegexLabel, isChecked: false, inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground })); this._register(this.regex.onChange(viaKeyboard => { @@ -172,6 +176,7 @@ export class FindInput extends Widget { appendTitle: appendWholeWordsLabel, isChecked: false, inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground })); this._register(this.wholeWords.onChange(viaKeyboard => { @@ -186,6 +191,7 @@ export class FindInput extends Widget { appendTitle: appendCaseSensitiveLabel, isChecked: false, inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground })); this._register(this.caseSensitive.onChange(viaKeyboard => { @@ -222,6 +228,7 @@ export class FindInput extends Widget { if (event.equals(KeyCode.Escape)) { indexes[index].blur(); + this.inputBox.focus(); } else if (newIndex >= 0) { indexes[newIndex].focus(); } @@ -252,7 +259,7 @@ export class FindInput extends Widget { } public enable(): void { - dom.removeClass(this.domNode, 'disabled'); + this.domNode.classList.remove('disabled'); this.inputBox.enable(); this.regex.enable(); this.wholeWords.enable(); @@ -260,7 +267,7 @@ export class FindInput extends Widget { } public disable(): void { - dom.addClass(this.domNode, 'disabled'); + this.domNode.classList.add('disabled'); this.inputBox.disable(); this.regex.disable(); this.wholeWords.disable(); @@ -301,6 +308,7 @@ export class FindInput extends Widget { public style(styles: IFindInputStyles): void { this.inputActiveOptionBorder = styles.inputActiveOptionBorder; + this.inputActiveOptionForeground = styles.inputActiveOptionForeground; this.inputActiveOptionBackground = styles.inputActiveOptionBackground; this.inputBackground = styles.inputBackground; this.inputForeground = styles.inputForeground; @@ -323,6 +331,7 @@ export class FindInput extends Widget { if (this.domNode) { const checkBoxStyles: ICheckboxStyles = { inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground, }; this.regex.style(checkBoxStyles); @@ -390,9 +399,9 @@ export class FindInput extends Widget { private _lastHighlightFindOptions: number = 0; public highlightFindOptions(): void { - dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); + this.domNode.classList.remove('highlight-' + (this._lastHighlightFindOptions)); this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions; - dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); + this.domNode.classList.add('highlight-' + (this._lastHighlightFindOptions)); } public validate(): void { diff --git a/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts b/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts index b7ca14b0d7a..07e80f78c7f 100644 --- a/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts +++ b/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts @@ -6,11 +6,13 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { Color } from 'vs/base/common/color'; import * as nls from 'vs/nls'; +import { Codicon } from 'vs/base/common/codicons'; export interface IFindInputCheckboxOpts { readonly appendTitle: string; readonly isChecked: boolean; readonly inputActiveOptionBorder?: Color; + readonly inputActiveOptionForeground?: Color; readonly inputActiveOptionBackground?: Color; } @@ -21,10 +23,11 @@ const NLS_REGEX_CHECKBOX_LABEL = nls.localize('regexDescription', "Use Regular E export class CaseSensitiveCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ - actionClassName: 'codicon-case-sensitive', + icon: Codicon.caseSensitive, title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } @@ -33,10 +36,11 @@ export class CaseSensitiveCheckbox extends Checkbox { export class WholeWordsCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ - actionClassName: 'codicon-whole-word', + icon: Codicon.wholeWord, title: NLS_WHOLE_WORD_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } @@ -45,10 +49,11 @@ export class WholeWordsCheckbox extends Checkbox { export class RegexCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ - actionClassName: 'codicon-regex', + icon: Codicon.regex, title: NLS_REGEX_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 5950e261de4..1d6200fdb85 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -17,6 +17,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; import { ICheckboxStyles, Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IFindInputCheckboxOpts } from 'vs/base/browser/ui/findinput/findInputCheckboxes'; +import { Codicon } from 'vs/base/common/codicons'; export interface IReplaceInputOptions extends IReplaceInputStyles { readonly placeholder?: string; @@ -27,11 +28,13 @@ export interface IReplaceInputOptions extends IReplaceInputStyles { readonly flexibleWidth?: boolean; readonly flexibleMaxHeight?: number; + readonly appendPreserveCaseLabel?: string; readonly history?: string[]; } export interface IReplaceInputStyles extends IInputBoxStyles { inputActiveOptionBorder?: Color; + inputActiveOptionForeground?: Color; inputActiveOptionBackground?: Color; } @@ -42,10 +45,11 @@ export class PreserveCaseCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ // TODO: does this need its own icon? - actionClassName: 'codicon-preserve-case', + icon: Codicon.preserveCase, title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } @@ -62,6 +66,7 @@ export class ReplaceInput extends Widget { private fixFocusOnOptionClickEnabled = true; private inputActiveOptionBorder?: Color; + private inputActiveOptionForeground?: Color; private inputActiveOptionBackground?: Color; private inputBackground?: Color; private inputForeground?: Color; @@ -108,6 +113,7 @@ export class ReplaceInput extends Widget { this.label = options.label || NLS_DEFAULT_LABEL; this.inputActiveOptionBorder = options.inputActiveOptionBorder; + this.inputActiveOptionForeground = options.inputActiveOptionForeground; this.inputActiveOptionBackground = options.inputActiveOptionBackground; this.inputBackground = options.inputBackground; this.inputForeground = options.inputForeground; @@ -123,13 +129,14 @@ export class ReplaceInput extends Widget { this.inputValidationErrorBackground = options.inputValidationErrorBackground; this.inputValidationErrorForeground = options.inputValidationErrorForeground; + const appendPreserveCaseLabel = options.appendPreserveCaseLabel || ''; const history = options.history || []; const flexibleHeight = !!options.flexibleHeight; const flexibleWidth = !!options.flexibleWidth; const flexibleMaxHeight = options.flexibleMaxHeight; this.domNode = document.createElement('div'); - dom.addClass(this.domNode, 'monaco-findInput'); + this.domNode.classList.add('monaco-findInput'); this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, { ariaLabel: this.label || '', @@ -156,9 +163,10 @@ export class ReplaceInput extends Widget { })); this.preserveCase = this._register(new PreserveCaseCheckbox({ - appendTitle: '', + appendTitle: appendPreserveCaseLabel, isChecked: false, inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground, })); this._register(this.preserveCase.onChange(viaKeyboard => { @@ -197,6 +205,7 @@ export class ReplaceInput extends Widget { if (event.equals(KeyCode.Escape)) { indexes[index].blur(); + this.inputBox.focus(); } else if (newIndex >= 0) { indexes[newIndex].focus(); } @@ -225,13 +234,13 @@ export class ReplaceInput extends Widget { } public enable(): void { - dom.removeClass(this.domNode, 'disabled'); + this.domNode.classList.remove('disabled'); this.inputBox.enable(); this.preserveCase.enable(); } public disable(): void { - dom.addClass(this.domNode, 'disabled'); + this.domNode.classList.add('disabled'); this.inputBox.disable(); this.preserveCase.disable(); } @@ -270,6 +279,7 @@ export class ReplaceInput extends Widget { public style(styles: IReplaceInputStyles): void { this.inputActiveOptionBorder = styles.inputActiveOptionBorder; + this.inputActiveOptionForeground = styles.inputActiveOptionForeground; this.inputActiveOptionBackground = styles.inputActiveOptionBackground; this.inputBackground = styles.inputBackground; this.inputForeground = styles.inputForeground; @@ -292,6 +302,7 @@ export class ReplaceInput extends Widget { if (this.domNode) { const checkBoxStyles: ICheckboxStyles = { inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground, }; this.preserveCase.style(checkBoxStyles); @@ -336,9 +347,9 @@ export class ReplaceInput extends Widget { private _lastHighlightFindOptions: number = 0; public highlightFindOptions(): void { - dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); + this.domNode.classList.remove('highlight-' + (this._lastHighlightFindOptions)); this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions; - dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); + this.domNode.classList.add('highlight-' + (this._lastHighlightFindOptions)); } public validate(): void { diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 336f2ff4a21..22d8a75e912 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -7,10 +7,10 @@ import 'vs/css!./gridview'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Disposable } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; -import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions } from './gridview'; +import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions, IBoundarySashes } from './gridview'; import { Event } from 'vs/base/common/event'; -export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview'; +export { Orientation, IViewSize, orthogonal, LayoutPriority } from './gridview'; export const enum Direction { Up, @@ -175,7 +175,7 @@ function getGridLocation(element: HTMLElement): number[] { } const index = indexInParent(parentElement); - const ancestor = parentElement.parentElement!.parentElement!.parentElement!; + const ancestor = parentElement.parentElement!.parentElement!.parentElement!.parentElement!; return [...getGridLocation(ancestor), index]; } @@ -212,6 +212,11 @@ export class Grid extends Disposable { get maximumHeight(): number { return this.gridview.maximumHeight; } get onDidChange(): Event<{ width: number; height: number; } | undefined> { return this.gridview.onDidChange; } + get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; } + set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; } + + set edgeSnapping(edgeSnapping: boolean) { this.gridview.edgeSnapping = edgeSnapping; } + get element(): HTMLElement { return this.gridview.element; } private didLayout = false; diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 27222583608..323f71fd633 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -21,6 +21,20 @@ export interface IViewSize { readonly height: number; } +interface IRelativeBoundarySashes { + readonly start?: Sash; + readonly end?: Sash; + readonly orthogonalStart?: Sash; + readonly orthogonalEnd?: Sash; +} + +export interface IBoundarySashes { + readonly top?: Sash; + readonly right?: Sash; + readonly bottom?: Sash; + readonly left?: Sash; +} + export interface IView { readonly element: HTMLElement; readonly minimumWidth: number; @@ -32,6 +46,7 @@ export interface IView { readonly snap?: boolean; layout(width: number, height: number, top: number, left: number): void; setVisible?(visible: boolean): void; + setBoundarySashes?(sashes: IBoundarySashes): void; } export interface ISerializableView extends IView { @@ -125,6 +140,22 @@ interface ILayoutContext { readonly absoluteOrthogonalSize: number; } +function toAbsoluteBoundarySashes(sashes: IRelativeBoundarySashes, orientation: Orientation): IBoundarySashes { + if (orientation === Orientation.HORIZONTAL) { + return { left: sashes.start, right: sashes.end, top: sashes.orthogonalStart, bottom: sashes.orthogonalEnd }; + } else { + return { top: sashes.start, bottom: sashes.end, left: sashes.orthogonalStart, right: sashes.orthogonalEnd }; + } +} + +function fromAbsoluteBoundarySashes(sashes: IBoundarySashes, orientation: Orientation): IRelativeBoundarySashes { + if (orientation === Orientation.HORIZONTAL) { + return { start: sashes.left, end: sashes.right, orthogonalStart: sashes.top, orthogonalEnd: sashes.bottom }; + } else { + return { start: sashes.top, end: sashes.bottom, orthogonalStart: sashes.left, orthogonalEnd: sashes.right }; + } +} + class BranchNode implements ISplitView, IDisposable { readonly element: HTMLElement; @@ -139,6 +170,7 @@ class BranchNode implements ISplitView, IDisposable { private absoluteOffset: number = 0; private absoluteOrthogonalOffset: number = 0; + private absoluteOrthogonalSize: number = 0; private _styles: IGridViewStyles; get styles(): IGridViewStyles { return this._styles; } @@ -217,10 +249,45 @@ class BranchNode implements ISplitView, IDisposable { private splitviewSashResetDisposable: IDisposable = Disposable.None; private childrenSashResetDisposable: IDisposable = Disposable.None; - get orthogonalStartSash(): Sash | undefined { return this.splitview.orthogonalStartSash; } - set orthogonalStartSash(sash: Sash | undefined) { this.splitview.orthogonalStartSash = sash; } - get orthogonalEndSash(): Sash | undefined { return this.splitview.orthogonalEndSash; } - set orthogonalEndSash(sash: Sash | undefined) { this.splitview.orthogonalEndSash = sash; } + private _boundarySashes: IRelativeBoundarySashes = {}; + get boundarySashes(): IRelativeBoundarySashes { return this._boundarySashes; } + set boundarySashes(boundarySashes: IRelativeBoundarySashes) { + this._boundarySashes = boundarySashes; + + this.splitview.orthogonalStartSash = boundarySashes.orthogonalStart; + this.splitview.orthogonalEndSash = boundarySashes.orthogonalEnd; + + for (let index = 0; index < this.children.length; index++) { + const child = this.children[index]; + const first = index === 0; + const last = index === this.children.length - 1; + + child.boundarySashes = { + start: boundarySashes.orthogonalStart, + end: boundarySashes.orthogonalEnd, + orthogonalStart: first ? boundarySashes.start : child.boundarySashes.orthogonalStart, + orthogonalEnd: last ? boundarySashes.end : child.boundarySashes.orthogonalEnd, + }; + } + } + + private _edgeSnapping = false; + get edgeSnapping(): boolean { return this._edgeSnapping; } + set edgeSnapping(edgeSnapping: boolean) { + if (this._edgeSnapping === edgeSnapping) { + return; + } + + this._edgeSnapping = edgeSnapping; + + for (const child of this.children) { + if (child instanceof BranchNode) { + child.edgeSnapping = edgeSnapping; + } + } + + this.updateSplitviewEdgeSnappingEnablement(); + } constructor( readonly orientation: Orientation, @@ -229,6 +296,7 @@ class BranchNode implements ISplitView, IDisposable { readonly proportionalLayout: boolean, size: number = 0, orthogonalSize: number = 0, + edgeSnapping: boolean = false, childDescriptors?: INodeDescriptor[] ) { this._styles = styles; @@ -260,9 +328,15 @@ class BranchNode implements ISplitView, IDisposable { this.splitview = new SplitView(this.element, { ...options, descriptor }); this.children.forEach((node, index) => { - // Set up orthogonal sashes for children - node.orthogonalStartSash = this.splitview.sashes[index - 1]; - node.orthogonalEndSash = this.splitview.sashes[index]; + const first = index === 0; + const last = index === this.children.length; + + node.boundarySashes = { + start: this.boundarySashes.orthogonalStart, + end: this.boundarySashes.orthogonalEnd, + orthogonalStart: first ? this.boundarySashes.start : this.splitview.sashes[index - 1], + orthogonalEnd: last ? this.boundarySashes.end : this.splitview.sashes[index], + }; }); } @@ -301,6 +375,7 @@ class BranchNode implements ISplitView, IDisposable { this._orthogonalSize = size; this.absoluteOffset = ctx.absoluteOffset + offset; this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset; + this.absoluteOrthogonalSize = ctx.absoluteOrthogonalSize; this.splitview.layout(ctx.orthogonalSize, { orthogonalSize: size, @@ -310,9 +385,7 @@ class BranchNode implements ISplitView, IDisposable { absoluteOrthogonalSize: ctx.absoluteSize }); - // Disable snapping on views which sit on the edges of the grid - this.splitview.startSnappingEnabled = this.absoluteOrthogonalOffset > 0; - this.splitview.endSnappingEnabled = this.absoluteOrthogonalOffset + ctx.orthogonalSize < ctx.absoluteOrthogonalSize; + this.updateSplitviewEdgeSnappingEnablement(); } setVisible(visible: boolean): void { @@ -326,7 +399,7 @@ class BranchNode implements ISplitView, IDisposable { throw new Error('Invalid index'); } - this.splitview.addView(node, size, index); + this.splitview.addView(node, size, index, skipLayout); this._addChild(node, index); this.onDidChildrenChange(); } @@ -335,15 +408,26 @@ class BranchNode implements ISplitView, IDisposable { const first = index === 0; const last = index === this.children.length; this.children.splice(index, 0, node); - node.orthogonalStartSash = this.splitview.sashes[index - 1]; - node.orthogonalEndSash = this.splitview.sashes[index]; + + node.boundarySashes = { + start: this.boundarySashes.orthogonalStart, + end: this.boundarySashes.orthogonalEnd, + orthogonalStart: first ? this.boundarySashes.start : this.splitview.sashes[index - 1], + orthogonalEnd: last ? this.boundarySashes.end : this.splitview.sashes[index], + }; if (!first) { - this.children[index - 1].orthogonalEndSash = this.splitview.sashes[index - 1]; + this.children[index - 1].boundarySashes = { + ...this.children[index - 1].boundarySashes, + orthogonalEnd: this.splitview.sashes[index - 1] + }; } if (!last) { - this.children[index + 1].orthogonalStartSash = this.splitview.sashes[index]; + this.children[index + 1].boundarySashes = { + ...this.children[index + 1].boundarySashes, + orthogonalStart: this.splitview.sashes[index] + }; } } @@ -363,11 +447,17 @@ class BranchNode implements ISplitView, IDisposable { const [child] = this.children.splice(index, 1); if (!first) { - this.children[index - 1].orthogonalEndSash = this.splitview.sashes[index - 1]; + this.children[index - 1].boundarySashes = { + ...this.children[index - 1].boundarySashes, + orthogonalEnd: this.splitview.sashes[index - 1] + }; } if (!last) { // [0,1,2,3] (2) => [0,1,3] - this.children[index].orthogonalStartSash = this.splitview.sashes[Math.max(index - 1, 0)]; + this.children[index].boundarySashes = { + ...this.children[index].boundarySashes, + orthogonalStart: this.splitview.sashes[Math.max(index - 1, 0)] + }; } return child; @@ -408,7 +498,12 @@ class BranchNode implements ISplitView, IDisposable { to = clamp(to, 0, this.children.length); this.splitview.swapViews(from, to); - [this.children[from].orthogonalStartSash, this.children[from].orthogonalEndSash, this.children[to].orthogonalStartSash, this.children[to].orthogonalEndSash] = [this.children[to].orthogonalStartSash, this.children[to].orthogonalEndSash, this.children[from].orthogonalStartSash, this.children[from].orthogonalEndSash]; + + // swap boundary sashes + [this.children[from].boundarySashes, this.children[to].boundarySashes] + = [this.children[from].boundarySashes, this.children[to].boundarySashes]; + + // swap children [this.children[from], this.children[to]] = [this.children[to], this.children[from]]; this.onDidChildrenChange(); @@ -531,6 +626,11 @@ class BranchNode implements ISplitView, IDisposable { }); } + private updateSplitviewEdgeSnappingEnablement(): void { + this.splitview.startSnappingEnabled = this._edgeSnapping || this.absoluteOrthogonalOffset > 0; + this.splitview.endSnappingEnabled = this._edgeSnapping || this.absoluteOrthogonalOffset + this._size < this.absoluteOrthogonalSize; + } + dispose(): void { for (const child of this.children) { child.dispose(); @@ -655,12 +755,14 @@ class LeafNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.maximumWidth : this.maximumHeight; } - set orthogonalStartSash(sash: Sash) { - // noop - } + private _boundarySashes: IRelativeBoundarySashes = {}; + get boundarySashes(): IRelativeBoundarySashes { return this._boundarySashes; } + set boundarySashes(boundarySashes: IRelativeBoundarySashes) { + this._boundarySashes = boundarySashes; - set orthogonalEndSash(sash: Sash) { - // noop + if (this.view.setBoundarySashes) { + this.view.setBoundarySashes(toAbsoluteBoundarySashes(boundarySashes, this.orientation)); + } } layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { @@ -697,7 +799,7 @@ export interface INodeDescriptor { function flipNode(node: T, size: number, orthogonalSize: number): T { if (node instanceof BranchNode) { - const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize); + const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize, node.edgeSnapping); let totalSize = 0; @@ -713,7 +815,7 @@ function flipNode(node: T, size: number, orthogonalSize: number) newSize += size - totalSize; } - result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0); + result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0, true); } return result as T; @@ -764,6 +866,7 @@ export class GridView implements IDisposable { const { size, orthogonalSize } = this._root; this.root = flipNode(this._root, orthogonalSize, size); this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize }); + this.boundarySashes = this.boundarySashes; } get width(): number { return this.root.width; } @@ -777,6 +880,17 @@ export class GridView implements IDisposable { private _onDidChange = new Relay(); readonly onDidChange = this._onDidChange.event; + private _boundarySashes: IBoundarySashes = {}; + get boundarySashes(): IBoundarySashes { return this._boundarySashes; } + set boundarySashes(boundarySashes: IBoundarySashes) { + this._boundarySashes = boundarySashes; + this.root.boundarySashes = fromAbsoluteBoundarySashes(boundarySashes, this.orientation); + } + + set edgeSnapping(edgeSnapping: boolean) { + this.root.edgeSnapping = edgeSnapping; + } + /** * The first layout controller makes sure layout only propagates * to the views after the very first call to gridview.layout() @@ -846,7 +960,7 @@ export class GridView implements IDisposable { grandParent.removeChild(parentIndex); - const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); + const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize, grandParent.edgeSnapping); grandParent.addChild(newParent, parent.size, parentIndex); const newSibling = new LeafNode(parent.view, grandParent.orientation, this.layoutController, parent.size); @@ -898,6 +1012,7 @@ export class GridView implements IDisposable { // we must promote sibling to be the new root parent.removeChild(0); this.root = sibling; + this.boundarySashes = this.boundarySashes; return node.view; } @@ -1118,7 +1233,7 @@ export class GridView implements IDisposable { } as INodeDescriptor; }); - result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, children); + result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, undefined, children); } else { result = new LeafNode(deserializer.fromJSON(node.data), orientation, this.layoutController, orthogonalSize, node.size); } diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index e408f21afd2..c6b9850857e 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { renderCodicons } from 'vs/base/common/codicons'; -import { escape } from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { renderCodicons } from 'vs/base/browser/codicons'; export interface IHighlight { start: number; @@ -15,7 +15,7 @@ export interface IHighlight { export class HighlightedLabel { - private domNode: HTMLElement; + private readonly domNode: HTMLElement; private text: string = ''; private title: string = ''; private highlights: IHighlight[] = []; @@ -44,10 +44,6 @@ export class HighlightedLabel { return; } - if (!Array.isArray(highlights)) { - highlights = []; - } - this.text = text; this.title = title; this.highlights = highlights; @@ -56,7 +52,7 @@ export class HighlightedLabel { private render(): void { - let htmlContent = ''; + const children: HTMLSpanElement[] = []; let pos = 0; for (const highlight of this.highlights) { @@ -64,31 +60,26 @@ export class HighlightedLabel { continue; } if (pos < highlight.start) { - htmlContent += ''; const substring = this.text.substring(pos, highlight.start); - htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); - htmlContent += ''; + children.push(dom.$('span', undefined, ...this.supportCodicons ? renderCodicons(substring) : [substring])); pos = highlight.end; } - if (highlight.extraClasses) { - htmlContent += ``; - } else { - htmlContent += ``; - } + const substring = this.text.substring(highlight.start, highlight.end); - htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); - htmlContent += ''; + const element = dom.$('span.highlight', undefined, ...this.supportCodicons ? renderCodicons(substring) : [substring]); + if (highlight.extraClasses) { + element.classList.add(highlight.extraClasses); + } + children.push(element); pos = highlight.end; } if (pos < this.text.length) { - htmlContent += ''; - const substring = this.text.substring(pos); - htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); - htmlContent += ''; + const substring = this.text.substring(pos,); + children.push(dom.$('span', undefined, ...this.supportCodicons ? renderCodicons(substring) : [substring])); } - this.domNode.innerHTML = htmlContent; + dom.reset(this.domNode, ...children); if (this.title) { this.domNode.title = this.title; } else { diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css new file mode 100644 index 00000000000..a5390be414d --- /dev/null +++ b/src/vs/base/browser/ui/hover/hover.css @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-hover { + cursor: default; + position: absolute; + overflow: hidden; + z-index: 50; + user-select: text; + -webkit-user-select: text; + -ms-user-select: text; + box-sizing: initial; + animation: fadein 100ms linear; + line-height: 1.5em; +} + +.monaco-hover.hidden { + display: none; +} + +.monaco-hover .hover-contents { + padding: 4px 8px; +} + +.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { + max-width: 500px; + word-wrap: break-word; +} + +.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr { + /* This is a strange rule but it avoids https://github.com/microsoft/vscode/issues/96795, just 100vw on its own caused the actual hover width to increase */ + min-width: calc(100% + 100vw); +} + +.monaco-hover p, +.monaco-hover .code, +.monaco-hover ul { + margin: 8px 0; +} + +.monaco-hover code { + font-family: var(--monaco-monospace-font); +} + +.monaco-hover hr { + margin-top: 4px; + margin-bottom: -4px; + margin-left: -10px; + margin-right: -10px; + height: 1px; +} + +.monaco-hover p:first-child, +.monaco-hover .code:first-child, +.monaco-hover ul:first-child { + margin-top: 0; +} + +.monaco-hover p:last-child, +.monaco-hover .code:last-child, +.monaco-hover ul:last-child { + margin-bottom: 0; +} + +/* MarkupContent Layout */ +.monaco-hover ul { + padding-left: 20px; +} +.monaco-hover ol { + padding-left: 20px; +} + +.monaco-hover li > p { + margin-bottom: 0; +} + +.monaco-hover li > ul { + margin-top: 0; +} + +.monaco-hover code { + border-radius: 3px; + padding: 0 0.4em; +} + +.monaco-hover .monaco-tokenized-source { + white-space: pre-wrap; + word-break: break-all; +} + +.monaco-hover .hover-row.status-bar { + font-size: 12px; + line-height: 22px; +} + +.monaco-hover .hover-row.status-bar .actions { + display: flex; + padding: 0px 8px; +} + +.monaco-hover .hover-row.status-bar .actions .action-container { + margin-right: 16px; + cursor: pointer; +} + +.monaco-hover .hover-row.status-bar .actions .action-container .action .icon { + padding-right: 4px; +} + +.monaco-hover .markdown-hover .hover-contents .codicon { + color: inherit; + font-size: inherit; + vertical-align: middle; +} + +.monaco-hover .hover-contents a.code-link:before { + content: '('; +} +.monaco-hover .hover-contents a.code-link:after { + content: ')'; +} + +.monaco-hover .hover-contents a.code-link { + color: inherit; +} +.monaco-hover .hover-contents a.code-link > span { + text-decoration: underline; + /** Hack to force underline to show **/ + border-bottom: 1px solid transparent; + text-underline-position: under; +} + +/** Spans in markdown hovers need a margin-bottom to avoid looking cramped: https://github.com/microsoft/vscode/issues/101496 **/ +.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents) span { + margin-bottom: 4px; + display: inline-block; +} diff --git a/src/vs/base/browser/ui/hover/hoverWidget.ts b/src/vs/base/browser/ui/hover/hoverWidget.ts new file mode 100644 index 00000000000..6162569486e --- /dev/null +++ b/src/vs/base/browser/ui/hover/hoverWidget.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./hover'; +import * as dom from 'vs/base/browser/dom'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; + +const $ = dom.$; + +export class HoverWidget extends Disposable { + + public readonly containerDomNode: HTMLElement; + public readonly contentsDomNode: HTMLElement; + private readonly _scrollbar: DomScrollableElement; + + constructor() { + super(); + + this.containerDomNode = document.createElement('div'); + this.containerDomNode.className = 'monaco-hover'; + this.containerDomNode.tabIndex = 0; + this.containerDomNode.setAttribute('role', 'tooltip'); + + this.contentsDomNode = document.createElement('div'); + this.contentsDomNode.className = 'monaco-hover-content'; + + this._scrollbar = this._register(new DomScrollableElement(this.contentsDomNode, {})); + this.containerDomNode.appendChild(this._scrollbar.getDomNode()); + } + + public onContentsChanged(): void { + this._scrollbar.scanDomNode(); + } +} + +export function renderHoverAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }, keybindingLabel: string | null): IDisposable { + const actionContainer = dom.append(parent, $('div.action-container')); + const action = dom.append(actionContainer, $('a.action')); + action.setAttribute('href', '#'); + action.setAttribute('role', 'button'); + if (actionOptions.iconClass) { + dom.append(action, $(`span.icon.${actionOptions.iconClass}`)); + } + const label = dom.append(action, $('span')); + label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label; + return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => { + e.stopPropagation(); + e.preventDefault(); + actionOptions.run(actionContainer); + }); +} diff --git a/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts b/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts new file mode 100644 index 00000000000..0e853d988b5 --- /dev/null +++ b/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export interface IHoverDelegateTarget extends IDisposable { + readonly targetElements: readonly HTMLElement[]; + x?: number; +} + +export interface IHoverDelegateOptions { + text: IMarkdownString | string; + target: IHoverDelegateTarget | HTMLElement; + anchorPosition?: AnchorPosition; +} + +export interface IHoverDelegate { + showHover(options: IHoverDelegateOptions): IDisposable | undefined; +} diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index af06f86e2fa..18a32bf9952 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -7,22 +7,30 @@ import 'vs/css!./iconlabel'; import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMatch } from 'vs/base/common/filters'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Range } from 'vs/base/common/range'; import { equals } from 'vs/base/common/objects'; +import { isMacintosh } from 'vs/base/common/platform'; +import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { isString } from 'vs/base/common/types'; +import { domEvent } from 'vs/base/browser/event'; export interface IIconLabelCreationOptions { supportHighlights?: boolean; supportDescriptionHighlights?: boolean; supportCodicons?: boolean; + hoverDelegate?: IHoverDelegate; } export interface IIconLabelValueOptions { - title?: string; + title?: string | IMarkdownString | Promise; descriptionTitle?: string; hideIcon?: boolean; extraClasses?: string[]; italic?: boolean; + strikethrough?: boolean; matches?: IMatch[]; labelEscapeNewLines?: boolean; descriptionMatches?: IMatch[]; @@ -34,7 +42,6 @@ class FastLabelNode { private disposed: boolean | undefined; private _textContent: string | undefined; private _className: string | undefined; - private _title: string | undefined; private _empty: boolean | undefined; constructor(private _element: HTMLElement) { @@ -62,19 +69,6 @@ class FastLabelNode { this._element.className = className; } - set title(title: string) { - if (this.disposed || title === this._title) { - return; - } - - this._title = title; - if (this._title) { - this._element.title = title; - } else { - this._element.removeAttribute('title'); - } - } - set empty(empty: boolean) { if (this.disposed || empty === this._empty) { return; @@ -99,6 +93,9 @@ export class IconLabel extends Disposable { private descriptionNode: FastLabelNode | HighlightedLabel | undefined; private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel; + private hoverDelegate: IHoverDelegate | undefined = undefined; + private readonly customHovers: Map = new Map(); + constructor(container: HTMLElement, options?: IIconLabelCreationOptions) { super(); @@ -120,6 +117,10 @@ export class IconLabel extends Disposable { } else { this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.descriptionContainer.element, dom.$('span.label-description')))); } + + if (options?.hoverDelegate) { + this.hoverDelegate = options.hoverDelegate; + } } get element(): HTMLElement { @@ -136,10 +137,14 @@ export class IconLabel extends Disposable { if (options.italic) { classes.push('italic'); } + + if (options.strikethrough) { + classes.push('strikethrough'); + } } this.domNode.className = classes.join(' '); - this.domNode.title = options?.title || ''; + this.setupHover(this.domNode.element, options?.title); this.nameNode.setLabel(label, options); @@ -150,18 +155,86 @@ export class IconLabel extends Disposable { if (this.descriptionNode instanceof HighlightedLabel) { this.descriptionNode.set(description || '', options ? options.descriptionMatches : undefined); - if (options?.descriptionTitle) { - this.descriptionNode.element.title = options.descriptionTitle; - } else { - this.descriptionNode.element.removeAttribute('title'); - } + this.setupHover(this.descriptionNode.element, options?.descriptionTitle); } else { this.descriptionNode.textContent = description || ''; - this.descriptionNode.title = options?.descriptionTitle || ''; + this.setupHover(this.descriptionNode.element, options?.descriptionTitle || ''); this.descriptionNode.empty = !description; } } } + + private setupHover(htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + const previousCustomHover = this.customHovers.get(htmlElement); + if (previousCustomHover) { + previousCustomHover.dispose(); + this.customHovers.delete(htmlElement); + } + + if (!tooltip) { + htmlElement.removeAttribute('title'); + return; + } + + if (!this.hoverDelegate) { + return this.setupNativeHover(htmlElement, tooltip); + } else { + return this.setupCustomHover(this.hoverDelegate, htmlElement, tooltip); + } + } + + private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + htmlElement.removeAttribute('title'); + // Testing has indicated that on Windows and Linux 500 ms matches the native hovers most closely. + // On Mac, the delay is 1500. + const hoverDelay = isMacintosh ? 1500 : 500; + let hoverOptions: IHoverDelegateOptions | undefined; + let mouseX: number | undefined; + function mouseOver(this: HTMLElement, e: MouseEvent): any { + let isHovering = true; + function mouseMove(this: HTMLElement, e: MouseEvent): any { + mouseX = e.x; + } + function mouseLeave(this: HTMLElement, e: MouseEvent): any { + isHovering = false; + } + const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeave.bind(htmlElement)); + const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement)); + setTimeout(async () => { + if (isHovering && tooltip) { + // Re-use the already computed hover options if they exist. + if (!hoverOptions) { + const target: IHoverDelegateTarget = { + targetElements: [this], + dispose: () => { } + }; + const resolvedTooltip = await tooltip; + if (resolvedTooltip) { + hoverOptions = { + text: resolvedTooltip, + target, + anchorPosition: AnchorPosition.BELOW + }; + } + } + if (hoverOptions) { + if (mouseX !== undefined) { + (hoverOptions.target).x = mouseX + 10; + } + hoverDelegate.showHover(hoverOptions); + } + } + mouseMoveDisposable.dispose(); + mouseLeaveDisposable.dispose(); + }, hoverDelay); + } + const mouseOverDisposable = this._register(domEvent(htmlElement, dom.EventType.MOUSE_OVER, true)(mouseOver.bind(htmlElement))); + this.customHovers.set(htmlElement, mouseOverDisposable); + } + + private setupNativeHover(htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + htmlElement.title = isString(tooltip) ? tooltip : ''; + } } class Label { @@ -182,22 +255,22 @@ class Label { if (typeof label === 'string') { if (!this.singleLabel) { - this.container.innerHTML = ''; - dom.removeClass(this.container, 'multiple'); + this.container.innerText = ''; + this.container.classList.remove('multiple'); this.singleLabel = dom.append(this.container, dom.$('a.label-name', { id: options?.domId })); } this.singleLabel.textContent = label; } else { - this.container.innerHTML = ''; - dom.addClass(this.container, 'multiple'); + this.container.innerText = ''; + this.container.classList.add('multiple'); this.singleLabel = undefined; for (let i = 0; i < label.length; i++) { const l = label[i]; const id = options?.domId && `${options?.domId}_${i}`; - dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i }, l)); + dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }, l)); if (i < label.length - 1) { dom.append(this.container, dom.$('span.label-separator', undefined, options?.separator || '/')); @@ -245,16 +318,15 @@ class LabelWithHighlights { if (typeof label === 'string') { if (!this.singleLabel) { - this.container.innerHTML = ''; - dom.removeClass(this.container, 'multiple'); + this.container.innerText = ''; + this.container.classList.remove('multiple'); this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options?.domId })), this.supportCodicons); } - this.singleLabel.set(label, options?.matches, options?.title, options?.labelEscapeNewLines); + this.singleLabel.set(label, options?.matches, undefined, options?.labelEscapeNewLines); } else { - - this.container.innerHTML = ''; - dom.addClass(this.container, 'multiple'); + this.container.innerText = ''; + this.container.classList.add('multiple'); this.singleLabel = undefined; const separator = options?.separator || '/'; @@ -265,9 +337,9 @@ class LabelWithHighlights { const m = matches ? matches[i] : undefined; const id = options?.domId && `${options?.domId}_${i}`; - const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i }); + const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }); const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), this.supportCodicons); - highlightedLabel.set(l, m, options?.title, options?.labelEscapeNewLines); + highlightedLabel.set(l, m, undefined, options?.labelEscapeNewLines); if (i < label.length - 1) { dom.append(name, dom.$('span.label-separator', undefined, separator)); diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index 8ee16195b5c..9341febad27 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -28,7 +28,7 @@ -moz-osx-font-smoothing: grayscale; vertical-align: top; - flex-shrink: 0; /* fix for https://github.com/Microsoft/vscode/issues/13787 */ + flex-shrink: 0; /* fix for https://github.com/microsoft/vscode/issues/13787 */ } .monaco-icon-label > .monaco-icon-label-container { @@ -55,11 +55,20 @@ white-space: pre; /* enable to show labels that include multiple whitespaces */ } +.vs .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { + opacity: .95; +} + .monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, .monaco-icon-label.italic > .monaco-icon-description-container > .label-description { font-style: italic; } +.monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, +.monaco-icon-label.strikethrough > .monaco-icon-description-container > .label-description { + text-decoration: line-through; +} + .monaco-icon-label::after { opacity: 0.75; font-size: 90%; @@ -69,16 +78,12 @@ } /* make sure selection color wins when a label is being selected */ -.monaco-tree.focused .selected .monaco-icon-label, /* tree */ -.monaco-tree.focused .selected .monaco-icon-label::after, .monaco-list:focus .selected .monaco-icon-label, /* list */ .monaco-list:focus .selected .monaco-icon-label::after { color: inherit !important; } -.monaco-tree-row.focused.selected .label-description, -.monaco-tree-row.selected .label-description, .monaco-list-row.focused.selected .label-description, .monaco-list-row.selected .label-description { opacity: .8; diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index cd57d7f772f..2410c5234bb 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -6,7 +6,6 @@ import 'vs/css!./inputBox'; import * as nls from 'vs/nls'; -import * as Bal from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; import { renderFormattedText, renderText } from 'vs/base/browser/formattedTextRenderer'; @@ -164,14 +163,14 @@ export class InputBox extends Widget { this.input.setAttribute('autocapitalize', 'off'); this.input.setAttribute('spellcheck', 'false'); - this.onfocus(this.input, () => dom.addClass(this.element, 'synthetic-focus')); - this.onblur(this.input, () => dom.removeClass(this.element, 'synthetic-focus')); + this.onfocus(this.input, () => this.element.classList.add('synthetic-focus')); + this.onblur(this.input, () => this.element.classList.remove('synthetic-focus')); if (this.options.flexibleHeight) { this.maxHeight = typeof this.options.flexibleMaxHeight === 'number' ? this.options.flexibleMaxHeight : Number.POSITIVE_INFINITY; this.mirror = dom.append(wrapper, $('div.mirror')); - this.mirror.innerHTML = ' '; + this.mirror.innerText = '\u00a0'; this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto }); @@ -212,14 +211,6 @@ export class InputBox extends Widget { this.onblur(this.input, () => this.onBlur()); this.onfocus(this.input, () => this.onFocus()); - // Add placeholder shim for IE because IE decides to hide the placeholder on focus (we dont want that!) - if (this.placeholder && Bal.isIE) { - this.onclick(this.input, (e) => { - dom.EventHelper.stop(e, true); - this.input.focus(); - }); - } - this.ignoreGesture(this.input); setTimeout(() => this.updateMirror(), 0); @@ -257,6 +248,10 @@ export class InputBox extends Widget { } } + public getAriaLabel(): string { + return this.ariaLabel; + } + public get mirrorElement(): HTMLElement | undefined { return this.mirror; } @@ -300,6 +295,10 @@ export class InputBox extends Widget { } } + public isSelectionAtEnd(): boolean { + return this.input.selectionEnd === this.input.value.length && this.input.selectionStart === this.input.selectionEnd; + } + public enable(): void { this.input.removeAttribute('disabled'); } @@ -369,27 +368,15 @@ export class InputBox extends Widget { public showMessage(message: IMessage, force?: boolean): void { this.message = message; - dom.removeClass(this.element, 'idle'); - dom.removeClass(this.element, 'info'); - dom.removeClass(this.element, 'warning'); - dom.removeClass(this.element, 'error'); - dom.addClass(this.element, this.classForType(message.type)); + this.element.classList.remove('idle'); + this.element.classList.remove('info'); + this.element.classList.remove('warning'); + this.element.classList.remove('error'); + this.element.classList.add(this.classForType(message.type)); const styles = this.stylesForType(this.message.type); this.element.style.border = styles.border ? `1px solid ${styles.border}` : ''; - // ARIA Support - let alertText: string; - if (message.type === MessageType.ERROR) { - alertText = nls.localize('alertErrorMessage', "Error: {0}", message.content); - } else if (message.type === MessageType.WARNING) { - alertText = nls.localize('alertWarningMessage', "Warning: {0}", message.content); - } else { - alertText = nls.localize('alertInfoMessage', "Info: {0}", message.content); - } - - aria.alert(alertText); - if (this.hasFocus() || force) { this._showMessage(); } @@ -398,10 +385,10 @@ export class InputBox extends Widget { public hideMessage(): void { this.message = null; - dom.removeClass(this.element, 'info'); - dom.removeClass(this.element, 'warning'); - dom.removeClass(this.element, 'error'); - dom.addClass(this.element, 'idle'); + this.element.classList.remove('info'); + this.element.classList.remove('warning'); + this.element.classList.remove('error'); + this.element.classList.add('idle'); this._hideMessage(); this.applyStyles(); @@ -473,7 +460,7 @@ export class InputBox extends Widget { const spanElement = (this.message.formatContent ? renderFormattedText(this.message.content, renderOptions) : renderText(this.message.content, renderOptions)); - dom.addClass(spanElement, this.classForType(this.message.type)); + spanElement.classList.add(this.classForType(this.message.type)); const styles = this.stylesForType(this.message.type); spanElement.style.backgroundColor = styles.background ? styles.background.toString() : ''; @@ -490,6 +477,18 @@ export class InputBox extends Widget { layout: layout }); + // ARIA Support + let alertText: string; + if (this.message.type === MessageType.ERROR) { + alertText = nls.localize('alertErrorMessage', "Error: {0}", this.message.content); + } else if (this.message.type === MessageType.WARNING) { + alertText = nls.localize('alertWarningMessage', "Warning: {0}", this.message.content); + } else { + alertText = nls.localize('alertInfoMessage', "Info: {0}", this.message.content); + } + + aria.alert(alertText); + this.state = 'open'; } @@ -510,7 +509,7 @@ export class InputBox extends Widget { this.validate(); this.updateMirror(); - dom.toggleClass(this.input, 'empty', !this.value); + this.input.classList.toggle('empty', !this.value); if (this.state === 'open' && this.contextViewProvider) { this.contextViewProvider.layout(); @@ -530,7 +529,7 @@ export class InputBox extends Widget { if (mirrorTextContent) { this.mirror.textContent = value + suffix; } else { - this.mirror.innerHTML = ' '; + this.mirror.innerText = '\u00a0'; } this.layout(); @@ -561,7 +560,7 @@ export class InputBox extends Widget { this.element.style.backgroundColor = background; this.element.style.color = foreground; - this.input.style.backgroundColor = background; + this.input.style.backgroundColor = 'inherit'; this.input.style.color = foreground; this.element.style.borderWidth = border ? '1px' : ''; diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index c4b75d53cde..53857f4dad4 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -129,10 +129,6 @@ cursor: pointer; } -.monaco-list-type-filter > .controls > .filter:checked::before { - content: "\eb83" !important; /* codicon-list-filter */ -} - .monaco-list-type-filter > .controls > .filter { margin-left: 4px; } diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index f07e22e1bee..4f8e796f206 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -63,22 +63,14 @@ export interface IIdentityProvider { getId(element: T): { toString(): string; }; } -export enum ListAriaRootRole { - /** default tree structure role */ - TREE = 'tree', - - /** role='tree' can interfere with screenreaders reading nested elements inside the tree row. Use FORM in that case. */ - FORM = 'form' -} - export interface IKeyboardNavigationLabelProvider { /** - * Return a keyboard navigation label which will be used by the - * list for filtering/navigating. Return `undefined` to make an - * element always match. + * Return a keyboard navigation label(s) which will be used by + * the list for filtering/navigating. Return `undefined` to make + * an element always match. */ - getKeyboardNavigationLabel(element: T): { toString(): string | undefined; } | undefined; + getKeyboardNavigationLabel(element: T): { toString(): string | undefined; } | { toString(): string | undefined; }[] | undefined; } export interface IKeyboardNavigationDelegate { diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 352dd6b8e34..03db9d2a60d 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -6,11 +6,13 @@ import 'vs/css!./list'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { range } from 'vs/base/common/arrays'; -import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent } from './list'; -import { List, IListStyles, IListOptions } from './listWidget'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent } from './list'; +import { List, IListStyles, IListOptions, IListAccessibilityProvider, IListOptionsUpdate } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; import { Event } from 'vs/base/common/event'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { IThemable } from 'vs/base/common/styler'; export interface IPagedRenderer extends IListRenderer { renderPlaceholder(index: number, templateData: TTemplateData): void; @@ -70,7 +72,55 @@ class PagedRenderer implements IListRenderer implements IDisposable { +class PagedAccessibilityProvider implements IListAccessibilityProvider { + + constructor( + private modelProvider: () => IPagedModel, + private accessibilityProvider: IListAccessibilityProvider + ) { } + + getWidgetAriaLabel(): string { + return this.accessibilityProvider.getWidgetAriaLabel(); + } + + getAriaLabel(index: number): string | null { + const model = this.modelProvider(); + + if (!model.isResolved(index)) { + return null; + } + + return this.accessibilityProvider.getAriaLabel(model.get(index)); + } +} + +export interface IPagedListOptions { + readonly enableKeyboardNavigation?: boolean; + readonly automaticKeyboardNavigation?: boolean; + readonly ariaLabel?: string; + readonly keyboardSupport?: boolean; + readonly multipleSelectionSupport?: boolean; + readonly accessibilityProvider?: IListAccessibilityProvider; + + // list view options + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly setRowHeight?: boolean; + readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; + readonly additionalScrollHeight?: number; +} + +function fromPagedListOptions(modelProvider: () => IPagedModel, options: IPagedListOptions): IListOptions { + return { + ...options, + accessibilityProvider: options.accessibilityProvider && new PagedAccessibilityProvider(modelProvider, options.accessibilityProvider) + }; +} + +export class PagedList implements IThemable, IDisposable { private list: List; private _model!: IPagedModel; @@ -80,10 +130,15 @@ export class PagedList implements IDisposable { container: HTMLElement, virtualDelegate: IListVirtualDelegate, renderers: IPagedRenderer[], - options: IListOptions = {} + options: IPagedListOptions = {} ) { - const pagedRenderers = renderers.map(r => new PagedRenderer>(r, () => this.model)); - this.list = new List(user, container, virtualDelegate, pagedRenderers, options); + const modelProvider = () => this.model; + const pagedRenderers = renderers.map(r => new PagedRenderer>(r, modelProvider)); + this.list = new List(user, container, virtualDelegate, pagedRenderers, fromPagedListOptions(modelProvider, options)); + } + + updateOptions(options: IListOptionsUpdate) { + this.list.updateOptions(options); } getHTMLElement(): HTMLElement { @@ -114,20 +169,28 @@ export class PagedList implements IDisposable { return this.list.onDidDispose; } - get onFocusChange(): Event> { - return Event.map(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + get onMouseClick(): Event> { + return Event.map(this.list.onMouseClick, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); } - get onOpen(): Event> { - return Event.map(this.list.onDidOpen, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); + get onMouseDblClick(): Event> { + return Event.map(this.list.onMouseDblClick, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); } - get onSelectionChange(): Event> { - return Event.map(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + get onTap(): Event> { + return Event.map(this.list.onTap, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); } - get onPin(): Event> { - return Event.map(this.list.onDidPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + get onPointer(): Event> { + return Event.map(this.list.onPointer, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); + } + + get onDidChangeFocus(): Event> { + return Event.map(this.list.onDidChangeFocus, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); + } + + get onDidChangeSelection(): Event> { + return Event.map(this.list.onDidChangeSelection, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); } get onContextMenu(): Event> { @@ -163,10 +226,6 @@ export class PagedList implements IDisposable { this.list.scrollLeft = scrollLeft; } - open(indexes: number[], browserEvent?: UIEvent): void { - this.list.open(indexes, browserEvent); - } - setFocus(indexes: number[]): void { this.list.setFocus(indexes); } @@ -191,8 +250,8 @@ export class PagedList implements IDisposable { return this.list.getFocus(); } - setSelection(indexes: number[]): void { - this.list.setSelection(indexes); + setSelection(indexes: number[], browserEvent?: UIEvent): void { + this.list.setSelection(indexes, browserEvent); } getSelection(): number[] { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 72fd9a63ad3..b957f128ee3 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -6,11 +6,10 @@ import { getOrDefault } from 'vs/base/common/objects'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; -import * as DOM from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; +import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions, Scrollable } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list'; import { RowCache, IRow } from './rowCache'; @@ -21,6 +20,8 @@ import { equals, distinct } from 'vs/base/common/arrays'; import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { disposableTimeout, Delayer } from 'vs/base/common/async'; import { isFirefox } from 'vs/base/browser/browser'; +import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { $, animate, getContentHeight, getContentWidth, getTopLeftOffset, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; interface IItem { readonly id: string; @@ -40,29 +41,36 @@ export interface IListViewDragAndDrop extends IListDragAndDrop { getDragElements(element: T): T[]; } -export interface IAriaProvider { - getSetSize(element: T, index: number, listLength: number): number; - getPosInSet(element: T, index: number): number; - getRole?(element: T): string; - isChecked?(element: T): boolean; +export interface IListViewAccessibilityProvider { + getSetSize?(element: T, index: number, listLength: number): number; + getPosInSet?(element: T, index: number): number; + getRole?(element: T): string | undefined; + isChecked?(element: T): boolean | undefined; } -export interface IListViewOptions { +export interface IListViewOptionsUpdate { + readonly additionalScrollHeight?: number; + readonly smoothScrolling?: boolean; + readonly horizontalScrolling?: boolean; +} + +export interface IListViewOptions extends IListViewOptionsUpdate { readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; readonly setRowLineHeight?: boolean; + readonly setRowHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; - readonly horizontalScrolling?: boolean; - readonly ariaProvider?: IAriaProvider; - readonly additionalScrollHeight?: number; + readonly accessibilityProvider?: IListViewAccessibilityProvider; + readonly transformOptimization?: boolean; } const DefaultOptions = { useShadows: true, verticalScrollMode: ScrollbarVisibility.Auto, setRowLineHeight: true, + setRowHeight: true, supportDynamicHeights: false, dnd: { getDragElements(e: T) { return [e]; }, @@ -71,13 +79,21 @@ const DefaultOptions = { onDragOver() { return false; }, drop() { } }, - horizontalScrolling: false + horizontalScrolling: false, + transformOptimization: true }; export class ElementsDragAndDropData implements IDragAndDropData { readonly elements: T[]; - context: TContext | undefined; + + private _context: TContext | undefined; + public get context(): TContext | undefined { + return this._context; + } + public set context(value: TContext | undefined) { + this._context = value; + } constructor(elements: T[]) { this.elements = elements; @@ -105,7 +121,7 @@ export class ExternalElementsDragAndDropData implements IDragAndDropData { } } -export class DesktopDragAndDropData implements IDragAndDropData { +export class NativeDragAndDropData implements IDragAndDropData { readonly types: any[]; readonly files: any[]; @@ -149,6 +165,40 @@ function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): return f1 === f2; } +class ListViewAccessibilityProvider implements Required> { + + readonly getSetSize: (element: any, index: number, listLength: number) => number; + readonly getPosInSet: (element: any, index: number) => number; + readonly getRole: (element: T) => string | undefined; + readonly isChecked: (element: T) => boolean | undefined; + + constructor(accessibilityProvider?: IListViewAccessibilityProvider) { + if (accessibilityProvider?.getSetSize) { + this.getSetSize = accessibilityProvider.getSetSize.bind(accessibilityProvider); + } else { + this.getSetSize = (e, i, l) => l; + } + + if (accessibilityProvider?.getPosInSet) { + this.getPosInSet = accessibilityProvider.getPosInSet.bind(accessibilityProvider); + } else { + this.getPosInSet = (e, i) => i + 1; + } + + if (accessibilityProvider?.getRole) { + this.getRole = accessibilityProvider.getRole.bind(accessibilityProvider); + } else { + this.getRole = _ => 'listitem'; + } + + if (accessibilityProvider?.isChecked) { + this.isChecked = accessibilityProvider.isChecked.bind(accessibilityProvider); + } else { + this.isChecked = _ => undefined; + } + } +} + export class ListView implements ISpliceable, IDisposable { private static InstanceCount = 0; @@ -165,7 +215,8 @@ export class ListView implements ISpliceable, IDisposable { private lastRenderHeight: number; private renderWidth = 0; private rowsContainer: HTMLElement; - private scrollableElement: ScrollableElement; + private scrollable: Scrollable; + private scrollableElement: SmoothScrollableElement; private _scrollHeight: number = 0; private scrollableElementUpdateDisposable: IDisposable | null = null; private scrollableElementWidthDelayer = new Delayer(50); @@ -174,10 +225,10 @@ export class ListView implements ISpliceable, IDisposable { private dragOverAnimationStopDisposable: IDisposable = Disposable.None; private dragOverMouseY: number = 0; private setRowLineHeight: boolean; + private setRowHeight: boolean; private supportDynamicHeights: boolean; - private horizontalScrolling: boolean; private additionalScrollHeight: number; - private ariaProvider: IAriaProvider; + private accessibilityProvider: ListViewAccessibilityProvider; private scrollWidth: number | undefined; private dnd: IListViewDragAndDrop; @@ -194,6 +245,37 @@ export class ListView implements ISpliceable, IDisposable { get contentHeight(): number { return this.rangeMap.size; } get onDidScroll(): Event { return this.scrollableElement.onScroll; } + get onWillScroll(): Event { return this.scrollableElement.onWillScroll; } + get containerDomNode(): HTMLElement { return this.rowsContainer; } + + private _horizontalScrolling: boolean = false; + private get horizontalScrolling(): boolean { return this._horizontalScrolling; } + private set horizontalScrolling(value: boolean) { + if (value === this._horizontalScrolling) { + return; + } + + if (value && this.supportDynamicHeights) { + throw new Error('Horizontal scrolling and dynamic heights not supported simultaneously'); + } + + this._horizontalScrolling = value; + this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); + + if (this._horizontalScrolling) { + for (const item of this.items) { + this.measureItemWidth(item); + } + + this.updateScrollWidth(); + this.scrollableElement.setScrollDimensions({ width: getContentWidth(this.domNode) }); + this.rowsContainer.style.width = `${Math.max(this.scrollWidth || 0, this.renderWidth)}px`; + } else { + this.scrollableElementWidthDelayer.cancel(); + this.scrollableElement.setScrollDimensions({ width: this.renderWidth, scrollWidth: this.renderWidth }); + this.rowsContainer.style.width = ''; + } + } constructor( container: HTMLElement, @@ -221,29 +303,34 @@ export class ListView implements ISpliceable, IDisposable { this.domNode = document.createElement('div'); this.domNode.className = 'monaco-list'; - DOM.addClass(this.domNode, this.domId); + this.domNode.classList.add(this.domId); this.domNode.tabIndex = 0; - DOM.toggleClass(this.domNode, 'mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); + this.domNode.classList.toggle('mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); - this.horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); - DOM.toggleClass(this.domNode, 'horizontal-scrolling', this.horizontalScrolling); + this._horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; - this.ariaProvider = options.ariaProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 }; + this.accessibilityProvider = new ListViewAccessibilityProvider(options.accessibilityProvider); this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; - this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)'; + + const transformOptimization = getOrDefault(options, o => o.transformOptimization, DefaultOptions.transformOptimization); + if (transformOptimization) { + this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)'; + } + this.disposables.add(Gesture.addTarget(this.rowsContainer)); - this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, { - alwaysConsumeMouseWheel: true, - horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, + this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => scheduleAtNextAnimationFrame(cb)); + this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { + horizontal: ScrollbarVisibility.Auto, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) - })); + useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), + }, this.scrollable)); this.domNode.appendChild(this.scrollableElement.getDomNode()); container.appendChild(this.domNode); @@ -252,7 +339,7 @@ export class ListView implements ISpliceable, IDisposable { domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables); // Prevent the monaco-scrollable-element from scrolling - // https://github.com/Microsoft/vscode/issues/44181 + // https://github.com/microsoft/vscode/issues/44181 domEvent(this.scrollableElement.getDomNode(), 'scroll') (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); @@ -262,12 +349,69 @@ export class ListView implements ISpliceable, IDisposable { domEvent(window, 'dragend')(this.onDragEnd, this, this.disposables); this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); + this.setRowHeight = getOrDefault(options, o => o.setRowHeight, DefaultOptions.setRowHeight); this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); this.layout(); } + updateOptions(options: IListViewOptionsUpdate) { + if (options.additionalScrollHeight !== undefined) { + this.additionalScrollHeight = options.additionalScrollHeight; + } + + if (options.smoothScrolling !== undefined) { + this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0); + } + + if (options.horizontalScrolling !== undefined) { + this.horizontalScrolling = options.horizontalScrolling; + } + } + + triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { + this.scrollableElement.triggerScrollFromMouseWheelEvent(browserEvent); + } + + updateElementHeight(index: number, size: number, anchorIndex: number | null): void { + if (index < 0 || index >= this.items.length) { + return; + } + + if (this.items[index].size === size) { + return; + } + + const lastRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + + let heightDiff = 0; + + if (index < lastRenderRange.start) { + // do not scroll the viewport if resized element is out of viewport + heightDiff = size - this.items[index].size; + } else { + if (anchorIndex !== null && anchorIndex > index && anchorIndex <= lastRenderRange.end) { + // anchor in viewport + // resized elemnet in viewport and above the anchor + heightDiff = size - this.items[index].size; + } else { + heightDiff = 0; + } + } + + this.rangeMap.splice(index, 1, [{ size: size }]); + this.items[index].size = size; + + this.render(lastRenderRange, Math.max(0, this.lastRenderTop + heightDiff), this.lastRenderHeight, undefined, undefined, true); + + this.eventuallyUpdateScrollDimensions(); + + if (this.supportDynamicHeights) { + this._rerender(this.lastRenderTop, this.lastRenderHeight); + } + } + splice(start: number, deleteCount: number, elements: T[] = []): T[] { if (this.splicing) { throw new Error('Can\'t run recursive splices.'); @@ -365,7 +509,7 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer.style.height = `${this._scrollHeight}px`; if (!this.scrollableElementUpdateDisposable) { - this.scrollableElementUpdateDisposable = DOM.scheduleAtNextAnimationFrame(() => { + this.scrollableElementUpdateDisposable = scheduleAtNextAnimationFrame(() => { this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); this.updateScrollWidth(); this.scrollableElementUpdateDisposable = null; @@ -375,6 +519,7 @@ export class ListView implements ISpliceable, IDisposable { private eventuallyUpdateScrollWidth(): void { if (!this.horizontalScrolling) { + this.scrollableElementWidthDelayer.cancel(); return; } @@ -386,10 +531,6 @@ export class ListView implements ISpliceable, IDisposable { return; } - if (this.items.length === 0) { - this.scrollableElement.setScrollDimensions({ scrollWidth: 0 }); - } - let scrollWidth = 0; for (const item of this.items) { @@ -399,7 +540,7 @@ export class ListView implements ISpliceable, IDisposable { } this.scrollWidth = scrollWidth; - this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth + 10 }); + this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth === 0 ? 0 : (scrollWidth + 10) }); } updateWidth(index: number): void { @@ -460,6 +601,10 @@ export class ListView implements ISpliceable, IDisposable { return this.items[index].element; } + indexOf(element: T): number { + return this.items.findIndex(item => item.element === element); + } + domElement(index: number): HTMLElement | null { const row = this.items[index].row; return row && row.domNode; @@ -483,7 +628,7 @@ export class ListView implements ISpliceable, IDisposable { layout(height?: number, width?: number): void { let scrollDimensions: INewScrollDimensions = { - height: typeof height === 'number' ? height : DOM.getContentHeight(this.domNode) + height: typeof height === 'number' ? height : getContentHeight(this.domNode) }; if (this.scrollableElementUpdateDisposable) { @@ -503,7 +648,7 @@ export class ListView implements ISpliceable, IDisposable { if (this.horizontalScrolling) { this.scrollableElement.setScrollDimensions({ - width: typeof width === 'number' ? width : DOM.getContentWidth(this.domNode) + width: typeof width === 'number' ? width : getContentWidth(this.domNode) }); } } @@ -511,14 +656,21 @@ export class ListView implements ISpliceable, IDisposable { // Render - private render(renderTop: number, renderHeight: number, renderLeft: number, scrollWidth: number): void { - const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + private render(previousRenderRange: IRange, renderTop: number, renderHeight: number, renderLeft: number | undefined, scrollWidth: number | undefined, updateItemsInDOM: boolean = false): void { const renderRange = this.getRenderRange(renderTop, renderHeight); const rangesToInsert = Range.relativeComplement(renderRange, previousRenderRange); const rangesToRemove = Range.relativeComplement(previousRenderRange, renderRange); const beforeElement = this.getNextToLastElement(rangesToInsert); + if (updateItemsInDOM) { + const rangesToUpdate = Range.intersect(previousRenderRange, renderRange); + + for (let i = rangesToUpdate.start; i < rangesToUpdate.end; i++) { + this.updateItemInDOM(this.items[i], i); + } + } + for (const range of rangesToInsert) { for (let i = range.start; i < range.end; i++) { this.insertItemInDOM(i, beforeElement); @@ -531,10 +683,13 @@ export class ListView implements ISpliceable, IDisposable { } } - this.rowsContainer.style.left = `-${renderLeft}px`; + if (renderLeft !== undefined) { + this.rowsContainer.style.left = `-${renderLeft}px`; + } + this.rowsContainer.style.top = `-${renderTop}px`; - if (this.horizontalScrolling) { + if (this.horizontalScrolling && scrollWidth !== undefined) { this.rowsContainer.style.width = `${Math.max(scrollWidth, this.renderWidth)}px`; } @@ -549,11 +704,11 @@ export class ListView implements ISpliceable, IDisposable { if (!item.row) { item.row = this.cache.alloc(item.templateId); - const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'treeitem'; + const role = this.accessibilityProvider.getRole(item.element) || 'listitem'; item.row!.domNode!.setAttribute('role', role); - const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined; + const checked = this.accessibilityProvider.isChecked(item.element); if (typeof checked !== 'undefined') { - item.row!.domNode!.setAttribute('aria-checked', String(checked)); + item.row!.domNode!.setAttribute('aria-checked', String(!!checked)); } } @@ -598,7 +753,7 @@ export class ListView implements ISpliceable, IDisposable { } item.row.domNode.style.width = isFirefox ? '-moz-fit-content' : 'fit-content'; - item.width = DOM.getContentWidth(item.row.domNode); + item.width = getContentWidth(item.row.domNode); const style = window.getComputedStyle(item.row.domNode); if (style.paddingLeft) { @@ -614,7 +769,10 @@ export class ListView implements ISpliceable, IDisposable { private updateItemInDOM(item: IItem, index: number): void { item.row!.domNode!.style.top = `${this.elementTop(index)}px`; - item.row!.domNode!.style.height = `${item.size}px`; + + if (this.setRowHeight) { + item.row!.domNode!.style.height = `${item.size}px`; + } if (this.setRowLineHeight) { item.row!.domNode!.style.lineHeight = `${item.size}px`; @@ -622,11 +780,11 @@ export class ListView implements ISpliceable, IDisposable { item.row!.domNode!.setAttribute('data-index', `${index}`); item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); - item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaProvider.getSetSize(item.element, index, this.length))); - item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaProvider.getPosInSet(item.element, index))); + item.row!.domNode!.setAttribute('aria-setsize', String(this.accessibilityProvider.getSetSize(item.element, index, this.length))); + item.row!.domNode!.setAttribute('aria-posinset', String(this.accessibilityProvider.getPosInSet(item.element, index))); item.row!.domNode!.setAttribute('id', this.getElementDomId(index)); - DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget); + item.row!.domNode!.classList.toggle('drop-target', item.dropTarget); } private removeItemFromDOM(index: number): void { @@ -634,8 +792,8 @@ export class ListView implements ISpliceable, IDisposable { item.dragStartDisposable.dispose(); const renderer = this.renderers.get(item.templateId); - if (renderer && renderer.disposeElement) { - renderer.disposeElement(item.element, index, item.row!.templateData, item.size); + if (item.row && renderer && renderer.disposeElement) { + renderer.disposeElement(item.element, index, item.row.templateData, item.size); } this.cache.release(item.row!); @@ -733,10 +891,13 @@ export class ListView implements ISpliceable, IDisposable { private onScroll(e: ScrollEvent): void { try { - this.render(e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); + const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + this.render(previousRenderRange, e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); if (this.supportDynamicHeights) { - this._rerender(e.scrollTop, e.height); + // Don't update scrollTop from within an scroll event + // so we don't break smooth scrolling. #104144 + this._rerender(e.scrollTop, e.height, false); } } catch (err) { console.error('Got bad scroll event:', e); @@ -774,7 +935,7 @@ export class ListView implements ISpliceable, IDisposable { label = String(elements.length); } - const dragImage = DOM.$('.monaco-drag-image'); + const dragImage = $('.monaco-drag-image'); dragImage.textContent = label; document.body.appendChild(dragImage); event.dataTransfer.setDragImage(dragImage, -10, -10); @@ -816,7 +977,7 @@ export class ListView implements ISpliceable, IDisposable { return false; } - this.currentDragData = new DesktopDragAndDropData(); + this.currentDragData = new NativeDragAndDropData(); } } @@ -855,11 +1016,11 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragFeedbackDisposable.dispose(); if (feedback[0] === -1) { // entire list feedback - DOM.addClass(this.domNode, 'drop-target'); - DOM.addClass(this.rowsContainer, 'drop-target'); + this.domNode.classList.add('drop-target'); + this.rowsContainer.classList.add('drop-target'); this.currentDragFeedbackDisposable = toDisposable(() => { - DOM.removeClass(this.domNode, 'drop-target'); - DOM.removeClass(this.rowsContainer, 'drop-target'); + this.domNode.classList.remove('drop-target'); + this.rowsContainer.classList.remove('drop-target'); }); } else { for (const index of feedback) { @@ -867,7 +1028,7 @@ export class ListView implements ISpliceable, IDisposable { item.dropTarget = true; if (item.row && item.row.domNode) { - DOM.addClass(item.row.domNode, 'drop-target'); + item.row.domNode.classList.add('drop-target'); } } @@ -877,7 +1038,7 @@ export class ListView implements ISpliceable, IDisposable { item.dropTarget = false; if (item.row && item.row.domNode) { - DOM.removeClass(item.row.domNode, 'drop-target'); + item.row.domNode.classList.remove('drop-target'); } } }); @@ -933,8 +1094,8 @@ export class ListView implements ISpliceable, IDisposable { private setupDragAndDropScrollTopAnimation(event: DragEvent): void { if (!this.dragOverAnimationDisposable) { - const viewTop = DOM.getTopLeftOffset(this.domNode).top; - this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); + const viewTop = getTopLeftOffset(this.domNode).top; + this.dragOverAnimationDisposable = animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); } this.dragOverAnimationStopDisposable.dispose(); @@ -975,9 +1136,10 @@ export class ListView implements ISpliceable, IDisposable { // Util private getItemIndexFromEventTarget(target: EventTarget | null): number | undefined { + const scrollableElement = this.scrollableElement.getDomNode(); let element: HTMLElement | null = target as (HTMLElement | null); - while (element instanceof HTMLElement && element !== this.rowsContainer) { + while (element instanceof HTMLElement && element !== this.rowsContainer && scrollableElement.contains(element)) { const rawIndex = element.getAttribute('data-index'); if (rawIndex) { @@ -1005,7 +1167,7 @@ export class ListView implements ISpliceable, IDisposable { * Given a stable rendered state, checks every rendered element whether it needs * to be probed for dynamic height. Adjusts scroll height and top if necessary. */ - private _rerender(renderTop: number, renderHeight: number): void { + private _rerender(renderTop: number, renderHeight: number, updateScrollTop: boolean = true): void { const previousRenderRange = this.getRenderRange(renderTop, renderHeight); // Let's remember the second element's position, this helps in scrolling up @@ -1071,7 +1233,7 @@ export class ListView implements ISpliceable, IDisposable { } } - if (typeof anchorElementIndex === 'number') { + if (updateScrollTop && typeof anchorElementIndex === 'number') { this.scrollTop = this.elementTop(anchorElementIndex) - anchorElementTopDelta!; } @@ -1088,7 +1250,19 @@ export class ListView implements ISpliceable, IDisposable { return 0; } + if (!!this.virtualDelegate.hasDynamicHeight && !this.virtualDelegate.hasDynamicHeight(item.element)) { + return 0; + } + const size = item.size; + + if (!this.setRowHeight && item.row && item.row.domNode) { + let newSize = item.row.domNode.offsetHeight; + item.size = newSize; + item.lastDynamicHeightWidth = this.renderWidth; + return newSize - size; + } + const row = this.cache.alloc(item.templateId); row.domNode!.style.height = ''; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 026344c5e1b..39d1bbed07c 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -4,20 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./list'; -import { localize } from 'vs/nls'; import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; -import { range, firstIndex, binarySearch } from 'vs/base/common/arrays'; +import { range, binarySearch } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; -import * as DOM from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole, ListError, IKeyboardNavigationDelegate } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaProvider } from './listView'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; @@ -26,6 +24,9 @@ import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; import { matchesPrefix } from 'vs/base/common/filters'; import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { IThemable } from 'vs/base/common/styler'; +import { createStyleSheet } from 'vs/base/browser/dom'; interface ITraitChangeEvent { indexes: number[]; @@ -54,7 +55,7 @@ class TraitRenderer implements IListRenderer } renderElement(element: T, index: number, templateData: ITraitTemplateData): void { - const renderedElementIndex = firstIndex(this.renderedElements, el => el.templateData === templateData); + const renderedElementIndex = this.renderedElements.findIndex(el => el.templateData === templateData); if (renderedElementIndex >= 0) { const rendered = this.renderedElements[renderedElementIndex]; @@ -95,7 +96,7 @@ class TraitRenderer implements IListRenderer } disposeTemplate(templateData: ITraitTemplateData): void { - const index = firstIndex(this.renderedElements, el => el.templateData === templateData); + const index = this.renderedElements.findIndex(el => el.templateData === templateData); if (index < 0) { return; @@ -136,11 +137,11 @@ class Trait implements ISpliceable, IDisposable { } renderIndex(index: number, container: HTMLElement): void { - DOM.toggleClass(container, this._trait, this.contains(index)); + container.classList.toggle(this._trait, this.contains(index)); } unrender(container: HTMLElement): void { - DOM.removeClass(container, this._trait); + container.classList.remove(this._trait); } /** @@ -180,19 +181,21 @@ class Trait implements ISpliceable, IDisposable { } } -class FocusTrait extends Trait { +class SelectionTrait extends Trait { - constructor() { - super('focused'); + constructor(private setAriaSelected: boolean) { + super('selected'); } renderIndex(index: number, container: HTMLElement): void { super.renderIndex(index, container); - if (this.contains(index)) { - container.setAttribute('aria-selected', 'true'); - } else { - container.removeAttribute('aria-selected'); + if (this.setAriaSelected) { + if (this.contains(index)) { + container.setAttribute('aria-selected', 'true'); + } else { + container.setAttribute('aria-selected', 'false'); + } } } } @@ -222,14 +225,29 @@ class TraitSpliceable implements ISpliceable { } } -function isInputElement(e: HTMLElement): boolean { +export function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } +export function isMonacoEditor(e: HTMLElement): boolean { + if (e.classList.contains('monaco-editor')) { + return true; + } + + if (e.classList.contains('monaco-list')) { + return false; + } + + if (!e.parentElement) { + return false; + } + + return isMonacoEditor(e.parentElement); +} + class KeyboardController implements IDisposable { private readonly disposables = new DisposableStore(); - private openController: IOpenController; constructor( private list: List, @@ -238,8 +256,6 @@ class KeyboardController implements IDisposable { ) { const multipleSelectionSupport = options.multipleSelectionSupport !== false; - this.openController = options.openController || DefaultOpenController; - const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -260,10 +276,6 @@ class KeyboardController implements IDisposable { e.preventDefault(); e.stopPropagation(); this.list.setSelection(this.list.getFocus(), e.browserEvent); - - if (this.openController.shouldOpen(e.browserEvent)) { - this.list.open(this.list.getFocus(), e.browserEvent); - } } private onUpArrow(e: StandardKeyboardEvent): void { @@ -342,6 +354,7 @@ class TypeLabelController implements IDisposable { private automaticKeyboardNavigation = true; private triggered = false; + private previouslyFocused = -1; private readonly enabledDisposables = new DisposableStore(); private readonly disposables = new DisposableStore(); @@ -391,6 +404,7 @@ class TypeLabelController implements IDisposable { const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i)); onInput(this.onInput, this, this.enabledDisposables); + onClear(this.onClear, this, this.enabledDisposables); this.enabled = true; this.triggered = false; @@ -406,6 +420,19 @@ class TypeLabelController implements IDisposable { this.triggered = false; } + private onClear(): void { + const focus = this.list.getFocus(); + if (focus.length > 0 && focus[0] === this.previouslyFocused) { + // List: re-anounce element on typing end since typed keys will interupt aria label of focused element + // Do not announce if there was a focus change at the end to prevent duplication https://github.com/microsoft/vscode/issues/95961 + const ariaLabel = this.list.options.accessibilityProvider?.getAriaLabel(this.list.element(focus[0])); + if (ariaLabel) { + alert(ariaLabel); + } + } + this.previouslyFocused = -1; + } + private onInput(word: string | null): void { if (!word) { this.state = TypeLabelControllerState.Idle; @@ -424,6 +451,7 @@ class TypeLabelController implements IDisposable { const labelStr = label && label.toString(); if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { + this.previouslyFocused = start; this.list.setFocus([index]); this.list.reveal(index); return; @@ -509,24 +537,16 @@ const DefaultMultipleSelectionController = { isSelectionRangeChangeEvent }; -const DefaultOpenController: IOpenController = { - shouldOpen: (event: UIEvent) => { - if (event instanceof MouseEvent) { - return !isMouseRightClick(event); - } - - return true; - } -}; - export class MouseController implements IDisposable { private multipleSelectionSupport: boolean; readonly multipleSelectionController: IMultipleSelectionController | undefined; - private openController: IOpenController; private mouseSupport: boolean; private readonly disposables = new DisposableStore(); + private _onPointer = new Emitter>(); + readonly onPointer: Event> = this._onPointer.event; + constructor(protected list: List) { this.multipleSelectionSupport = !(list.options.multipleSelectionSupport === false); @@ -534,7 +554,6 @@ export class MouseController implements IDisposable { this.multipleSelectionController = list.options.multipleSelectionController || DefaultMultipleSelectionController; } - this.openController = list.options.openController || DefaultOpenController; this.mouseSupport = typeof list.options.mouseSupport === 'undefined' || !!list.options.mouseSupport; if (this.mouseSupport) { @@ -545,9 +564,7 @@ export class MouseController implements IDisposable { this.disposables.add(Gesture.addTarget(list.getHTMLElement())); } - list.onMouseClick(this.onPointer, this, this.disposables); - list.onMouseMiddleClick(this.onPointer, this, this.disposables); - list.onTap(this.onPointer, this, this.disposables); + Event.any(list.onMouseClick, list.onMouseMiddleClick, list.onTap)(this.onViewPointer, this, this.disposables); } protected isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { @@ -571,22 +588,30 @@ export class MouseController implements IDisposable { } private onMouseDown(e: IListMouseEvent | IListTouchEvent): void { + if (isMonacoEditor(e.browserEvent.target as HTMLElement)) { + return; + } + if (document.activeElement !== e.browserEvent.target) { this.list.domFocus(); } } private onContextMenu(e: IListContextMenuEvent): void { + if (isMonacoEditor(e.browserEvent.target as HTMLElement)) { + return; + } + const focus = typeof e.index === 'undefined' ? [] : [e.index]; this.list.setFocus(focus, e.browserEvent); } - protected onPointer(e: IListMouseEvent): void { + protected onViewPointer(e: IListMouseEvent): void { if (!this.mouseSupport) { return; } - if (isInputElement(e.browserEvent.target as HTMLElement)) { + if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) { return; } @@ -614,15 +639,13 @@ export class MouseController implements IDisposable { if (!isMouseRightClick(e.browserEvent)) { this.list.setSelection([focus], e.browserEvent); - - if (this.openController.shouldOpen(e.browserEvent)) { - this.list.open([focus], e.browserEvent); - } } + + this._onPointer.fire(e); } protected onDoubleClick(e: IListMouseEvent): void { - if (isInputElement(e.browserEvent.target as HTMLElement)) { + if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) { return; } @@ -632,7 +655,6 @@ export class MouseController implements IDisposable { const focus = this.list.getFocus(); this.list.setSelection(focus, e.browserEvent); - this.list.pin(focus); } private changeSelection(e: IListMouseEvent | IListTouchEvent, reference: number | undefined): void { @@ -676,33 +698,15 @@ export interface IMultipleSelectionController { isSelectionRangeChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean; } -export interface IOpenController { - shouldOpen(event: UIEvent): boolean; -} - export interface IStyleController { style(styles: IListStyles): void; } -export interface IAccessibilityProvider { - - /** - * Given an element in the tree, return the ARIA label that should be associated with the - * item. This helps screen readers to provide a meaningful label for the currently focused - * tree element. - * - * Returning null will not disable ARIA for the element. Instead it is up to the screen reader - * to compute a meaningful label based on the contents of the element in the DOM - * - * See also: https://www.w3.org/TR/wai-aria/#aria-label - */ +export interface IListAccessibilityProvider extends IListViewAccessibilityProvider { getAriaLabel(element: T): string | null; - - /** - * https://www.w3.org/TR/wai-aria/#aria-level - */ + getWidgetAriaLabel(): string; + getWidgetRole?(): string; getAriaLevel?(element: T): number | undefined; - onDidChangeActiveDescendant?: Event; getActiveDescendantId?(element: T): string | undefined; } @@ -820,10 +824,7 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); } - const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; - } + this.styleElement.textContent = content.join('\n'); } } @@ -834,23 +835,23 @@ export interface IListOptions { readonly automaticKeyboardNavigation?: boolean; readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate; - readonly ariaRole?: ListAriaRootRole | string; - readonly ariaLabel?: string; readonly keyboardSupport?: boolean; readonly multipleSelectionSupport?: boolean; readonly multipleSelectionController?: IMultipleSelectionController; - readonly openController?: IOpenController; readonly styleController?: (suffix: string) => IStyleController; - readonly accessibilityProvider?: IAccessibilityProvider; + readonly accessibilityProvider?: IListAccessibilityProvider; // list view options readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; readonly setRowLineHeight?: boolean; + readonly setRowHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; - readonly ariaProvider?: IAriaProvider; + readonly additionalScrollHeight?: number; + readonly transformOptimization?: boolean; + readonly smoothScrolling?: boolean; } export interface IListStyles { @@ -890,7 +891,7 @@ const defaultStyles: IListStyles = { treeIndentGuidesStroke: Color.fromHex('#a9a9a9') }; -const DefaultOptions = { +const DefaultOptions: IListOptions = { keyboardSupport: true, mouseSupport: true, multipleSelectionSupport: true, @@ -899,8 +900,7 @@ const DefaultOptions = { onDragStart(): void { }, onDragOver() { return false; }, drop() { } - }, - ariaRootRole: ListAriaRootRole.TREE + } }; // TODO@Joao: move these utils into a SortedArray class @@ -1032,7 +1032,7 @@ class AccessibiltyRenderer implements IListRenderer { templateId: string = 'a18n'; - constructor(private accessibilityProvider: IAccessibilityProvider) { } + constructor(private accessibilityProvider: IListAccessibilityProvider) { } renderTemplate(container: HTMLElement): HTMLElement { return container; @@ -1104,43 +1104,40 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } } -export interface IListOptionsUpdate { +export interface IListOptionsUpdate extends IListViewOptionsUpdate { readonly enableKeyboardNavigation?: boolean; readonly automaticKeyboardNavigation?: boolean; } -export class List implements ISpliceable, IDisposable { +export class List implements ISpliceable, IThemable, IDisposable { private focus: Trait; private selection: Trait; private eventBufferer = new EventBufferer(); - private view: ListView; + protected view: ListView; private spliceable: ISpliceable; private styleController: IStyleController; private typeLabelController?: TypeLabelController; - private accessibilityProvider?: IAccessibilityProvider; + private accessibilityProvider?: IListAccessibilityProvider; + private mouseController: MouseController; + private _ariaLabel: string = ''; protected readonly disposables = new DisposableStore(); - @memoize get onFocusChange(): Event> { + @memoize get onDidChangeFocus(): Event> { return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); } - @memoize get onSelectionChange(): Event> { + @memoize get onDidChangeSelection(): Event> { return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e)); } - private readonly _onDidOpen = new Emitter>(); - readonly onDidOpen: Event> = this._onDidOpen.event; - - private readonly _onDidPin = new Emitter>(); - readonly onDidPin: Event> = this._onDidPin.event; - get domId(): string { return this.view.domId; } get onDidScroll(): Event { return this.view.onDidScroll; } get onMouseClick(): Event> { return this.view.onMouseClick; } get onMouseDblClick(): Event> { return this.view.onMouseDblClick; } get onMouseMiddleClick(): Event> { return this.view.onMouseMiddleClick; } + get onPointer(): Event> { return this.mouseController.onPointer; } get onMouseUp(): Event> { return this.view.onMouseUp; } get onMouseDown(): Event> { return this.view.onMouseDown; } get onMouseOver(): Event> { return this.view.onMouseOver; } @@ -1197,8 +1194,9 @@ export class List implements ISpliceable, IDisposable { renderers: IListRenderer[], private _options: IListOptions = DefaultOptions ) { - this.focus = new FocusTrait(); - this.selection = new Trait('selected'); + const role = this._options.accessibilityProvider && this._options.accessibilityProvider.getWidgetRole ? this._options.accessibilityProvider?.getWidgetRole() : 'list'; + this.selection = new SelectionTrait(role !== 'listbox'); + this.focus = new Trait('focused'); mixin(_options, defaultStyles, false); @@ -1222,17 +1220,12 @@ export class List implements ISpliceable, IDisposable { }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); - - if (typeof _options.ariaRole !== 'string') { - this.view.domNode.setAttribute('role', ListAriaRootRole.TREE); - } else { - this.view.domNode.setAttribute('role', _options.ariaRole); - } + this.view.domNode.setAttribute('role', role); if (_options.styleController) { this.styleController = _options.styleController(this.view.domId); } else { - const styleElement = DOM.createStyleSheet(this.view.domNode); + const styleElement = createStyleSheet(this.view.domNode); this.styleController = new DefaultStyleController(styleElement, this.view.domId); } @@ -1263,13 +1256,17 @@ export class List implements ISpliceable, IDisposable { this.disposables.add(this.typeLabelController); } - this.disposables.add(this.createMouseController(_options)); + this.mouseController = this.createMouseController(_options); + this.disposables.add(this.mouseController); - this.onFocusChange(this._onFocusChange, this, this.disposables); - this.onSelectionChange(this._onSelectionChange, this, this.disposables); + this.onDidChangeFocus(this._onFocusChange, this, this.disposables); + this.onDidChangeSelection(this._onSelectionChange, this, this.disposables); - if (_options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel)); + if (this.accessibilityProvider) { + this.ariaLabel = this.accessibilityProvider.getWidgetAriaLabel(); + } + if (_options.multipleSelectionSupport) { + this.view.domNode.setAttribute('aria-multiselectable', 'true'); } } @@ -1283,6 +1280,8 @@ export class List implements ISpliceable, IDisposable { if (this.typeLabelController) { this.typeLabelController.updateOptions(this._options); } + + this.view.updateOptions(optionsUpdate); } get options(): IListOptions { @@ -1309,6 +1308,10 @@ export class List implements ISpliceable, IDisposable { this.view.updateWidth(index); } + updateElementHeight(index: number, size: number): void { + this.view.updateElementHeight(index, size, null); + } + rerender(): void { this.view.rerender(); } @@ -1317,6 +1320,10 @@ export class List implements ISpliceable, IDisposable { return this.view.element(index); } + indexOf(element: T): number { + return this.view.indexOf(element); + } + get length(): number { return this.view.length; } @@ -1361,6 +1368,15 @@ export class List implements ISpliceable, IDisposable { return this.view.lastVisibleIndex; } + get ariaLabel(): string { + return this._ariaLabel; + } + + set ariaLabel(value: string) { + this._ariaLabel = value; + this.view.domNode.setAttribute('aria-label', value); + } + domFocus(): void { this.view.domNode.focus(); } @@ -1493,9 +1509,13 @@ export class List implements ISpliceable, IDisposable { } focusFirst(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { + this.focusNth(0, browserEvent, filter); + } + + focusNth(n: number, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - const index = this.findNextIndex(0, false, filter); + const index = this.findNextIndex(n, false, filter); if (index > -1) { this.setFocus([index], browserEvent); @@ -1603,26 +1623,6 @@ export class List implements ISpliceable, IDisposable { return this.view.domNode; } - open(indexes: number[], browserEvent?: UIEvent): void { - for (const index of indexes) { - if (index < 0 || index >= this.length) { - throw new ListError(this.user, `Invalid index ${index}`); - } - } - - this._onDidOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent }); - } - - pin(indexes: number[], browserEvent?: UIEvent): void { - for (const index of indexes) { - if (index < 0 || index >= this.length) { - throw new ListError(this.user, `Invalid index ${index}`); - } - } - - this._onDidPin.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent }); - } - style(styles: IListStyles): void { this.styleController.style(styles); } @@ -1633,7 +1633,7 @@ export class List implements ISpliceable, IDisposable { private _onFocusChange(): void { const focus = this.focus.get(); - DOM.toggleClass(this.view.domNode, 'element-focused', focus.length > 0); + this.view.domNode.classList.toggle('element-focused', focus.length > 0); this.onDidChangeActiveDescendant(); } @@ -1656,17 +1656,15 @@ export class List implements ISpliceable, IDisposable { private _onSelectionChange(): void { const selection = this.selection.get(); - DOM.toggleClass(this.view.domNode, 'selection-none', selection.length === 0); - DOM.toggleClass(this.view.domNode, 'selection-single', selection.length === 1); - DOM.toggleClass(this.view.domNode, 'selection-multiple', selection.length > 1); + this.view.domNode.classList.toggle('selection-none', selection.length === 0); + this.view.domNode.classList.toggle('selection-single', selection.length === 1); + this.view.domNode.classList.toggle('selection-multiple', selection.length > 1); } dispose(): void { this._onDidDispose.fire(); this.disposables.dispose(); - this._onDidOpen.dispose(); - this._onDidPin.dispose(); this._onDidDispose.dispose(); } } diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 25eb321997b..953a11d13b4 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -5,7 +5,7 @@ import { IListRenderer } from './list'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { $, removeClass } from 'vs/base/browser/dom'; +import { $ } from 'vs/base/browser/dom'; export interface IRow { domNode: HTMLElement | null; @@ -60,7 +60,7 @@ export class RowCache implements IDisposable { private releaseRow(row: IRow): void { const { domNode, templateId } = row; if (domNode) { - removeClass(domNode, 'scrolling'); + domNode.classList.remove('scrolling'); removeFromParent(domNode); } diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css deleted file mode 100644 index 1812044cd50..00000000000 --- a/src/vs/base/browser/ui/menu/menu.css +++ /dev/null @@ -1,221 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-menu .monaco-action-bar.vertical { - margin-left: 0; - overflow: visible; -} - -.monaco-menu .monaco-action-bar.vertical .actions-container { - display: block; -} - -.monaco-menu .monaco-action-bar.vertical .action-item { - padding: 0; - transform: none; - display: flex; -} - -.monaco-menu .monaco-action-bar.vertical .action-item.active { - transform: none; -} - -.monaco-menu .monaco-action-bar.vertical .action-menu-item { - flex: 1 1 auto; - display: flex; - height: 2em; - align-items: center; - position: relative; -} - -.monaco-menu .monaco-action-bar.vertical .action-label { - flex: 1 1 auto; - text-decoration: none; - padding: 0 1em; - background: none; - font-size: 12px; - line-height: 1; -} - -.monaco-menu .monaco-action-bar.vertical .keybinding, -.monaco-menu .monaco-action-bar.vertical .submenu-indicator { - display: inline-block; - flex: 2 1 auto; - padding: 0 1em; - text-align: right; - font-size: 12px; - line-height: 1; -} - -.monaco-menu .monaco-action-bar.vertical .submenu-indicator { - height: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon { - font-size: 16px !important; - display: flex; - align-items: center; -} - -.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before { - margin-left: auto; - margin-right: -20px; -} - -.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, -.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator { - opacity: 0.4; -} - -.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) { - display: inline-block; - box-sizing: border-box; - margin: 0; -} - -.monaco-menu .monaco-action-bar.vertical .action-item { - position: static; - overflow: visible; -} - -.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu { - position: absolute; -} - -.monaco-menu .monaco-action-bar.vertical .action-label.separator { - padding: 0.5em 0 0 0; - margin-bottom: 0.5em; - width: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .action-label.separator.text { - padding: 0.7em 1em 0.1em 1em; - font-weight: bold; - opacity: 1; -} - -.monaco-menu .monaco-action-bar.vertical .action-label:hover { - color: inherit; -} - -.monaco-menu .monaco-action-bar.vertical .menu-item-check { - position: absolute; - visibility: hidden; - width: 1em; - height: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { - visibility: visible; - display: flex; - align-items: center; - justify-content: center; -} - -/* Context Menu */ - -.context-view.monaco-menu-container { - outline: 0; - border: none; - animation: fadeIn 0.083s linear; -} - -.context-view.monaco-menu-container :focus, -.context-view.monaco-menu-container .monaco-action-bar.vertical:focus, -.context-view.monaco-menu-container .monaco-action-bar.vertical :focus { - outline: 0; -} - -.monaco-menu .monaco-action-bar.vertical .action-item { - border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ -} - - -/* High Contrast Theming */ -.hc-black .context-view.monaco-menu-container { - box-shadow: none; -} - -.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused { - background: none; -} - -/* Menubar styles */ - -.menubar { - display: flex; - flex-shrink: 1; - box-sizing: border-box; - height: 30px; - overflow: hidden; - flex-wrap: wrap; -} - -.fullscreen .menubar:not(.compact) { - margin: 0px; - padding: 0px 5px; -} - -.menubar > .menubar-menu-button { - align-items: center; - box-sizing: border-box; - padding: 0px 8px; - cursor: default; - -webkit-app-region: no-drag; - zoom: 1; - white-space: nowrap; - outline: 0; -} - -.menubar.compact { - flex-shrink: 0; -} - -.menubar.compact > .menubar-menu-button { - width: 100%; - height: 100%; - padding: 0px; -} - -.menubar .menubar-menu-items-holder { - position: absolute; - left: 0px; - opacity: 1; - z-index: 2000; -} - -.menubar .menubar-menu-items-holder.monaco-menu-container { - outline: 0; - border: none; -} - -.menubar .menubar-menu-items-holder.monaco-menu-container :focus { - outline: 0; -} - -.menubar .toolbar-toggle-more { - width: 20px; - height: 100%; -} - -.menubar.compact .toolbar-toggle-more { - position: absolute; - left: 0px; - top: 0px; - cursor: pointer; - width: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.menubar .toolbar-toggle-more { - padding: 0; - vertical-align: sub; -} - -.menubar.compact .toolbar-toggle-more::before { - content: "\eb94" !important; -} diff --git a/src/vs/base/browser/ui/menu/menu.svg b/src/vs/base/browser/ui/menu/menu.svg deleted file mode 100644 index 1b61c978229..00000000000 --- a/src/vs/base/browser/ui/menu/menu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index dfac50ca823..8b3da5df8d8 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./menu'; import * as nls from 'vs/nls'; import * as strings from 'vs/base/common/strings'; -import { IActionRunner, IAction, Action, IActionViewItem } from 'vs/base/common/actions'; -import { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionRunner, IAction, SubmenuAction, Separator, IActionViewItemProvider, EmptySubmenuAction } from 'vs/base/common/actions'; +import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; -import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses } from 'vs/base/browser/dom'; +import { EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -17,12 +16,20 @@ import { Color } from 'vs/base/common/color'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons'; +import { BaseActionViewItem, ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { formatRule } from 'vs/base/browser/ui/codicons/codiconStyles'; +import { isFirefox } from 'vs/base/browser/browser'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/; export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g; +const menuSelectionIcon = registerIcon('menu-selection', Codicon.check); +const menuSubmenuIcon = registerIcon('menu-submenu', Codicon.chevronRight); + export enum Direction { Right, Left @@ -37,6 +44,8 @@ export interface IMenuOptions { enableMnemonics?: boolean; anchorAlignment?: AnchorAlignment; expandDirection?: Direction; + useEventAsContext?: boolean; + submenuIds?: Set; } export interface IMenuStyles { @@ -50,12 +59,6 @@ export interface IMenuStyles { separatorColor?: Color; } -export class SubmenuAction extends Action { - constructor(label: string, public entries: ReadonlyArray, cssClass?: string) { - super(!!cssClass ? cssClass : 'submenu', label, '', true); - } -} - interface ISubMenuData { parent: Menu; submenu?: Menu; @@ -66,12 +69,14 @@ export class Menu extends ActionBar { private readonly menuDisposables: DisposableStore; private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; + static globalStyleSheet: HTMLStyleElement; + protected styleSheet: HTMLStyleElement | undefined; constructor(container: HTMLElement, actions: ReadonlyArray, options: IMenuOptions = {}) { - addClass(container, 'monaco-menu-container'); + container.classList.add('monaco-menu-container'); container.setAttribute('role', 'presentation'); const menuElement = document.createElement('div'); - addClass(menuElement, 'monaco-menu'); + menuElement.classList.add('monaco-menu'); menuElement.setAttribute('role', 'presentation'); super(menuElement, { @@ -80,7 +85,7 @@ export class Menu extends ActionBar { context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, - triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh ? [KeyCode.Space] : [])], keyDown: true } + triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true } }); this.menuElement = menuElement; @@ -91,6 +96,8 @@ export class Menu extends ActionBar { this.menuDisposables = this._register(new DisposableStore()); + this.initializeStyleSheet(container); + addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { const event = new StandardKeyboardEvent(e); @@ -163,7 +170,7 @@ export class Menu extends ActionBar { target = target.parentElement; } - if (hasClass(target, 'action-item')) { + if (target.classList.contains('action-item')) { const lastFocusedItem = this.focusedItem; this.setFocusedItem(target); @@ -193,23 +200,46 @@ export class Menu extends ActionBar { scrollElement.style.position = ''; this._register(addDisposableListener(scrollElement, EventType.MOUSE_UP, e => { - // Absorb clicks in menu dead space https://github.com/Microsoft/vscode/issues/63575 + // Absorb clicks in menu dead space https://github.com/microsoft/vscode/issues/63575 // We do this on the scroll element so the scroll bar doesn't dismiss the menu either e.preventDefault(); })); - menuElement.style.maxHeight = `${Math.max(10, window.innerHeight - container.getBoundingClientRect().top - 30)}px`; + menuElement.style.maxHeight = `${Math.max(10, window.innerHeight - container.getBoundingClientRect().top - 35)}px`; + + actions = actions.filter(a => { + if (options.submenuIds?.has(a.id)) { + console.warn(`Found submenu cycle: ${a.id}`); + return false; + } + + return true; + }); this.push(actions, { icon: true, label: true, isMenu: true }); container.appendChild(this.scrollableElement.getDomNode()); this.scrollableElement.scanDomNode(); - this.viewItems.filter(item => !(item instanceof MenuSeparatorActionViewItem)).forEach((item: IActionViewItem, index: number, array: any[]) => { + this.viewItems.filter(item => !(item instanceof MenuSeparatorActionViewItem)).forEach((item, index, array) => { (item as BaseMenuActionViewItem).updatePositionInSet(index + 1, array.length); }); } + private initializeStyleSheet(container: HTMLElement): void { + if (isInShadowDOM(container)) { + this.styleSheet = createStyleSheet(container); + this.styleSheet.textContent = MENU_WIDGET_CSS; + } else { + if (!Menu.globalStyleSheet) { + Menu.globalStyleSheet = createStyleSheet(); + Menu.globalStyleSheet.textContent = MENU_WIDGET_CSS; + } + + this.styleSheet = Menu.globalStyleSheet; + } + } + style(style: IMenuStyles): void { const container = this.getContainer(); @@ -294,7 +324,7 @@ export class Menu extends ActionBar { if (action instanceof Separator) { return new MenuSeparatorActionViewItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { - const menuActionViewItem = new SubmenuMenuActionViewItem(action, action.entries, parentData, options); + const menuActionViewItem = new SubmenuMenuActionViewItem(action, action.actions, parentData, { ...options, submenuIds: new Set([...(options.submenuIds || []), action.id]) }); if (options.enableMnemonics) { const mnemonic = menuActionViewItem.getMnemonic(); @@ -312,7 +342,7 @@ export class Menu extends ActionBar { return menuActionViewItem; } else { - const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics }; + const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics, useEventAsContext: options.useEventAsContext }; if (options.getKeyBinding) { const keybinding = options.getKeyBinding(action); if (keybinding) { @@ -363,7 +393,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { private cssClass: string; protected menuStyle: IMenuStyles | undefined; - constructor(ctx: any, action: IAction, options: IMenuItemOptions = {}) { + constructor(ctx: unknown, action: IAction, options: IMenuItemOptions = {}) { options.isMenu = true; super(action, action, options); @@ -390,12 +420,43 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } this._register(addDisposableListener(this.element, EventType.MOUSE_UP, e => { - if (e.defaultPrevented) { - return; + // removed default prevention as it conflicts + // with BaseActionViewItem #101537 + // add back if issues arise and link new issue + EventHelper.stop(e, true); + + // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard + // > Writing to the clipboard + // > You can use the "cut" and "copy" commands without any special + // permission if you are using them in a short-lived event handler + // for a user action (for example, a click handler). + + // => to get the Copy and Paste context menu actions working on Firefox, + // there should be no timeout here + if (isFirefox) { + const mouseEvent = new StandardMouseEvent(e); + + // Allowing right click to trigger the event causes the issue described below, + // but since the solution below does not work in FF, we must disable right click + if (mouseEvent.rightButton) { + return; + } + + this.onClick(e); } + // In all other cases, set timout to allow context menu cancellation to trigger + // otherwise the action will destroy the menu and a second context menu + // will still trigger for right click. + else { + setTimeout(() => { + this.onClick(e); + }, 0); + } + })); + + this._register(addDisposableListener(this.element, EventType.CONTEXT_MENU, e => { EventHelper.stop(e, true); - this.onClick(e); })); }, 100); @@ -422,7 +483,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } } - this.check = append(this.item, $('span.menu-item-check.codicon.codicon-check')); + this.check = append(this.item, $('span.menu-item-check' + menuSelectionIcon.cssSelector)); this.check.setAttribute('role', 'none'); this.label = append(this.item, $('span.action-label')); @@ -464,17 +525,21 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } updateLabel(): void { + if (!this.label) { + return; + } + if (this.options.label) { - let label = this.getAction().label; + clearNode(this.label); + + let label = stripCodicons(this.getAction().label); if (label) { const cleanLabel = cleanMnemonic(label); if (!this.options.enableMnemonics) { label = cleanLabel; } - if (this.label) { - this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&')); - } + this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&')); const matches = MENU_MNEMONIC_REGEX.exec(label); @@ -490,22 +555,25 @@ class BaseMenuActionViewItem extends BaseActionViewItem { escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(label); } + const replaceDoubleEscapes = (str: string) => str.replace(/&&/g, '&'); + if (escMatch) { - label = `${label.substr(0, escMatch.index)}${label.substr(escMatch.index + escMatch[0].length)}`; + this.label.append( + strings.ltrim(replaceDoubleEscapes(label.substr(0, escMatch.index)), ' '), + $('u', { 'aria-hidden': 'true' }, + escMatch[3]), + strings.rtrim(replaceDoubleEscapes(label.substr(escMatch.index + escMatch[0].length)), ' ')); + } else { + this.label.innerText = replaceDoubleEscapes(label).trim(); } - label = label.replace(/&&/g, '&'); if (this.item) { this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); } } else { - label = label.replace(/&&/g, '&'); + this.label.innerText = label.replace(/&&/g, '&').trim(); } } - - if (this.label) { - this.label.innerHTML = label.trim(); - } } } @@ -530,37 +598,37 @@ class BaseMenuActionViewItem extends BaseActionViewItem { updateClass(): void { if (this.cssClass && this.item) { - removeClasses(this.item, this.cssClass); + this.item.classList.remove(...this.cssClass.split(' ')); } if (this.options.icon && this.label) { this.cssClass = this.getAction().class || ''; - addClass(this.label, 'icon'); + this.label.classList.add('icon'); if (this.cssClass) { - addClasses(this.label, this.cssClass); + this.label.classList.add(...this.cssClass.split(' ')); } this.updateEnabled(); } else if (this.label) { - removeClass(this.label, 'icon'); + this.label.classList.remove('icon'); } } updateEnabled(): void { if (this.getAction().enabled) { if (this.element) { - removeClass(this.element, 'disabled'); + this.element.classList.remove('disabled'); } if (this.item) { - removeClass(this.item, 'disabled'); + this.item.classList.remove('disabled'); this.item.tabIndex = 0; } } else { if (this.element) { - addClass(this.element, 'disabled'); + this.element.classList.add('disabled'); } if (this.item) { - addClass(this.item, 'disabled'); + this.item.classList.add('disabled'); removeTabIndexAndUpdateFocus(this.item); } } @@ -572,11 +640,11 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } if (this.getAction().checked) { - addClass(this.item, 'checked'); + this.item.classList.add('checked'); this.item.setAttribute('role', 'menuitemcheckbox'); this.item.setAttribute('aria-checked', 'true'); } else { - removeClass(this.item, 'checked'); + this.item.classList.remove('checked'); this.item.setAttribute('role', 'menuitem'); this.item.setAttribute('aria-checked', 'false'); } @@ -591,7 +659,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { return; } - const isSelected = this.element && hasClass(this.element, 'focused'); + const isSelected = this.element && this.element.classList.contains('focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : undefined; const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; @@ -644,7 +712,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { }, 250); this.hideScheduler = new RunOnceScheduler(() => { - if (this.element && (!isAncestor(document.activeElement, this.element) && this.parentData.submenu === this.mysubmenu)) { + if (this.element && (!isAncestor(getActiveElement(), this.element) && this.parentData.submenu === this.mysubmenu)) { this.parentData.parent.focus(false); this.cleanupExistingSubmenu(true); } @@ -659,10 +727,10 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { } if (this.item) { - addClass(this.item, 'monaco-submenu-item'); + this.item.classList.add('monaco-submenu-item'); this.item.setAttribute('aria-haspopup', 'true'); this.updateAriaExpanded('false'); - this.submenuIndicator = append(this.item, $('span.submenu-indicator.codicon.codicon-chevron-right')); + this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector)); this.submenuIndicator.setAttribute('aria-hidden', 'true'); } @@ -678,7 +746,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this._register(addDisposableListener(this.element, EventType.KEY_DOWN, e => { let event = new StandardKeyboardEvent(e); - if (document.activeElement === this.item) { + if (getActiveElement() === this.item) { if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { EventHelper.stop(e, true); } @@ -698,7 +766,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { })); this._register(addDisposableListener(this.element, EventType.FOCUS_OUT, e => { - if (this.element && !isAncestor(document.activeElement, this.element)) { + if (this.element && !isAncestor(getActiveElement(), this.element)) { this.hideScheduler.schedule(); } })); @@ -724,7 +792,12 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { private cleanupExistingSubmenu(force: boolean): void { if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) { - this.parentData.submenu.dispose(); + + // disposal may throw if the submenu has already been removed + try { + this.parentData.submenu.dispose(); + } catch { } + this.parentData.submenu = undefined; this.updateAriaExpanded('false'); if (this.submenuContainer) { @@ -734,6 +807,33 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { } } + private calculateSubmenuMenuLayout(windowDimensions: Dimension, submenu: Dimension, entry: IDomNodePagePosition, expandDirection: Direction): { top: number, left: number } { + const ret = { top: 0, left: 0 }; + + // Start with horizontal + ret.left = layout(windowDimensions.width, submenu.width, { position: expandDirection === Direction.Right ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, offset: entry.left, size: entry.width }); + + // We don't have enough room to layout the menu fully, so we are overlapping the menu + if (ret.left >= entry.left && ret.left < entry.left + entry.width) { + if (entry.left + 10 + submenu.width <= windowDimensions.width) { + ret.left = entry.left + 10; + } + + entry.top += 10; + entry.height = 0; + } + + // Now that we have a horizontal position, try layout vertically + ret.top = layout(windowDimensions.height, submenu.height, { position: LayoutAnchorPosition.Before, offset: entry.top, size: 0 }); + + // We didn't have enough room below, but we did above, so we shift down to align the menu + if (ret.top + submenu.height === entry.top && ret.top + entry.height + submenu.height <= windowDimensions.height) { + ret.top += entry.height; + } + + return ret; + } + private createSubmenu(selectFirstItem = true): void { if (!this.element) { return; @@ -742,35 +842,37 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { if (!this.parentData.submenu) { this.updateAriaExpanded('true'); this.submenuContainer = append(this.element, $('div.monaco-submenu')); - addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view'); + this.submenuContainer.classList.add('menubar-menu-items-holder', 'context-view'); // Set the top value of the menu container before construction // This allows the menu constructor to calculate the proper max height const computedStyles = getComputedStyle(this.parentData.parent.domNode); const paddingTop = parseFloat(computedStyles.paddingTop || '0') || 0; - this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; + // this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; + this.submenuContainer.style.zIndex = '1'; + this.submenuContainer.style.position = 'fixed'; + this.submenuContainer.style.top = '0'; + this.submenuContainer.style.left = '0'; - this.parentData.submenu = new Menu(this.submenuContainer, this.submenuActions, this.submenuOptions); + this.parentData.submenu = new Menu(this.submenuContainer, this.submenuActions.length ? this.submenuActions : [new EmptySubmenuAction()], this.submenuOptions); if (this.menuStyle) { this.parentData.submenu.style(this.menuStyle); } - const boundingRect = this.element.getBoundingClientRect(); - const childBoundingRect = this.submenuContainer.getBoundingClientRect(); + // layout submenu + const entryBox = this.element.getBoundingClientRect(); + const entryBoxUpdated = { + top: entryBox.top - paddingTop, + left: entryBox.left, + height: entryBox.height + 2 * paddingTop, + width: entryBox.width + }; - if (this.expandDirection === Direction.Right) { - if (window.innerWidth <= boundingRect.right + childBoundingRect.width) { - this.submenuContainer.style.left = '10px'; - this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset + boundingRect.height}px`; - } else { - this.submenuContainer.style.left = `${this.element.offsetWidth}px`; - this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; - } - } else if (this.expandDirection === Direction.Left) { - this.submenuContainer.style.right = `${this.element.offsetWidth}px`; - this.submenuContainer.style.left = 'auto'; - this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; - } + const viewBox = this.submenuContainer.getBoundingClientRect(); + + const { top, left } = this.calculateSubmenuMenuLayout(new Dimension(window.innerWidth, window.innerHeight), Dimension.lift(viewBox), entryBoxUpdated, this.expandDirection); + this.submenuContainer.style.left = `${left}px`; + this.submenuContainer.style.top = `${top}px`; this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { let event = new StandardKeyboardEvent(e); @@ -818,7 +920,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { return; } - const isSelected = this.element && hasClass(this.element, 'focused'); + const isSelected = this.element && this.element.classList.contains('focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; if (this.submenuIndicator) { @@ -866,3 +968,400 @@ export function cleanMnemonic(label: string): string { return label.replace(regex, mnemonicInText ? '$2$3' : '').trim(); } + +let MENU_WIDGET_CSS: string = /* css */` +.monaco-menu { + font-size: 13px; + +} + +${formatRule(menuSelectionIcon)} +${formatRule(menuSubmenuIcon)} + +.monaco-menu .monaco-action-bar { + text-align: right; + overflow: hidden; + white-space: nowrap; +} + +.monaco-menu .monaco-action-bar .actions-container { + display: flex; + margin: 0 auto; + padding: 0; + width: 100%; + justify-content: flex-end; +} + +.monaco-menu .monaco-action-bar.vertical .actions-container { + display: inline-block; +} + +.monaco-menu .monaco-action-bar.reverse .actions-container { + flex-direction: row-reverse; +} + +.monaco-menu .monaco-action-bar .action-item { + cursor: pointer; + display: inline-block; + transition: transform 50ms ease; + position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */ +} + +.monaco-menu .monaco-action-bar .action-item.disabled { + cursor: default; +} + +.monaco-menu .monaco-action-bar.animated .action-item.active { + transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */ +} + +.monaco-menu .monaco-action-bar .action-item .icon, +.monaco-menu .monaco-action-bar .action-item .codicon { + display: inline-block; +} + +.monaco-menu .monaco-action-bar .action-item .codicon { + display: flex; + align-items: center; +} + +.monaco-menu .monaco-action-bar .action-label { + font-size: 11px; + margin-right: 4px; +} + +.monaco-menu .monaco-action-bar .action-item.disabled .action-label, +.monaco-menu .monaco-action-bar .action-item.disabled .action-label:hover { + opacity: 0.4; +} + +/* Vertical actions */ + +.monaco-menu .monaco-action-bar.vertical { + text-align: left; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + display: block; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator { + display: block; + border-bottom: 1px solid #bbb; + padding-top: 1px; + margin-left: .8em; + margin-right: .8em; +} + +.monaco-menu .secondary-actions .monaco-action-bar .action-label { + margin-left: 6px; +} + +/* Action Items */ +.monaco-menu .monaco-action-bar .action-item.select-container { + overflow: hidden; /* somehow the dropdown overflows its container, we prevent it here to not push */ + flex: 1; + max-width: 170px; + min-width: 60px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; +} + +.monaco-menu .monaco-action-bar.vertical { + margin-left: 0; + overflow: visible; +} + +.monaco-menu .monaco-action-bar.vertical .actions-container { + display: block; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + padding: 0; + transform: none; + display: flex; +} + +.monaco-menu .monaco-action-bar.vertical .action-item.active { + transform: none; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item { + flex: 1 1 auto; + display: flex; + height: 2em; + align-items: center; + position: relative; +} + +.monaco-menu .monaco-action-bar.vertical .action-label { + flex: 1 1 auto; + text-decoration: none; + padding: 0 1em; + background: none; + font-size: 12px; + line-height: 1; +} + +.monaco-menu .monaco-action-bar.vertical .keybinding, +.monaco-menu .monaco-action-bar.vertical .submenu-indicator { + display: inline-block; + flex: 2 1 auto; + padding: 0 1em; + text-align: right; + font-size: 12px; + line-height: 1; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator { + height: 100%; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon { + font-size: 16px !important; + display: flex; + align-items: center; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before { + margin-left: auto; + margin-right: -20px; +} + +.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, +.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator { + opacity: 0.4; +} + +.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) { + display: inline-block; + box-sizing: border-box; + margin: 0; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + position: static; + overflow: visible; +} + +.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu { + position: absolute; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator { + padding: 0.5em 0 0 0; + margin-bottom: 0.5em; + width: 100%; + height: 0px !important; + margin-left: .8em !important; + margin-right: .8em !important; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator.text { + padding: 0.7em 1em 0.1em 1em; + font-weight: bold; + opacity: 1; +} + +.monaco-menu .monaco-action-bar.vertical .action-label:hover { + color: inherit; +} + +.monaco-menu .monaco-action-bar.vertical .menu-item-check { + position: absolute; + visibility: hidden; + width: 1em; + height: 100%; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { + visibility: visible; + display: flex; + align-items: center; + justify-content: center; +} + +/* Context Menu */ + +.context-view.monaco-menu-container { + outline: 0; + border: none; + animation: fadeIn 0.083s linear; +} + +.context-view.monaco-menu-container :focus, +.context-view.monaco-menu-container .monaco-action-bar.vertical:focus, +.context-view.monaco-menu-container .monaco-action-bar.vertical :focus { + outline: 0; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ +} + + +/* High Contrast Theming */ +:host-context(.hc-black) .context-view.monaco-menu-container { + box-shadow: none; +} + +:host-context(.hc-black) .monaco-menu .monaco-action-bar.vertical .action-item.focused { + background: none; +} + +/* Vertical Action Bar Styles */ + +.monaco-menu .monaco-action-bar.vertical { + padding: .5em 0; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item { + height: 1.8em; +} + +.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), +.monaco-menu .monaco-action-bar.vertical .keybinding { + font-size: inherit; + padding: 0 2em; +} + +.monaco-menu .monaco-action-bar.vertical .menu-item-check { + font-size: inherit; + width: 2em; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator { + font-size: inherit; + padding: 0.2em 0 0 0; + margin-bottom: 0.2em; +} + +:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .action-label.separator { + margin-left: 0; + margin-right: 0; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator { + font-size: 60%; + padding: 0 1.8em; +} + +:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator { + height: 100%; + mask-size: 10px 10px; + -webkit-mask-size: 10px 10px; +} + +.monaco-menu .action-item { + cursor: default; +} + +/* Arrows */ +.monaco-scrollable-element > .scrollbar > .scra { + cursor: pointer; + font-size: 11px !important; +} + +.monaco-scrollable-element > .visible { + opacity: 1; + + /* Background rule added for IE9 - to allow clicks on dom node */ + background:rgba(0,0,0,0); + + transition: opacity 100ms linear; +} +.monaco-scrollable-element > .invisible { + opacity: 0; + pointer-events: none; +} +.monaco-scrollable-element > .invisible.fade { + transition: opacity 800ms linear; +} + +/* Scrollable Content Inset Shadow */ +.monaco-scrollable-element > .shadow { + position: absolute; + display: none; +} +.monaco-scrollable-element > .shadow.top { + display: block; + top: 0; + left: 3px; + height: 3px; + width: 100%; + box-shadow: #DDD 0 6px 6px -6px inset; +} +.monaco-scrollable-element > .shadow.left { + display: block; + top: 3px; + left: 0; + height: 100%; + width: 3px; + box-shadow: #DDD 6px 0 6px -6px inset; +} +.monaco-scrollable-element > .shadow.top-left-corner { + display: block; + top: 0; + left: 0; + height: 3px; + width: 3px; +} +.monaco-scrollable-element > .shadow.top.left { + box-shadow: #DDD 6px 6px 6px -6px inset; +} + +/* ---------- Default Style ---------- */ + +:host-context(.vs) .monaco-scrollable-element > .scrollbar > .slider { + background: rgba(100, 100, 100, .4); +} +:host-context(.vs-dark) .monaco-scrollable-element > .scrollbar > .slider { + background: rgba(121, 121, 121, .4); +} +:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider { + background: rgba(111, 195, 223, .6); +} + +.monaco-scrollable-element > .scrollbar > .slider:hover { + background: rgba(100, 100, 100, .7); +} +:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider:hover { + background: rgba(111, 195, 223, .8); +} + +.monaco-scrollable-element > .scrollbar > .slider.active { + background: rgba(0, 0, 0, .6); +} +:host-context(.vs-dark) .monaco-scrollable-element > .scrollbar > .slider.active { + background: rgba(191, 191, 191, .4); +} +:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider.active { + background: rgba(111, 195, 223, 1); +} + +:host-context(.vs-dark) .monaco-scrollable-element .shadow.top { + box-shadow: none; +} + +:host-context(.vs-dark) .monaco-scrollable-element .shadow.left { + box-shadow: #000 6px 0 6px -6px inset; +} + +:host-context(.vs-dark) .monaco-scrollable-element .shadow.top.left { + box-shadow: #000 6px 6px 6px -6px inset; +} + +:host-context(.hc-black) .monaco-scrollable-element .shadow.top { + box-shadow: none; +} + +:host-context(.hc-black) .monaco-scrollable-element .shadow.left { + box-shadow: none; +} + +:host-context(.hc-black) .monaco-scrollable-element .shadow.top.left { + box-shadow: none; +} +`; diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css new file mode 100644 index 00000000000..bfa79a1c523 --- /dev/null +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Menubar styles */ + +.menubar { + display: flex; + flex-shrink: 1; + box-sizing: border-box; + height: 30px; + overflow: hidden; + flex-wrap: wrap; +} + +.fullscreen .menubar:not(.compact) { + margin: 0px; + padding: 0px 5px; +} + +.menubar > .menubar-menu-button { + align-items: center; + box-sizing: border-box; + padding: 0px 8px; + cursor: default; + -webkit-app-region: no-drag; + zoom: 1; + white-space: nowrap; + outline: 0; +} + +.menubar.compact { + flex-shrink: 0; + overflow: visible; /* to avoid the compact menu to be repositioned when clicking */ +} + +.menubar.compact > .menubar-menu-button { + width: 100%; + height: 100%; + padding: 0px; +} + +.menubar .menubar-menu-items-holder { + position: absolute; + left: 0px; + opacity: 1; + z-index: 2000; +} + +.menubar.compact .menubar-menu-items-holder { + position: fixed; +} + +.menubar .menubar-menu-items-holder.monaco-menu-container { + outline: 0; + border: none; +} + +.menubar .menubar-menu-items-holder.monaco-menu-container :focus { + outline: 0; +} + +.menubar .toolbar-toggle-more { + width: 20px; + height: 100%; +} + +.menubar.compact .toolbar-toggle-more { + position: relative; + left: 0px; + top: 0px; + cursor: pointer; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.menubar .toolbar-toggle-more { + padding: 0; + vertical-align: sub; +} + +.menubar.compact .toolbar-toggle-more::before { + content: "\eb94" !important; +} diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 767b62dabec..cb2b3fc0cac 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -3,27 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./menubar'; import * as browser from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import * as strings from 'vs/base/common/strings'; import * as nls from 'vs/nls'; -import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; -import { cleanMnemonic, IMenuOptions, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MNEMONIC_REGEX, SubmenuAction, IMenuStyles, Direction } from 'vs/base/browser/ui/menu/menu'; -import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; +import { cleanMnemonic, IMenuOptions, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MNEMONIC_REGEX, IMenuStyles, Direction } from 'vs/base/browser/ui/menu/menu'; +import { ActionRunner, IAction, IActionRunner, SubmenuAction, Separator } from 'vs/base/common/actions'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { KeyCode, ResolvedKeybinding, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { asArray } from 'vs/base/common/arrays'; import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; import { isMacintosh } from 'vs/base/common/platform'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; const $ = DOM.$; +const menuBarMoreIcon = registerIcon('menubar-more', Codicon.more); + export interface IMenuBarOptions { enableMnemonics?: boolean; disableAltFocus?: boolean; @@ -31,10 +34,11 @@ export interface IMenuBarOptions { getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined; alwaysOnMnemonics?: boolean; compactMode?: Direction; + getCompactMenuActions?: () => IAction[] } export interface MenuBarMenu { - actions: ReadonlyArray; + actions: IAction[]; label: string; } @@ -53,7 +57,7 @@ export class MenuBar extends Disposable { buttonElement: HTMLElement; titleElement: HTMLElement; label: string; - actions?: ReadonlyArray; + actions?: IAction[]; }[]; private overflowMenu!: { @@ -88,14 +92,14 @@ export class MenuBar extends Disposable { private numMenusShown: number = 0; private menuStyle: IMenuStyles | undefined; - private overflowLayoutScheduled: IDisposable | null = null; + private overflowLayoutScheduled: IDisposable | undefined = undefined; constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) { super(); this.container.setAttribute('role', 'menubar'); if (this.options.compactMode !== undefined) { - DOM.addClass(this.container, 'compact'); + this.container.classList.add('compact'); } this.menuCache = []; @@ -115,7 +119,7 @@ export class MenuBar extends Disposable { this.setUnfocusedState(); })); - this._register(ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this)); + this._register(DOM.ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this)); this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); @@ -135,8 +139,8 @@ export class MenuBar extends Disposable { eventHandled = false; } - // Never allow default tab behavior - if (event.equals(KeyCode.Tab | KeyMod.Shift) || event.equals(KeyCode.Tab)) { + // Never allow default tab behavior when not compact + if (this.options.compactMode === undefined && (event.equals(KeyCode.Tab | KeyMod.Shift) || event.equals(KeyCode.Tab))) { event.preventDefault(); } @@ -310,8 +314,8 @@ export class MenuBar extends Disposable { createOverflowMenu(): void { const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More'); const title = this.options.compactMode !== undefined ? label : undefined; - const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': title, 'aria-haspopup': true }); - const titleElement = $('div.menubar-menu-title.toolbar-toggle-more.codicon.codicon-more', { 'role': 'none', 'aria-hidden': true }); + const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.options.compactMode !== undefined ? 0 : -1, 'aria-label': label, 'title': title, 'aria-haspopup': true }); + const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + menuBarMoreIcon.cssSelector, { 'role': 'none', 'aria-hidden': true }); buttonElement.appendChild(titleElement); this.container.appendChild(buttonElement); @@ -321,7 +325,15 @@ export class MenuBar extends Disposable { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; - if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) { + const triggerKeys = [KeyCode.Enter]; + if (this.options.compactMode === undefined) { + triggerKeys.push(KeyCode.DownArrow); + } else { + triggerKeys.push(KeyCode.Space); + triggerKeys.push(this.options.compactMode === Direction.Right ? KeyCode.RightArrow : KeyCode.LeftArrow); + } + + if ((triggerKeys.some(k => event.equals(k)) && !this.isOpen)) { this.focusedMenu = { index: MenuBar.OVERFLOW_INDEX }; this.openedViaKeyboard = true; this.focusState = MenubarState.OPEN; @@ -412,16 +424,15 @@ export class MenuBar extends Disposable { super.dispose(); this.menuCache.forEach(menuBarMenu => { - DOM.removeNode(menuBarMenu.titleElement); - DOM.removeNode(menuBarMenu.buttonElement); + menuBarMenu.titleElement.remove(); + menuBarMenu.buttonElement.remove(); }); - DOM.removeNode(this.overflowMenu.titleElement); - DOM.removeNode(this.overflowMenu.buttonElement); + this.overflowMenu.titleElement.remove(); + this.overflowMenu.buttonElement.remove(); - if (this.overflowLayoutScheduled) { - this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled); - } + dispose(this.overflowLayoutScheduled); + this.overflowLayoutScheduled = undefined; } blur(): void { @@ -442,6 +453,16 @@ export class MenuBar extends Disposable { return this.container.clientHeight; } + toggleFocus(): void { + if (!this.isFocused && this.options.visibility !== 'hidden') { + this.mnemonicsInUse = true; + this.focusedMenu = { index: this.numMenusShown > 0 ? 0 : MenuBar.OVERFLOW_INDEX }; + this.focusState = MenubarState.FOCUSED; + } else if (!this.isOpen) { + this.setUnfocusedState(); + } + } + private updateOverflowAction(): void { if (!this.menuCache || !this.menuCache.length) { return; @@ -483,16 +504,22 @@ export class MenuBar extends Disposable { this.overflowMenu.actions = []; for (let idx = this.numMenusShown; idx < this.menuCache.length; idx++) { - this.overflowMenu.actions.push(new SubmenuAction(this.menuCache[idx].label, this.menuCache[idx].actions || [])); + this.overflowMenu.actions.push(new SubmenuAction(`menubar.submenu.${this.menuCache[idx].label}`, this.menuCache[idx].label, this.menuCache[idx].actions || [])); } if (this.overflowMenu.buttonElement.nextElementSibling !== this.menuCache[this.numMenusShown].buttonElement) { - DOM.removeNode(this.overflowMenu.buttonElement); + this.overflowMenu.buttonElement.remove(); this.container.insertBefore(this.overflowMenu.buttonElement, this.menuCache[this.numMenusShown].buttonElement); this.overflowMenu.buttonElement.style.visibility = 'visible'; } + + const compactMenuActions = this.options.getCompactMenuActions?.(); + if (compactMenuActions && compactMenuActions.length) { + this.overflowMenu.actions.push(new Separator()); + this.overflowMenu.actions.push(...compactMenuActions); + } } else { - DOM.removeNode(this.overflowMenu.buttonElement); + this.overflowMenu.buttonElement.remove(); this.container.appendChild(this.overflowMenu.buttonElement); this.overflowMenu.buttonElement.style.visibility = 'hidden'; } @@ -504,25 +531,31 @@ export class MenuBar extends Disposable { // Update the button label to reflect mnemonics if (this.options.enableMnemonics) { - let innerHtml = strings.escape(label); + let cleanLabel = strings.escape(label); // This is global so reset it MENU_ESCAPED_MNEMONIC_REGEX.lastIndex = 0; - let escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(innerHtml); + let escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(cleanLabel); // We can't use negative lookbehind so we match our negative and skip while (escMatch && escMatch[1]) { - escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(innerHtml); + escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(cleanLabel); } + const replaceDoubleEscapes = (str: string) => str.replace(/&&/g, '&'); + if (escMatch) { - innerHtml = `${innerHtml.substr(0, escMatch.index)}${innerHtml.substr(escMatch.index + escMatch[0].length)}`; + titleElement.innerText = ''; + titleElement.append( + strings.ltrim(replaceDoubleEscapes(cleanLabel.substr(0, escMatch.index)), ' '), + $('mnemonic', { 'aria-hidden': 'true' }, escMatch[3]), + strings.rtrim(replaceDoubleEscapes(cleanLabel.substr(escMatch.index + escMatch[0].length)), ' ') + ); + } else { + titleElement.innerText = replaceDoubleEscapes(cleanLabel).trim(); } - - innerHtml = innerHtml.replace(/&&/g, '&'); - titleElement.innerHTML = innerHtml; } else { - titleElement.innerHTML = cleanMenuLabel.replace(/&&/g, '&'); + titleElement.innerText = cleanMenuLabel.replace(/&&/g, '&'); } let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); @@ -561,7 +594,7 @@ export class MenuBar extends Disposable { if (!this.overflowLayoutScheduled) { this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => { this.updateOverflowAction(); - this.overflowLayoutScheduled = null; + this.overflowLayoutScheduled = undefined; }); } @@ -826,8 +859,8 @@ export class MenuBar extends Disposable { } } - private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void { - const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; + private onModifierKeyToggled(modifierKeyStatus: DOM.IModifierKeyStatus): void { + const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey && !modifierKeyStatus.metaKey; if (this.options.visibility === 'hidden') { return; @@ -889,7 +922,7 @@ export class MenuBar extends Disposable { if (this.focusedMenu.holder) { if (this.focusedMenu.holder.parentElement) { - DOM.removeClass(this.focusedMenu.holder.parentElement, 'open'); + this.focusedMenu.holder.parentElement.classList.remove('open'); } this.focusedMenu.holder.remove(); @@ -913,19 +946,20 @@ export class MenuBar extends Disposable { const menuHolder = $('div.menubar-menu-items-holder', { 'title': '' }); - DOM.addClass(customMenu.buttonElement, 'open'); + customMenu.buttonElement.classList.add('open'); + + const buttonBoundingRect = customMenu.buttonElement.getBoundingClientRect(); if (this.options.compactMode === Direction.Right) { - menuHolder.style.top = `0px`; - menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left + this.container.clientWidth}px`; + menuHolder.style.top = `${buttonBoundingRect.top}px`; + menuHolder.style.left = `${buttonBoundingRect.left + this.container.clientWidth}px`; } else if (this.options.compactMode === Direction.Left) { - menuHolder.style.top = `0px`; + menuHolder.style.top = `${buttonBoundingRect.top}px`; menuHolder.style.right = `${this.container.clientWidth}px`; menuHolder.style.left = 'auto'; - console.log(customMenu.buttonElement.getBoundingClientRect().right - this.container.clientWidth); } else { menuHolder.style.top = `${this.container.clientHeight}px`; - menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left}px`; + menuHolder.style.left = `${buttonBoundingRect.left}px`; } customMenu.buttonElement.appendChild(menuHolder); @@ -935,7 +969,8 @@ export class MenuBar extends Disposable { actionRunner: this.actionRunner, enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics), ariaLabel: withNullAsUndefined(customMenu.buttonElement.getAttribute('aria-label')), - expandDirection: this.options.compactMode !== undefined ? this.options.compactMode : Direction.Right + expandDirection: this.options.compactMode !== undefined ? this.options.compactMode : Direction.Right, + useEventAsContext: true }; let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); @@ -960,119 +995,3 @@ export class MenuBar extends Disposable { }; } } - -type ModifierKey = 'alt' | 'ctrl' | 'shift'; - -interface IModifierKeyStatus { - altKey: boolean; - shiftKey: boolean; - ctrlKey: boolean; - lastKeyPressed?: ModifierKey; - lastKeyReleased?: ModifierKey; - event?: KeyboardEvent; -} - - -class ModifierKeyEmitter extends Emitter { - - private readonly _subscriptions = new DisposableStore(); - private _keyStatus: IModifierKeyStatus; - private static instance: ModifierKeyEmitter; - - private constructor() { - super(); - - this._keyStatus = { - altKey: false, - shiftKey: false, - ctrlKey: false - }; - - this._subscriptions.add(domEvent(document.body, 'keydown', true)(e => { - const event = new StandardKeyboardEvent(e); - - if (e.altKey && !this._keyStatus.altKey) { - this._keyStatus.lastKeyPressed = 'alt'; - } else if (e.ctrlKey && !this._keyStatus.ctrlKey) { - this._keyStatus.lastKeyPressed = 'ctrl'; - } else if (e.shiftKey && !this._keyStatus.shiftKey) { - this._keyStatus.lastKeyPressed = 'shift'; - } else if (event.keyCode !== KeyCode.Alt) { - this._keyStatus.lastKeyPressed = undefined; - } else { - return; - } - - this._keyStatus.altKey = e.altKey; - this._keyStatus.ctrlKey = e.ctrlKey; - this._keyStatus.shiftKey = e.shiftKey; - - if (this._keyStatus.lastKeyPressed) { - this._keyStatus.event = e; - this.fire(this._keyStatus); - } - })); - - this._subscriptions.add(domEvent(document.body, 'keyup', true)(e => { - if (!e.altKey && this._keyStatus.altKey) { - this._keyStatus.lastKeyReleased = 'alt'; - } else if (!e.ctrlKey && this._keyStatus.ctrlKey) { - this._keyStatus.lastKeyReleased = 'ctrl'; - } else if (!e.shiftKey && this._keyStatus.shiftKey) { - this._keyStatus.lastKeyReleased = 'shift'; - } else { - this._keyStatus.lastKeyReleased = undefined; - } - - if (this._keyStatus.lastKeyPressed !== this._keyStatus.lastKeyReleased) { - this._keyStatus.lastKeyPressed = undefined; - } - - this._keyStatus.altKey = e.altKey; - this._keyStatus.ctrlKey = e.ctrlKey; - this._keyStatus.shiftKey = e.shiftKey; - - if (this._keyStatus.lastKeyReleased) { - this._keyStatus.event = e; - this.fire(this._keyStatus); - } - })); - - this._subscriptions.add(domEvent(document.body, 'mousedown', true)(e => { - this._keyStatus.lastKeyPressed = undefined; - })); - - this._subscriptions.add(domEvent(document.body, 'mouseup', true)(e => { - this._keyStatus.lastKeyPressed = undefined; - })); - - this._subscriptions.add(domEvent(document.body, 'mousemove', true)(e => { - if (e.buttons) { - this._keyStatus.lastKeyPressed = undefined; - } - })); - - this._subscriptions.add(domEvent(window, 'blur')(e => { - this._keyStatus.lastKeyPressed = undefined; - this._keyStatus.lastKeyReleased = undefined; - this._keyStatus.altKey = false; - this._keyStatus.shiftKey = false; - this._keyStatus.shiftKey = false; - - this.fire(this._keyStatus); - })); - } - - static getInstance() { - if (!ModifierKeyEmitter.instance) { - ModifierKeyEmitter.instance = new ModifierKeyEmitter(); - } - - return ModifierKeyEmitter.instance; - } - - dispose() { - super.dispose(); - this._subscriptions.dispose(); - } -} diff --git a/src/vs/base/browser/ui/mouseCursor/mouseCursor.css b/src/vs/base/browser/ui/mouseCursor/mouseCursor.css new file mode 100644 index 00000000000..2a998c4d970 --- /dev/null +++ b/src/vs/base/browser/ui/mouseCursor/mouseCursor.css @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-mouse-cursor-text { + cursor: text; +} + +/* The following selector looks a bit funny, but that is needed to cover all the workbench and the editor!! */ +.vs-dark .mac .monaco-mouse-cursor-text, .hc-black .mac .monaco-mouse-cursor-text, +.vs-dark.mac .monaco-mouse-cursor-text, .hc-black.mac .monaco-mouse-cursor-text { + cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text; +} diff --git a/src/vs/base/browser/ui/mouseCursor/mouseCursor.ts b/src/vs/base/browser/ui/mouseCursor/mouseCursor.ts new file mode 100644 index 00000000000..8e5fd174cd8 --- /dev/null +++ b/src/vs/base/browser/ui/mouseCursor/mouseCursor.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./mouseCursor'; + +export const MOUSE_CURSOR_TEXT_CSS_CLASS_NAME = `monaco-mouse-cursor-text`; diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index 198d8e2310e..5e47bcb1388 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -7,16 +7,14 @@ import 'vs/css!./progressbar'; import { Disposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; -import { removeClasses, addClass, hasClass, addClasses, removeClass, hide, show } from 'vs/base/browser/dom'; +import { hide, show } from 'vs/base/browser/dom'; import { RunOnceScheduler } from 'vs/base/common/async'; import { isNumber } from 'vs/base/common/types'; -const css_done = 'done'; -const css_active = 'active'; -const css_infinite = 'infinite'; -const css_discrete = 'discrete'; -const css_progress_container = 'monaco-progress-container'; -const css_progress_bit = 'progress-bit'; +const CSS_DONE = 'done'; +const CSS_ACTIVE = 'active'; +const CSS_INFINITE = 'infinite'; +const CSS_DISCRETE = 'discrete'; export interface IProgressBarOptions extends IProgressBarStyles { } @@ -58,11 +56,12 @@ export class ProgressBar extends Disposable { private create(container: HTMLElement): void { this.element = document.createElement('div'); - addClass(this.element, css_progress_container); + this.element.classList.add('monaco-progress-container'); + this.element.setAttribute('role', 'progressbar'); container.appendChild(this.element); this.bit = document.createElement('div'); - addClass(this.bit, css_progress_bit); + this.bit.classList.add('progress-bit'); this.element.appendChild(this.bit); this.applyStyles(); @@ -71,7 +70,7 @@ export class ProgressBar extends Disposable { private off(): void { this.bit.style.width = 'inherit'; this.bit.style.opacity = '1'; - removeClasses(this.element, css_active, css_infinite, css_discrete); + this.element.classList.remove(CSS_ACTIVE, CSS_INFINITE, CSS_DISCRETE); this.workedVal = 0; this.totalWork = undefined; @@ -92,10 +91,10 @@ export class ProgressBar extends Disposable { } private doDone(delayed: boolean): ProgressBar { - addClass(this.element, css_done); + this.element.classList.add(CSS_DONE); // let it grow to 100% width and hide afterwards - if (!hasClass(this.element, css_infinite)) { + if (!this.element.classList.contains(CSS_INFINITE)) { this.bit.style.width = 'inherit'; if (delayed) { @@ -125,8 +124,8 @@ export class ProgressBar extends Disposable { this.bit.style.width = '2%'; this.bit.style.opacity = '1'; - removeClasses(this.element, css_discrete, css_done); - addClasses(this.element, css_active, css_infinite); + this.element.classList.remove(CSS_DISCRETE, CSS_DONE); + this.element.classList.add(CSS_ACTIVE, CSS_INFINITE); return this; } @@ -138,6 +137,7 @@ export class ProgressBar extends Disposable { total(value: number): ProgressBar { this.workedVal = 0; this.totalWork = value; + this.element.setAttribute('aria-valuemax', value.toString()); return this; } @@ -173,21 +173,9 @@ export class ProgressBar extends Disposable { this.workedVal = value; this.workedVal = Math.min(totalWork, this.workedVal); - if (hasClass(this.element, css_infinite)) { - removeClass(this.element, css_infinite); - } - - if (hasClass(this.element, css_done)) { - removeClass(this.element, css_done); - } - - if (!hasClass(this.element, css_active)) { - addClass(this.element, css_active); - } - - if (!hasClass(this.element, css_discrete)) { - addClass(this.element, css_discrete); - } + this.element.classList.remove(CSS_INFINITE, CSS_DONE); + this.element.classList.add(CSS_ACTIVE, CSS_DISCRETE); + this.element.setAttribute('aria-valuenow', value.toString()); this.bit.style.width = 100 * (this.workedVal / (totalWork)) + '%'; diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index 27be21169e7..9db2f04e0ab 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +:root { + --sash-size: 4px; +} + .monaco-sash { position: absolute; z-index: 35; @@ -13,13 +17,6 @@ pointer-events: none; } -.monaco-sash.vertical { - cursor: ew-resize; - top: 0; - width: 4px; - height: 100%; -} - .monaco-sash.mac.vertical { cursor: col-resize; } @@ -32,13 +29,6 @@ cursor: w-resize; } -.monaco-sash.horizontal { - cursor: ns-resize; - left: 0; - width: 100%; - height: 4px; -} - .monaco-sash.mac.horizontal { cursor: row-resize; } @@ -51,11 +41,30 @@ cursor: n-resize; } +.monaco-sash.disabled { + cursor: default !important; + pointer-events: none !important; +} + +.monaco-sash.vertical { + cursor: ew-resize; + top: 0; + width: var(--sash-size); + height: 100%; +} + +.monaco-sash.horizontal { + cursor: ns-resize; + left: 0; + width: 100%; + height: var(--sash-size); +} + .monaco-sash:not(.disabled).orthogonal-start::before, .monaco-sash:not(.disabled).orthogonal-end::after { - content: ' '; - height: 8px; - width: 8px; + content: " "; + height: calc(var(--sash-size) * 2); + width: calc(var(--sash-size) * 2); z-index: 100; display: block; cursor: all-scroll; @@ -63,38 +72,25 @@ } .monaco-sash.orthogonal-start.vertical::before { - left: -2px; - top: -4px; + left: -calc(var(--sash-size) / 2); + top: calc(var(--sash-size) * -1); } - .monaco-sash.orthogonal-end.vertical::after { - left: -2px; - bottom: -4px; + left: -calc(var(--sash-size) / 2); + bottom: calc(var(--sash-size) * -1); } - .monaco-sash.orthogonal-start.horizontal::before { - top: -2px; - left: -4px; + top: -calc(var(--sash-size) / 2); + left: calc(var(--sash-size) * -1); } - .monaco-sash.orthogonal-end.horizontal::after { - top: -2px; - right: -4px; + top: -calc(var(--sash-size) / 2); + right: calc(var(--sash-size) * -1); } -.monaco-sash.disabled { - cursor: default !important; - pointer-events: none !important; -} - -/** Touch **/ - -.monaco-sash.touch.vertical { - width: 20px; -} - -.monaco-sash.touch.horizontal { - height: 20px; +.monaco-sash { + transition: background-color 0.1s ease-out; + background: transparent; } /** Debug **/ @@ -110,4 +106,4 @@ .monaco-sash.debug:not(.disabled).orthogonal-start::before, .monaco-sash.debug:not(.disabled).orthogonal-end::after { background: red; -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 7c2a52a89bf..3f7801ddb6a 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -5,13 +5,12 @@ import 'vs/css!./sash'; import { IDisposable, dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { isIPad } from 'vs/base/browser/browser'; import { isMacintosh } from 'vs/base/common/platform'; import * as types from 'vs/base/common/types'; import { EventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Event, Emitter } from 'vs/base/common/event'; -import { getElementsByTagName, EventHelper, createStyleSheet, addDisposableListener, append, $, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; +import { getElementsByTagName, EventHelper, createStyleSheet, addDisposableListener, append, $ } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; const DEBUG = false; @@ -39,9 +38,18 @@ export interface ISashEvent { } export interface ISashOptions { - orientation?: Orientation; - orthogonalStartSash?: Sash; - orthogonalEndSash?: Sash; + readonly orientation: Orientation; + readonly orthogonalStartSash?: Sash; + readonly orthogonalEndSash?: Sash; + readonly size?: number; +} + +export interface IVerticalSashOptions extends ISashOptions { + readonly orientation: Orientation.VERTICAL; +} + +export interface IHorizontalSashOptions extends ISashOptions { + readonly orientation: Orientation.HORIZONTAL; } export const enum Orientation { @@ -56,12 +64,20 @@ export const enum SashState { Enabled } +let globalSize = 4; +const onDidChangeGlobalSize = new Emitter(); +export function setGlobalSashSize(size: number): void { + globalSize = size; + onDidChangeGlobalSize.fire(size); +} + export class Sash extends Disposable { private el: HTMLElement; private layoutProvider: ISashLayoutProvider; private hidden: boolean; private orientation!: Orientation; + private size: number; private _state: SashState = SashState.Enabled; get state(): SashState { return this._state; } @@ -70,9 +86,9 @@ export class Sash extends Disposable { return; } - toggleClass(this.el, 'disabled', state === SashState.Disabled); - toggleClass(this.el, 'minimum', state === SashState.Minimum); - toggleClass(this.el, 'maximum', state === SashState.Maximum); + this.el.classList.toggle('disabled', state === SashState.Disabled); + this.el.classList.toggle('minimum', state === SashState.Minimum); + this.el.classList.toggle('maximum', state === SashState.Maximum); this._state = state; this._onDidEnablementChange.fire(state); @@ -127,13 +143,15 @@ export class Sash extends Disposable { this._orthogonalEndSash = sash; } - constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions = {}) { + constructor(container: HTMLElement, layoutProvider: IVerticalSashLayoutProvider, options: ISashOptions); + constructor(container: HTMLElement, layoutProvider: IHorizontalSashLayoutProvider, options: ISashOptions); + constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) { super(); this.el = append(container, $('.monaco-sash')); if (isMacintosh) { - addClass(this.el, 'mac'); + this.el.classList.add('mac'); } this._register(domEvent(this.el, 'mousedown')(this.onMouseDown, this)); @@ -142,12 +160,21 @@ export class Sash extends Disposable { this._register(Gesture.addTarget(this.el)); this._register(domEvent(this.el, EventType.Start)(this.onTouchStart, this)); - if (isIPad) { - // see also https://ux.stackexchange.com/questions/39023/what-is-the-optimum-button-size-of-touch-screen-applications - addClass(this.el, 'touch'); - } + if (typeof options.size === 'number') { + this.size = options.size; - this.setOrientation(options.orientation || Orientation.VERTICAL); + if (options.orientation === Orientation.VERTICAL) { + this.el.style.width = `${this.size}px`; + } else { + this.el.style.height = `${this.size}px`; + } + } else { + this.size = globalSize; + this._register(onDidChangeGlobalSize.event(size => { + this.size = size; + this.layout(); + })); + } this.hidden = false; this.layoutProvider = layoutProvider; @@ -155,23 +182,19 @@ export class Sash extends Disposable { this.orthogonalStartSash = options.orthogonalStartSash; this.orthogonalEndSash = options.orthogonalEndSash; - toggleClass(this.el, 'debug', DEBUG); - } - - setOrientation(orientation: Orientation): void { - this.orientation = orientation; + this.orientation = options.orientation || Orientation.VERTICAL; if (this.orientation === Orientation.HORIZONTAL) { - addClass(this.el, 'horizontal'); - removeClass(this.el, 'vertical'); + this.el.classList.add('horizontal'); + this.el.classList.remove('vertical'); } else { - removeClass(this.el, 'horizontal'); - addClass(this.el, 'vertical'); + this.el.classList.remove('horizontal'); + this.el.classList.add('vertical'); } - if (this.layoutProvider) { - this.layout(); - } + this.el.classList.toggle('debug', DEBUG); + + this.layout(); } private onMouseDown(e: MouseEvent): void { @@ -215,10 +238,10 @@ export class Sash extends Disposable { const altKey = mouseDownEvent.altKey; const startEvent: ISashEvent = { startX, currentX: startX, startY, currentY: startY, altKey }; - addClass(this.el, 'active'); + this.el.classList.add('active'); this._onDidStart.fire(startEvent); - // fix https://github.com/Microsoft/vscode/issues/21675 + // fix https://github.com/microsoft/vscode/issues/21675 const style = createStyleSheet(this.el); const updateStyle = () => { let cursor = ''; @@ -243,7 +266,7 @@ export class Sash extends Disposable { } } - style.innerHTML = `* { cursor: ${cursor} !important; }`; + style.textContent = `* { cursor: ${cursor} !important; }`; }; const disposables = new DisposableStore(); @@ -267,7 +290,7 @@ export class Sash extends Disposable { this.el.removeChild(style); - removeClass(this.el, 'active'); + this.el.classList.remove('active'); this._onDidEnd.fire(); disposables.dispose(); @@ -331,11 +354,9 @@ export class Sash extends Disposable { } layout(): void { - const size = isIPad ? 20 : 4; - if (this.orientation === Orientation.VERTICAL) { const verticalProvider = (this.layoutProvider); - this.el.style.left = verticalProvider.getVerticalSashLeft(this) - (size / 2) + 'px'; + this.el.style.left = verticalProvider.getVerticalSashLeft(this) - (this.size / 2) + 'px'; if (verticalProvider.getVerticalSashTop) { this.el.style.top = verticalProvider.getVerticalSashTop(this) + 'px'; @@ -346,7 +367,7 @@ export class Sash extends Disposable { } } else { const horizontalProvider = (this.layoutProvider); - this.el.style.top = horizontalProvider.getHorizontalSashTop(this) - (size / 2) + 'px'; + this.el.style.top = horizontalProvider.getHorizontalSashTop(this) - (this.size / 2) + 'px'; if (horizontalProvider.getHorizontalSashLeft) { this.el.style.left = horizontalProvider.getHorizontalSashLeft(this) + 'px'; @@ -375,24 +396,24 @@ export class Sash extends Disposable { } private onOrthogonalStartSashEnablementChange(state: SashState): void { - toggleClass(this.el, 'orthogonal-start', state !== SashState.Disabled); + this.el.classList.toggle('orthogonal-start', state !== SashState.Disabled); } private onOrthogonalEndSashEnablementChange(state: SashState): void { - toggleClass(this.el, 'orthogonal-end', state !== SashState.Disabled); + this.el.classList.toggle('orthogonal-end', state !== SashState.Disabled); } private getOrthogonalSash(e: MouseEvent): Sash | undefined { if (this.orientation === Orientation.VERTICAL) { - if (e.offsetY <= 4) { + if (e.offsetY <= this.size) { return this.orthogonalStartSash; - } else if (e.offsetY >= this.el.clientHeight - 4) { + } else if (e.offsetY >= this.el.clientHeight - this.size) { return this.orthogonalEndSash; } } else { - if (e.offsetX <= 4) { + if (e.offsetX <= this.size) { return this.orthogonalStartSash; - } else if (e.offsetX >= this.el.clientWidth - 4) { + } else if (e.offsetX >= this.el.clientWidth - this.size) { return this.orthogonalEndSash; } } diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 6658a8ceedd..23bfdb2c7bf 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -259,6 +259,15 @@ export abstract class AbstractScrollbar extends Widget { this._scrollable.setScrollPositionNow(desiredScrollPosition); } + public updateScrollbarSize(scrollbarSize: number): void { + this._updateScrollbarSize(scrollbarSize); + this._scrollbarState.setScrollbarSize(scrollbarSize); + this._shouldRender = true; + if (!this._lazyRender) { + this.render(); + } + } + // ----------------- Overwrite these protected abstract _renderDomNode(largeSize: number, smallSize: number): void; @@ -267,6 +276,7 @@ export abstract class AbstractScrollbar extends Widget { protected abstract _mouseDownRelativePosition(offsetX: number, offsetY: number): number; protected abstract _sliderMousePosition(e: ISimplifiedMouseEvent): number; protected abstract _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number; + protected abstract _updateScrollbarSize(size: number): void; public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void; } diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index f68d4434b4d..6e7f132e99f 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -9,6 +9,11 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + + +const scrollbarButtonLeftIcon = registerIcon('scrollbar-button-left', Codicon.triangleLeft); +const scrollbarButtonRightIcon = registerIcon('scrollbar-button-right', Codicon.triangleRight); export class HorizontalScrollbar extends AbstractScrollbar { @@ -36,7 +41,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { let scrollbarDelta = (options.horizontalScrollbarSize - ARROW_IMG_SIZE) / 2; this._createArrow({ - className: 'left-arrow', + className: 'scra', + icon: scrollbarButtonLeftIcon, top: scrollbarDelta, left: arrowDelta, bottom: undefined, @@ -47,7 +53,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { }); this._createArrow({ - className: 'right-arrow', + className: 'scra', + icon: scrollbarButtonRightIcon, top: scrollbarDelta, left: undefined, bottom: undefined, @@ -92,6 +99,10 @@ export class HorizontalScrollbar extends AbstractScrollbar { return e.posy; } + protected _updateScrollbarSize(size: number): void { + this.slider.setHeight(size); + } + public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void { target.scrollLeft = scrollPosition; } diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-down-dark.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-down-dark.svg deleted file mode 100644 index 23a6284928b..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-down-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-down.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-down.svg deleted file mode 100644 index cf127c6a098..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-left-dark.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-left-dark.svg deleted file mode 100644 index 8a5909bb262..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-left-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-left.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-left.svg deleted file mode 100644 index d4f475e4808..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-right-dark.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-right-dark.svg deleted file mode 100644 index 61dddd673cb..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-right-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-right.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-right.svg deleted file mode 100644 index 824671db551..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-up-dark.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-up-dark.svg deleted file mode 100644 index 69a83f0f02a..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-up-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/arrow-up.svg b/src/vs/base/browser/ui/scrollbar/media/arrow-up.svg deleted file mode 100644 index d2da965deed..00000000000 --- a/src/vs/base/browser/ui/scrollbar/media/arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/scrollbar/media/scrollbars.css b/src/vs/base/browser/ui/scrollbar/media/scrollbars.css index b05c77eed8a..5d7a2dc705a 100644 --- a/src/vs/base/browser/ui/scrollbar/media/scrollbars.css +++ b/src/vs/base/browser/ui/scrollbar/media/scrollbars.css @@ -4,38 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /* Arrows */ -.monaco-scrollable-element > .scrollbar > .up-arrow { - background: url('arrow-up.svg'); +.monaco-scrollable-element > .scrollbar > .scra { cursor: pointer; -} -.monaco-scrollable-element > .scrollbar > .down-arrow { - background: url('arrow-down.svg'); - cursor: pointer; -} -.monaco-scrollable-element > .scrollbar > .left-arrow { - background: url('arrow-left.svg'); - cursor: pointer; -} -.monaco-scrollable-element > .scrollbar > .right-arrow { - background: url('arrow-right.svg'); - cursor: pointer; -} - -.hc-black .monaco-scrollable-element > .scrollbar > .up-arrow, -.vs-dark .monaco-scrollable-element > .scrollbar > .up-arrow { - background: url('arrow-up-dark.svg'); -} -.hc-black .monaco-scrollable-element > .scrollbar > .down-arrow, -.vs-dark .monaco-scrollable-element > .scrollbar > .down-arrow { - background: url('arrow-down-dark.svg'); -} -.hc-black .monaco-scrollable-element > .scrollbar > .left-arrow, -.vs-dark .monaco-scrollable-element > .scrollbar > .left-arrow { - background: url('arrow-left-dark.svg'); -} -.hc-black .monaco-scrollable-element > .scrollbar > .right-arrow, -.vs-dark .monaco-scrollable-element > .scrollbar > .right-arrow { - background: url('arrow-right-dark.svg'); + font-size: 11px !important; } .monaco-scrollable-element > .visible { @@ -137,4 +108,4 @@ .hc-black .monaco-scrollable-element .shadow.top.left { box-shadow: none; -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 462cf44c0b7..24fbe00f769 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/scrollbars'; -import { isEdgeOrIE } from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; @@ -18,6 +17,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { getZoomFactor } from 'vs/base/browser/browser'; const HIDE_TIMEOUT = 500; const SCROLL_WHEEL_SENSITIVITY = 50; @@ -131,13 +131,18 @@ export class MouseWheelClassifier { // } } - if (Math.abs(item.deltaX - Math.round(item.deltaX)) > 0 || Math.abs(item.deltaY - Math.round(item.deltaY)) > 0) { + if (!this._isAlmostInt(item.deltaX) || !this._isAlmostInt(item.deltaY)) { // non-integer deltas => indicator that this is not a physical mouse wheel score += 0.25; } return Math.min(Math.max(score, 0), 1); } + + private _isAlmostInt(value: number): boolean { + const delta = Math.abs(Math.round(value) - value); + return (delta < 0.01); + } } export abstract class AbstractScrollableElement extends Widget { @@ -167,6 +172,9 @@ export abstract class AbstractScrollableElement extends Widget { private readonly _onScroll = this._register(new Emitter()); public readonly onScroll: Event = this._onScroll.event; + private readonly _onWillScroll = this._register(new Emitter()); + public readonly onWillScroll: Event = this._onWillScroll.event; + protected constructor(element: HTMLElement, options: ScrollableElementCreationOptions, scrollable: Scrollable) { super(); element.style.overflow = 'hidden'; @@ -174,6 +182,7 @@ export abstract class AbstractScrollableElement extends Widget { this._scrollable = scrollable; this._register(this._scrollable.onScroll((e) => { + this._onWillScroll.fire(e); this._onDidScroll(e); this._onScroll.fire(e); })); @@ -262,7 +271,7 @@ export abstract class AbstractScrollableElement extends Widget { } public setScrollDimensions(dimensions: INewScrollDimensions): void { - this._scrollable.setScrollDimensions(dimensions); + this._scrollable.setScrollDimensions(dimensions, false); } /** @@ -283,11 +292,22 @@ export abstract class AbstractScrollableElement extends Widget { * depend on Editor. */ public updateOptions(newOptions: ScrollableElementChangeOptions): void { - let massagedOptions = resolveOptions(newOptions); - this._options.handleMouseWheel = massagedOptions.handleMouseWheel; - this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity; - this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity; - this._setListeningToMouseWheel(this._options.handleMouseWheel); + if (typeof newOptions.handleMouseWheel !== 'undefined') { + this._options.handleMouseWheel = newOptions.handleMouseWheel; + this._setListeningToMouseWheel(this._options.handleMouseWheel); + } + if (typeof newOptions.mouseWheelScrollSensitivity !== 'undefined') { + this._options.mouseWheelScrollSensitivity = newOptions.mouseWheelScrollSensitivity; + } + if (typeof newOptions.fastScrollSensitivity !== 'undefined') { + this._options.fastScrollSensitivity = newOptions.fastScrollSensitivity; + } + if (typeof newOptions.scrollPredominantAxis !== 'undefined') { + this._options.scrollPredominantAxis = newOptions.scrollPredominantAxis; + } + if (typeof newOptions.horizontalScrollbarSize !== 'undefined') { + this._horizontalScrollbar.updateScrollbarSize(newOptions.horizontalScrollbarSize); + } if (!this._options.lazyRender) { this._render(); @@ -298,6 +318,10 @@ export abstract class AbstractScrollableElement extends Widget { this._revealOnScroll = value; } + public triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { + this._onMouseWheel(new StandardWheelEvent(browserEvent)); + } + // -------------------- mouse wheel scrolling -------------------- private _setListeningToMouseWheel(shouldListen: boolean): void { @@ -317,7 +341,7 @@ export abstract class AbstractScrollableElement extends Widget { this._onMouseWheel(new StandardWheelEvent(browserEvent)); }; - this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, { passive: false })); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false })); } } @@ -325,15 +349,32 @@ export abstract class AbstractScrollableElement extends Widget { const classifier = MouseWheelClassifier.INSTANCE; if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) { - classifier.accept(Date.now(), e.deltaX, e.deltaY); + const osZoomFactor = window.devicePixelRatio / getZoomFactor(); + if (platform.isWindows || platform.isLinux) { + // On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor. + // The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account. + classifier.accept(Date.now(), e.deltaX / osZoomFactor, e.deltaY / osZoomFactor); + } else { + classifier.accept(Date.now(), e.deltaX, e.deltaY); + } } // console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`); + let didScroll = false; + if (e.deltaY || e.deltaX) { let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity; let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity; + if (this._options.scrollPredominantAxis) { + if (Math.abs(deltaY) >= Math.abs(deltaX)) { + deltaX = 0; + } else { + deltaY = 0; + } + } + if (this._options.flipAxes) { [deltaY, deltaX] = [deltaX, deltaY]; } @@ -380,11 +421,12 @@ export abstract class AbstractScrollableElement extends Widget { } else { this._scrollable.setScrollPositionNow(desiredScrollPosition); } - this._shouldRender = true; + + didScroll = true; } } - if (this._options.alwaysConsumeMouseWheel || this._shouldRender) { + if (this._options.alwaysConsumeMouseWheel || didScroll) { e.preventDefault(); e.stopPropagation(); } @@ -507,6 +549,14 @@ export class SmoothScrollableElement extends AbstractScrollableElement { super(element, options, scrollable); } + public setScrollPosition(update: INewScrollPosition): void { + this._scrollable.setScrollPositionNow(update); + } + + public getScrollPosition(): IScrollPosition { + return this._scrollable.getCurrentScrollPosition(); + } + } export class DomScrollableElement extends ScrollableElement { @@ -553,6 +603,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false), mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1), fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5), + scrollPredominantAxis: (typeof opts.scrollPredominantAxis !== 'undefined' ? opts.scrollPredominantAxis : true), mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true), arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11), diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index 7073bee8cb5..afb227be73b 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -55,6 +55,13 @@ export interface ScrollableElementCreationOptions { * Defaults to 5. */ fastScrollSensitivity?: number; + /** + * Whether the scrollable will only scroll along the predominant axis when scrolling both + * vertically and horizontally at the same time. + * Prevents horizontal drift when scrolling vertically on a trackpad. + * Defaults to true. + */ + scrollPredominantAxis?: boolean; /** * Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right). * Defaults to 11. @@ -112,7 +119,9 @@ export interface ScrollableElementCreationOptions { export interface ScrollableElementChangeOptions { handleMouseWheel?: boolean; mouseWheelScrollSensitivity?: number; - fastScrollSensitivity: number; + fastScrollSensitivity?: number; + scrollPredominantAxis?: boolean; + horizontalScrollbarSize?: number; } export interface ScrollableElementResolvedOptions { @@ -125,6 +134,7 @@ export interface ScrollableElementResolvedOptions { alwaysConsumeMouseWheel: boolean; mouseWheelScrollSensitivity: number; fastScrollSensitivity: number; + scrollPredominantAxis: boolean; mouseWheelSmoothScroll: boolean; arrowSize: number; listenOnDomNode: HTMLElement | null; diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts index fcde58933bf..64b8e8c98ee 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -7,6 +7,7 @@ import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveM import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Widget } from 'vs/base/browser/ui/widget'; import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; /** * The arrow image size. @@ -16,6 +17,7 @@ export const ARROW_IMG_SIZE = 11; export interface ScrollbarArrowOptions { onActivate: () => void; className: string; + icon: Codicon; bgWidth: number; bgHeight: number; @@ -59,6 +61,8 @@ export class ScrollbarArrow extends Widget { this.domNode = document.createElement('div'); this.domNode.className = opts.className; + this.domNode.classList.add(...opts.icon.classNamesArray); + this.domNode.style.position = 'absolute'; this.domNode.style.width = ARROW_IMG_SIZE + 'px'; this.domNode.style.height = ARROW_IMG_SIZE + 'px'; diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index a3554658ecb..48e20a5a033 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -14,7 +14,7 @@ export class ScrollbarState { * For the vertical scrollbar: the width. * For the horizontal scrollbar: the height. */ - private readonly _scrollbarSize: number; + private _scrollbarSize: number; /** * For the vertical scrollbar: the height of the pair horizontal scrollbar. @@ -114,6 +114,10 @@ export class ScrollbarState { return false; } + public setScrollbarSize(scrollbarSize: number): void { + this._scrollbarSize = scrollbarSize; + } + private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) { const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize); const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize); diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index c974029acc2..296913a3fd8 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -9,6 +9,10 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + +const scrollbarButtonUpIcon = registerIcon('scrollbar-button-up', Codicon.triangleUp); +const scrollbarButtonDownIcon = registerIcon('scrollbar-button-down', Codicon.triangleDown); export class VerticalScrollbar extends AbstractScrollbar { @@ -37,7 +41,8 @@ export class VerticalScrollbar extends AbstractScrollbar { let scrollbarDelta = (options.verticalScrollbarSize - ARROW_IMG_SIZE) / 2; this._createArrow({ - className: 'up-arrow', + className: 'scra', + icon: scrollbarButtonUpIcon, top: arrowDelta, left: scrollbarDelta, bottom: undefined, @@ -48,7 +53,8 @@ export class VerticalScrollbar extends AbstractScrollbar { }); this._createArrow({ - className: 'down-arrow', + className: 'scra', + icon: scrollbarButtonDownIcon, top: undefined, left: scrollbarDelta, bottom: arrowDelta, @@ -93,6 +99,10 @@ export class VerticalScrollbar extends AbstractScrollbar { return e.posx; } + protected _updateScrollbarSize(size: number): void { + this.slider.setWidth(size); + } + public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void { target.scrollTop = scrollPosition; } diff --git a/src/vs/base/browser/ui/selectBox/selectBox.css b/src/vs/base/browser/ui/selectBox/selectBox.css index f684dd1085c..d296d1ff0a0 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.css +++ b/src/vs/base/browser/ui/selectBox/selectBox.css @@ -6,3 +6,27 @@ .monaco-select-box { width: 100%; } + +.monaco-select-box-dropdown-container { + font-size: 13px; + font-weight: normal; + text-transform: none; +} + +/** Actions */ + +.monaco-action-bar .action-item.select-container { + cursor: default; +} + +.monaco-action-bar .action-item .monaco-select-box { + cursor: pointer; + min-width: 110px; + min-height: 18px; + padding: 2px 23px 2px 8px; +} + +.mac .monaco-action-bar .action-item .monaco-select-box { + font-size: 11px; + border-radius: 5px; +} diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index cff14f50206..79f0da0e6cd 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -40,6 +40,7 @@ export interface ISelectBoxOptions { useCustomDrawn?: boolean; ariaLabel?: string; minBottomMargin?: number; + optionsAsChildren?: boolean; } // Utilize optionItem interface to capture all option parameters diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index b717cde73f6..536a9422359 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -30,7 +30,7 @@ .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown code { line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-family: var(--monaco-monospace-font); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 07258ce3bef..62438e1b391 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -20,6 +20,7 @@ import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxSty import { isMacintosh } from 'vs/base/common/platform'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -28,7 +29,6 @@ const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template'; interface ISelectListTemplateData { root: HTMLElement; text: HTMLElement; - itemDescription: HTMLElement; decoratorRight: HTMLElement; disposables: IDisposable[]; } @@ -43,8 +43,6 @@ class SelectListRenderer implements IListRenderer; @@ -160,7 +152,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.contextViewProvider = contextViewProvider; this.selectDropDownContainer = dom.$('.monaco-select-box-dropdown-container'); // Use custom CSS vars for padding calculation (shared with parent select) - dom.addClass(this.selectDropDownContainer, 'monaco-select-box-dropdown-padding'); + this.selectDropDownContainer.classList.add('monaco-select-box-dropdown-padding'); // Setup container for select option details this.selectionDetailsPane = dom.append(this.selectDropDownContainer, $('.select-box-details-pane')); @@ -230,7 +222,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi if (showDropDown) { this.showSelectDropDown(); - dom.EventHelper.stop(e); + dom.EventHelper.stop(e, true); } })); } @@ -306,7 +298,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } public render(container: HTMLElement): void { - dom.addClass(container, 'select-container'); + this.container = container; + container.classList.add('select-container'); container.appendChild(this.selectElement); this.applyStyles(); } @@ -354,10 +347,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row.option-disabled:hover { background-color: ${this.styles.selectBackground} !important; }`); } - // Match quickOpen outline styles - ignore for disabled options + // Match quick input outline styles - ignore for disabled options if (this.styles.listFocusOutline) { content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row.focused { outline: 1.6px dotted ${this.styles.listFocusOutline} !important; outline-offset: -1.6px !important; }`); - } if (this.styles.listHoverOutline) { @@ -365,7 +357,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row.option-disabled:hover { outline: none !important; }`); } - this.styleElement.innerHTML = content.join('\n'); + this.styleElement.textContent = content.join('\n'); this.applyStyles(); } @@ -438,11 +430,11 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.layoutSelectDropDown(); }, onHide: () => { - dom.toggleClass(this.selectDropDownContainer, 'visible', false); - dom.toggleClass(this.selectElement, 'synthetic-focus', false); + this.selectDropDownContainer.classList.remove('visible'); + this.selectElement.classList.remove('synthetic-focus'); }, anchorPosition: this._dropDownPosition - }); + }, this.selectBoxOptions.optionsAsChildren ? this.container : undefined); // Hide so we can relay out this._isVisible = true; @@ -453,15 +445,16 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi render: (container: HTMLElement) => this.renderSelectDropDown(container), layout: () => this.layoutSelectDropDown(), onHide: () => { - dom.toggleClass(this.selectDropDownContainer, 'visible', false); - dom.toggleClass(this.selectElement, 'synthetic-focus', false); + this.selectDropDownContainer.classList.remove('visible'); + this.selectElement.classList.remove('synthetic-focus'); }, anchorPosition: this._dropDownPosition - }); + }, this.selectBoxOptions.optionsAsChildren ? this.container : undefined); // Track initial selection the case user escape, blur this._currentSelection = this.selected; this._isVisible = true; + this.selectElement.setAttribute('aria-expanded', 'true'); } private hideSelectDropDown(focusSelect: boolean) { @@ -470,6 +463,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } this._isVisible = false; + this.selectElement.setAttribute('aria-expanded', 'false'); if (focusSelect) { this.selectElement.focus(); @@ -499,42 +493,15 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Iterate over detailed descriptions, find max height private measureMaxDetailsHeight(): number { - let maxDetailsPaneHeight = 0; - this.options.forEach((option, index) => { - - this.selectionDetailsPane.innerText = ''; - - if (option.description) { - if (option.descriptionIsMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(option.description)); - } else { - this.selectionDetailsPane.innerText = option.description; - } - this.selectionDetailsPane.style.display = 'block'; - } else { - this.selectionDetailsPane.style.display = 'none'; - } + this.options.forEach((_option, index) => { + this.updateDetail(index); if (this.selectionDetailsPane.offsetHeight > maxDetailsPaneHeight) { maxDetailsPaneHeight = this.selectionDetailsPane.offsetHeight; } }); - // Reset description to selected - - this.selectionDetailsPane.innerText = ''; - const description = this.options[this.selected].description || null; - const descriptionIsMarkdown = this.options[this.selected].descriptionIsMarkdown || null; - - if (description) { - if (descriptionIsMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description)); - } else { - this.selectionDetailsPane.innerText = description; - } - this.selectionDetailsPane.style.display = 'block'; - } return maxDetailsPaneHeight; } @@ -552,7 +519,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi if (this.selectList) { // Make visible to enable measurements - dom.toggleClass(this.selectDropDownContainer, 'visible', true); + this.selectDropDownContainer.classList.add('visible'); const selectPosition = dom.getDomNodePagePosition(this.selectElement); const styles = getComputedStyle(this.selectElement); @@ -607,8 +574,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectDropDownContainer.appendChild(this.selectionDetailsPane); this.selectDropDownContainer.appendChild(this.selectDropDownListContainer); - dom.removeClass(this.selectionDetailsPane, 'border-top'); - dom.addClass(this.selectionDetailsPane, 'border-bottom'); + this.selectionDetailsPane.classList.remove('border-top'); + this.selectionDetailsPane.classList.add('border-bottom'); } else { this._dropDownPosition = AnchorPosition.BELOW; @@ -617,8 +584,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectDropDownContainer.appendChild(this.selectDropDownListContainer); this.selectDropDownContainer.appendChild(this.selectionDetailsPane); - dom.removeClass(this.selectionDetailsPane, 'border-bottom'); - dom.addClass(this.selectionDetailsPane, 'border-top'); + this.selectionDetailsPane.classList.remove('border-bottom'); + this.selectionDetailsPane.classList.add('border-top'); } // Do full layout on showSelectDropDown only return true; @@ -672,12 +639,14 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectDropDownContainer.style.height = (listHeight + verticalPadding) + 'px'; } + this.updateDetail(this.selected); + this.selectDropDownContainer.style.width = selectOptimalWidth; // Maintain focus outline on parent select as well as list container - tabindex for focus this.selectDropDownListContainer.setAttribute('tabindex', '0'); - dom.toggleClass(this.selectElement, 'synthetic-focus', true); - dom.toggleClass(this.selectDropDownContainer, 'synthetic-focus', true); + this.selectElement.classList.add('synthetic-focus'); + this.selectDropDownContainer.classList.add('synthetic-focus'); return true; } else { @@ -701,7 +670,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi }); - container.innerHTML = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); + container.textContent = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); elementWidth = dom.getTotalWidth(container); } @@ -721,12 +690,31 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.listRenderer = new SelectListRenderer(); this.selectList = new List('SelectBoxCustom', this.selectDropDownListContainer, this, [this.listRenderer], { - ariaLabel: this.selectBoxOptions.ariaLabel, useShadows: false, verticalScrollMode: ScrollbarVisibility.Visible, keyboardSupport: false, - mouseSupport: false + mouseSupport: false, + accessibilityProvider: { + getAriaLabel: element => { + let label = element.text; + if (element.decoratorRight) { + label += `. ${element.decoratorRight}`; + } + + if (element.description) { + label += `. ${element.description}`; + } + + return label; + }, + getWidgetAriaLabel: () => localize({ key: 'selectBox', comment: ['Behave like native select dropdown element.'] }, "Select Box"), + getRole: () => 'option', + getWidgetRole: () => 'listbox' + } }); + if (this.selectBoxOptions.ariaLabel) { + this.selectList.ariaLabel = this.selectBoxOptions.ariaLabel; + } // SetUp list keyboard controller - control navigation, disabled items, focus const onSelectDropDownKeyDown = Event.chain(domEvent(this.selectDropDownListContainer, 'keydown')) @@ -750,7 +738,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi .on(e => this.onMouseUp(e), this)); this._register(this.selectList.onMouseOver(e => typeof e.index !== 'undefined' && this.selectList.setFocus([e.index]))); - this._register(this.selectList.onFocusChange(e => this.onListFocus(e))); + this._register(this.selectList.onDidChangeFocus(e => this.onListFocus(e))); this._register(dom.addDisposableListener(this.selectDropDownContainer, dom.EventType.FOCUS_OUT, e => { if (!this._isVisible || dom.isAncestor(e.relatedTarget as HTMLElement, this.selectDropDownContainer)) { @@ -858,8 +846,11 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi return; } + this.updateDetail(e.indexes[0]); + } + + private updateDetail(selectedIndex: number): void { this.selectionDetailsPane.innerText = ''; - const selectedIndex = e.indexes[0]; const description = this.options[selectedIndex].description; const descriptionIsMarkdown = this.options[selectedIndex].descriptionIsMarkdown; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index fc18c6d3b18..b6ed0f12383 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -51,6 +51,10 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { })); }); + this._register(dom.addStandardDisposableListener(this.selectElement, 'click', (e) => { + dom.EventHelper.stop(e, true); + })); + this._register(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => { this.selectElement.title = e.target.value; this._onDidSelect.fire({ @@ -139,7 +143,7 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { } public render(container: HTMLElement): void { - dom.addClass(container, 'select-container'); + container.classList.add('select-container'); container.appendChild(this.selectElement); this.setOptions(this.options, this.selected); this.applyStyles(); diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css index c1397d6c62c..06743287c1a 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -16,7 +16,12 @@ flex-direction: column; } +.monaco-pane-view .pane.horizontal:not(.expanded) { + flex-direction: row; +} + .monaco-pane-view .pane > .pane-header { + height: 22px; font-size: 11px; font-weight: bold; text-transform: uppercase; @@ -24,6 +29,13 @@ display: flex; cursor: pointer; align-items: center; + box-sizing: border-box; +} + +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header { + flex-direction: column; + height: 100%; + width: 22px; } .monaco-pane-view .pane > .pane-header > .twisties { @@ -36,6 +48,11 @@ flex-shrink: 0; } +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .twisties { + margin-top: 2px; + margin-bottom: 2px; +} + .monaco-pane-view .pane > .pane-header.expanded > .twisties::before { transform: rotate(90deg); } @@ -43,7 +60,7 @@ /* TODO: actions should be part of the pane, but they aren't yet */ .monaco-pane-view .pane > .pane-header > .actions { display: none; - flex: 1; + margin-left: auto; } /* TODO: actions should be part of the pane, but they aren't yet */ @@ -68,6 +85,24 @@ color: inherit; } +.monaco-pane-view .pane > .pane-header .monaco-action-bar .action-item.select-container { + cursor: default; +} + +.monaco-pane-view .pane > .pane-header .action-item .monaco-select-box { + cursor: pointer; + min-width: 110px; + min-height: 18px; + padding: 2px 23px 2px 8px; + background-color: inherit !important; + color: inherit !important; +} + +.linux .monaco-pane-view .pane > .pane-header .action-item .monaco-select-box, +.windows .monaco-pane-view .pane > .pane-header .action-item .monaco-select-box { + padding: 0px 23px 0px 8px; +} + /* Bold font style does not go well with CJK fonts */ .monaco-pane-view:lang(zh-Hans) .pane > .pane-header, .monaco-pane-view:lang(zh-Hant) .pane > .pane-header, @@ -99,3 +134,27 @@ .monaco-pane-view.animated.horizontal .split-view-view { transition-property: width; } + +#monaco-pane-drop-overlay { + position: absolute; + z-index: 10000; + width: 100%; + height: 100%; + left: 0; + box-sizing: border-box; +} + +#monaco-pane-drop-overlay > .pane-overlay-indicator { + position: absolute; + width: 100%; + height: 100%; + min-height: 22px; + min-width: 19px; + + pointer-events: none; /* very important to not take events away from the parent */ + transition: opacity 150ms ease-out; +} + +#monaco-pane-drop-overlay > .pane-overlay-indicator.overlay-move-transition { + transition: top 70ms ease-out, left 70ms ease-out, width 70ms ease-out, height 70ms ease-out, opacity 150ms ease-out; +} diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index 037ffdce27b..673094b0c57 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -9,18 +9,21 @@ import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { $, append, addClass, removeClass, toggleClass, trackFocus, EventHelper } from 'vs/base/browser/dom'; -import { firstIndex } from 'vs/base/common/arrays'; +import { $, append, trackFocus, EventHelper, clearNode } from 'vs/base/browser/dom'; import { Color, RGBA } from 'vs/base/common/color'; import { SplitView, IView } from './splitview'; import { isFirefox } from 'vs/base/browser/browser'; import { DataTransfers } from 'vs/base/browser/dnd'; +import { Orientation } from 'vs/base/browser/ui/sash/sash'; +import { localize } from 'vs/nls'; export interface IPaneOptions { - ariaHeaderLabel?: string; minimumBodySize?: number; maximumBodySize?: number; expanded?: boolean; + orientation?: Orientation; + title: string; + titleDescription?: string; } export interface IPaneStyles { @@ -28,6 +31,7 @@ export interface IPaneStyles { headerForeground?: Color; headerBackground?: Color; headerBorder?: Color; + leftBorder?: Color; } /** @@ -48,6 +52,7 @@ export abstract class Pane extends Disposable implements IView { private body!: HTMLElement; protected _expanded: boolean; + protected _orientation: Orientation; private expandedSize: number | undefined = undefined; private _headerVisible = true; @@ -101,7 +106,7 @@ export abstract class Pane extends Disposable implements IView { get minimumSize(): number { const headerSize = this.headerSize; const expanded = !this.headerVisible || this.isExpanded(); - const minimumBodySize = expanded ? this._minimumBodySize : 0; + const minimumBodySize = expanded ? this.minimumBodySize : 0; return headerSize + minimumBodySize; } @@ -109,18 +114,19 @@ export abstract class Pane extends Disposable implements IView { get maximumSize(): number { const headerSize = this.headerSize; const expanded = !this.headerVisible || this.isExpanded(); - const maximumBodySize = expanded ? this._maximumBodySize : 0; + const maximumBodySize = expanded ? this.maximumBodySize : 0; return headerSize + maximumBodySize; } - width: number = 0; + orthogonalSize: number = 0; - constructor(options: IPaneOptions = {}) { + constructor(options: IPaneOptions) { super(); this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; - this.ariaHeaderLabel = options.ariaHeaderLabel || ''; - this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120; + this._orientation = typeof options.orientation === 'undefined' ? Orientation.VERTICAL : options.orientation; + this.ariaHeaderLabel = localize('viewSection', "{0} Section", options.title); + this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 200 : 120; this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY; this.element = $('.pane'); @@ -135,6 +141,10 @@ export abstract class Pane extends Disposable implements IView { return false; } + if (this.element) { + this.element.classList.toggle('expanded', expanded); + } + this._expanded = !!expanded; this.updateHeader(); @@ -168,21 +178,48 @@ export abstract class Pane extends Disposable implements IView { this._onDidChange.fire(undefined); } + get orientation(): Orientation { + return this._orientation; + } + + set orientation(orientation: Orientation) { + if (this._orientation === orientation) { + return; + } + + this._orientation = orientation; + + if (this.element) { + this.element.classList.toggle('horizontal', this.orientation === Orientation.HORIZONTAL); + this.element.classList.toggle('vertical', this.orientation === Orientation.VERTICAL); + } + + if (this.header) { + this.updateHeader(); + } + } + render(): void { + this.element.classList.toggle('expanded', this.isExpanded()); + this.element.classList.toggle('horizontal', this.orientation === Orientation.HORIZONTAL); + this.element.classList.toggle('vertical', this.orientation === Orientation.VERTICAL); + this.header = $('.pane-header'); append(this.element, this.header); this.header.setAttribute('tabindex', '0'); - this.header.setAttribute('role', 'toolbar'); + // Use role button so the aria-expanded state gets read https://github.com/microsoft/vscode/issues/95996 + this.header.setAttribute('role', 'button'); this.header.setAttribute('aria-label', this.ariaHeaderLabel); this.renderHeader(this.header); const focusTracker = trackFocus(this.header); this._register(focusTracker); - this._register(focusTracker.onDidFocus(() => addClass(this.header, 'focused'), null)); - this._register(focusTracker.onDidBlur(() => removeClass(this.header, 'focused'), null)); + this._register(focusTracker.onDidFocus(() => this.header.classList.add('focused'), null)); + this._register(focusTracker.onDidBlur(() => this.header.classList.remove('focused'), null)); this.updateHeader(); + const onHeaderKeyDown = Event.chain(domEvent(this.header, 'keydown')) .map(e => new StandardKeyboardEvent(e)); @@ -196,18 +233,30 @@ export abstract class Pane extends Disposable implements IView { .event(() => this.setExpanded(true), null)); this._register(domEvent(this.header, 'click') - (() => this.setExpanded(!this.isExpanded()), null)); + (e => { + if (!e.defaultPrevented) { + this.setExpanded(!this.isExpanded()); + } + }, null)); this.body = append(this.element, $('.pane-body')); this.renderBody(this.body); + + if (!this.isExpanded()) { + this.body.remove(); + } } - layout(height: number): void { + layout(size: number): void { const headerSize = this.headerVisible ? Pane.HEADER_SIZE : 0; + const width = this._orientation === Orientation.VERTICAL ? this.orthogonalSize : size; + const height = this._orientation === Orientation.VERTICAL ? size - headerSize : this.orthogonalSize - headerSize; + if (this.isExpanded()) { - this.layoutBody(height - headerSize, this.width); - this.expandedSize = height; + this.body.classList.toggle('wide', width >= 600); + this.layoutBody(height, width); + this.expandedSize = size; } } @@ -224,16 +273,16 @@ export abstract class Pane extends Disposable implements IView { protected updateHeader(): void { const expanded = !this.headerVisible || this.isExpanded(); - this.header.style.height = `${this.headerSize}px`; this.header.style.lineHeight = `${this.headerSize}px`; - toggleClass(this.header, 'hidden', !this.headerVisible); - toggleClass(this.header, 'expanded', expanded); + this.header.classList.toggle('hidden', !this.headerVisible); + this.header.classList.toggle('expanded', expanded); this.header.setAttribute('aria-expanded', String(expanded)); this.header.style.color = this.styles.headerForeground ? this.styles.headerForeground.toString() : ''; this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : ''; - this.header.style.borderTop = this.styles.headerBorder ? `1px solid ${this.styles.headerBorder}` : ''; + this.header.style.borderTop = this.styles.headerBorder && this.orientation === Orientation.VERTICAL ? `1px solid ${this.styles.headerBorder}` : ''; this._dropBackground = this.styles.dropBackground; + this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : ''; } protected abstract renderHeader(container: HTMLElement): void; @@ -249,7 +298,7 @@ class PaneDraggable extends Disposable { private static readonly DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5)); - private dragOverCounter = 0; // see https://github.com/Microsoft/vscode/issues/14470 + private dragOverCounter = 0; // see https://github.com/microsoft/vscode/issues/14470 private _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>()); readonly onDidDrop = this._onDidDrop.event; @@ -371,6 +420,7 @@ export class DefaultPaneDndController implements IPaneDndController { export interface IPaneViewOptions { dnd?: IPaneDndController; + orientation?: Orientation; } interface IPaneItem { @@ -384,21 +434,24 @@ export class PaneView extends Disposable { private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; private paneItems: IPaneItem[] = []; - private width: number = 0; + private orthogonalSize: number = 0; + private size: number = 0; private splitview: SplitView; private animationTimer: number | undefined = undefined; private _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>()); readonly onDidDrop: Event<{ from: Pane, to: Pane }> = this._onDidDrop.event; + orientation: Orientation; readonly onDidSashChange: Event; constructor(container: HTMLElement, options: IPaneViewOptions = {}) { super(); this.dnd = options.dnd; + this.orientation = options.orientation ?? Orientation.VERTICAL; this.el = append(container, $('.monaco-pane-view')); - this.splitview = this._register(new SplitView(this.el)); + this.splitview = this._register(new SplitView(this.el, { orientation: this.orientation })); this.onDidSashChange = this.splitview.onDidSashChange; } @@ -408,7 +461,8 @@ export class PaneView extends Disposable { const paneItem = { pane: pane, disposable: disposables }; this.paneItems.splice(index, 0, paneItem); - pane.width = this.width; + pane.orientation = this.orientation; + pane.orthogonalSize = this.orthogonalSize; this.splitview.addView(pane, size, index); if (this.dnd) { @@ -419,7 +473,7 @@ export class PaneView extends Disposable { } removePane(pane: Pane): void { - const index = firstIndex(this.paneItems, item => item.pane === pane); + const index = this.paneItems.findIndex(item => item.pane === pane); if (index === -1) { return; @@ -431,8 +485,8 @@ export class PaneView extends Disposable { } movePane(from: Pane, to: Pane): void { - const fromIndex = firstIndex(this.paneItems, item => item.pane === from); - const toIndex = firstIndex(this.paneItems, item => item.pane === to); + const fromIndex = this.paneItems.findIndex(item => item.pane === from); + const toIndex = this.paneItems.findIndex(item => item.pane === to); if (fromIndex === -1 || toIndex === -1) { return; @@ -445,7 +499,7 @@ export class PaneView extends Disposable { } resizePane(pane: Pane, size: number): void { - const index = firstIndex(this.paneItems, item => item.pane === pane); + const index = this.paneItems.findIndex(item => item.pane === pane); if (index === -1) { return; @@ -455,7 +509,7 @@ export class PaneView extends Disposable { } getPaneSize(pane: Pane): number { - const index = firstIndex(this.paneItems, item => item.pane === pane); + const index = this.paneItems.findIndex(item => item.pane === pane); if (index === -1) { return -1; @@ -465,13 +519,40 @@ export class PaneView extends Disposable { } layout(height: number, width: number): void { - this.width = width; + this.orthogonalSize = this.orientation === Orientation.VERTICAL ? width : height; + this.size = this.orientation === Orientation.HORIZONTAL ? width : height; for (const paneItem of this.paneItems) { - paneItem.pane.width = width; + paneItem.pane.orthogonalSize = this.orthogonalSize; } - this.splitview.layout(height); + this.splitview.layout(this.size); + } + + flipOrientation(height: number, width: number): void { + this.orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL; + const paneSizes = this.paneItems.map(pane => this.getPaneSize(pane.pane)); + + this.splitview.dispose(); + clearNode(this.el); + + this.splitview = this._register(new SplitView(this.el, { orientation: this.orientation })); + + const newOrthogonalSize = this.orientation === Orientation.VERTICAL ? width : height; + const newSize = this.orientation === Orientation.HORIZONTAL ? width : height; + + this.paneItems.forEach((pane, index) => { + pane.pane.orthogonalSize = newOrthogonalSize; + pane.pane.orientation = this.orientation; + + const viewSize = this.size === 0 ? 0 : (newSize * paneSizes[index]) / this.size; + this.splitview.addView(pane.pane, viewSize, index); + }); + + this.size = newSize; + this.orthogonalSize = newOrthogonalSize; + + this.splitview.layout(this.size); } private setupAnimation(): void { @@ -479,11 +560,11 @@ export class PaneView extends Disposable { window.clearTimeout(this.animationTimer); } - addClass(this.el, 'animated'); + this.el.classList.add('animated'); this.animationTimer = window.setTimeout(() => { this.animationTimer = undefined; - removeClass(this.el, 'animated'); + this.el.classList.remove('animated'); }, 200); } diff --git a/src/vs/base/browser/ui/splitview/splitview.css b/src/vs/base/browser/ui/splitview/splitview.css index e5baba1f32a..3af3e9062d2 100644 --- a/src/vs/base/browser/ui/splitview/splitview.css +++ b/src/vs/base/browser/ui/splitview/splitview.css @@ -20,31 +20,36 @@ pointer-events: initial; } -.monaco-split-view2 > .split-view-container { +.monaco-split-view2 > .monaco-scrollable-element { + width: 100%; + height: 100%; +} + +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container { width: 100%; height: 100%; white-space: nowrap; position: relative; } -.monaco-split-view2 > .split-view-container > .split-view-view { +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view { white-space: initial; position: absolute; } -.monaco-split-view2 > .split-view-container > .split-view-view:not(.visible) { +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view:not(.visible) { display: none; } -.monaco-split-view2.vertical > .split-view-container > .split-view-view { +.monaco-split-view2.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view { width: 100%; } -.monaco-split-view2.horizontal > .split-view-container > .split-view-view { +.monaco-split-view2.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view { height: 100%; } -.monaco-split-view2.separator-border > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { content: ' '; position: absolute; top: 0; @@ -54,12 +59,12 @@ background-color: var(--separator-border); } -.monaco-split-view2.separator-border.horizontal > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { height: 100%; width: 1px; } -.monaco-split-view2.separator-border.vertical > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { height: 1px; width: 100%; } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 6bca3b4ec7b..8bb89391279 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -7,12 +7,14 @@ import 'vs/css!./splitview'; import { IDisposable, toDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; -import * as dom from 'vs/base/browser/dom'; import { clamp } from 'vs/base/common/numbers'; -import { range, firstIndex, pushToStart, pushToEnd } from 'vs/base/common/arrays'; +import { range, pushToStart, pushToEnd } from 'vs/base/common/arrays'; import { Sash, Orientation, ISashEvent as IBaseSashEvent, SashState } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { domEvent } from 'vs/base/browser/event'; +import { $, append, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface ISplitViewStyles { @@ -93,7 +95,7 @@ abstract class ViewItem { this.size = 0; } - dom.toggleClass(this.container, 'visible', visible); + this.container.classList.toggle('visible', visible); if (this.view.setVisible) { this.view.setVisible(visible); @@ -110,7 +112,7 @@ abstract class ViewItem { get snap(): boolean { return !!this.view.snap; } set enabled(enabled: boolean) { - this.container.style.pointerEvents = enabled ? null : 'none'; + this.container.style.pointerEvents = enabled ? '' : 'none'; } constructor( @@ -122,7 +124,7 @@ abstract class ViewItem { if (typeof size === 'number') { this._size = size; this._cachedVisibleSize = undefined; - dom.addClass(container, 'visible'); + container.classList.add('visible'); } else { this._size = 0; this._cachedVisibleSize = size.cachedVisibleSize; @@ -213,6 +215,8 @@ export class SplitView extends Disposable { readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; + private scrollable: Scrollable; + private scrollableElement: SmoothScrollableElement; private size = 0; private layoutContext: TLayoutContext | undefined; private contentSize = 0; @@ -296,12 +300,25 @@ export class SplitView extends Disposable { this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout; this.el = document.createElement('div'); - dom.addClass(this.el, 'monaco-split-view2'); - dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal'); + this.el.classList.add('monaco-split-view2'); + this.el.classList.add(this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal'); container.appendChild(this.el); - this.sashContainer = dom.append(this.el, dom.$('.sash-container')); - this.viewContainer = dom.append(this.el, dom.$('.split-view-container')); + this.sashContainer = append(this.el, $('.sash-container')); + this.viewContainer = $('.split-view-container'); + + this.scrollable = new Scrollable(125, scheduleAtNextAnimationFrame); + this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { + vertical: this.orientation === Orientation.VERTICAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, + horizontal: this.orientation === Orientation.HORIZONTAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden + }, this.scrollable)); + + this._register(this.scrollableElement.onScroll(e => { + this.viewContainer.scrollTop = e.scrollTop; + this.viewContainer.scrollLeft = e.scrollLeft; + })); + + append(this.el, this.scrollableElement.getDomNode()); this.style(options.styles || defaultStyles); @@ -323,16 +340,16 @@ export class SplitView extends Disposable { style(styles: ISplitViewStyles): void { if (styles.separatorBorder.isTransparent()) { - dom.removeClass(this.el, 'separator-border'); + this.el.classList.remove('separator-border'); this.el.style.removeProperty('--separator-border'); } else { - dom.addClass(this.el, 'separator-border'); + this.el.classList.add('separator-border'); this.el.style.setProperty('--separator-border', styles.separatorBorder.toString()); } } - addView(view: IView, size: number | Sizing, index = this.viewItems.length): void { - this.doAddView(view, size, index, false); + addView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void { + this.doAddView(view, size, index, skipLayout); } removeView(index: number, sizing?: Sizing): IView { @@ -460,7 +477,7 @@ export class SplitView extends Disposable { item.enabled = false; } - const index = firstIndex(this.sashItems, item => item.sash === sash); + const index = this.sashItems.findIndex(item => item.sash === sash); // This way, we can press Alt while we resize a sash, macOS style! const disposable = combinedDisposable( @@ -657,7 +674,7 @@ export class SplitView extends Disposable { this.state = State.Busy; // Add view - const container = dom.$('.split-view-view'); + const container = $('.split-view-view'); if (index === this.viewItems.length) { this.viewContainer.appendChild(container); @@ -689,13 +706,17 @@ export class SplitView extends Disposable { // Add sash if (this.viewItems.length > 1) { - const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL; - const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) } : { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) }; - const sash = new Sash(this.sashContainer, layoutProvider, { - orientation, - orthogonalStartSash: this.orthogonalStartSash, - orthogonalEndSash: this.orthogonalEndSash - }); + const sash = this.orientation === Orientation.VERTICAL + ? new Sash(this.sashContainer, { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) }, { + orientation: Orientation.HORIZONTAL, + orthogonalStartSash: this.orthogonalStartSash, + orthogonalEndSash: this.orthogonalEndSash + }) + : new Sash(this.sashContainer, { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) }, { + orientation: Orientation.VERTICAL, + orthogonalStartSash: this.orthogonalStartSash, + orthogonalEndSash: this.orthogonalEndSash + }); const sashEventMapper = this.orientation === Orientation.VERTICAL ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey }) @@ -705,11 +726,11 @@ export class SplitView extends Disposable { const onStartDisposable = onStart(this.onSashStart, this); const onChange = Event.map(sash.onDidChange, sashEventMapper); const onChangeDisposable = onChange(this.onSashChange, this); - const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); + const onEnd = Event.map(sash.onDidEnd, () => this.sashItems.findIndex(item => item.sash === sash)); const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => { - const index = firstIndex(this.sashItems, item => item.sash === sash); + const index = this.sashItems.findIndex(item => item.sash === sash); const upIndexes = range(index, -1); const downIndexes = range(index + 1, this.viewItems.length); const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); @@ -893,6 +914,21 @@ export class SplitView extends Disposable { // Layout sashes this.sashItems.forEach(item => item.sash.layout()); this.updateSashEnablement(); + this.updateScrollableElement(); + } + + private updateScrollableElement(): void { + if (this.orientation === Orientation.VERTICAL) { + this.scrollableElement.setScrollDimensions({ + height: this.size, + scrollHeight: this.contentSize + }); + } else { + this.scrollableElement.setScrollDimensions({ + width: this.size, + scrollWidth: this.contentSize + }); + } } private updateSashEnablement(): void { @@ -951,7 +987,7 @@ export class SplitView extends Disposable { position += this.viewItems[i].size; if (this.sashItems[i].sash === sash) { - return Math.min(position, this.contentSize - 2); + return position; } } diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 846e156dfa3..d2b361446a7 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -5,15 +5,18 @@ import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; -import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; -import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IContextMenuProvider, DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; +import { Action, IActionRunner, IAction, IActionViewItemProvider, SubmenuAction } from 'vs/base/common/actions'; +import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { EventMultiplexer } from 'vs/base/common/event'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; -export const CONTEXT = 'context.toolbar'; +const toolBarMoreIcon = registerIcon('toolbar-more', Codicon.more); export interface IToolBarOptions { orientation?: ActionsOrientation; @@ -23,6 +26,7 @@ export interface IToolBarOptions { actionRunner?: IActionRunner; toggleMenuTitle?: string; anchorAlignmentProvider?: () => AnchorAlignment; + renderDropdownAsChildElement?: boolean; } /** @@ -32,9 +36,15 @@ export class ToolBar extends Disposable { private options: IToolBarOptions; private actionBar: ActionBar; private toggleMenuAction: ToggleMenuAction; - private toggleMenuActionViewItem = this._register(new MutableDisposable()); + private toggleMenuActionViewItem: DropdownMenuActionViewItem | undefined; + private submenuActionViewItems: DropdownMenuActionViewItem[] = []; private hasSecondaryActions: boolean = false; private lookupKeybindings: boolean; + private element: HTMLElement; + + private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer()); + readonly onDidChangeDropdownVisibility = this._onDidChangeDropdownVisibility.event; + private disposables = new DisposableStore(); constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) { super(); @@ -42,38 +52,67 @@ export class ToolBar extends Disposable { this.options = options; this.lookupKeybindings = typeof this.options.getKeyBinding === 'function'; - this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem.value && this.toggleMenuActionViewItem.value.show(), options.toggleMenuTitle)); + this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem?.show(), options.toggleMenuTitle)); - let element = document.createElement('div'); - element.className = 'monaco-toolbar'; - container.appendChild(element); + this.element = document.createElement('div'); + this.element.className = 'monaco-toolbar'; + container.appendChild(this.element); - this.actionBar = this._register(new ActionBar(element, { + this.actionBar = this._register(new ActionBar(this.element, { orientation: options.orientation, ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, actionViewItemProvider: (action: IAction) => { - - // Return special action item for the toggle menu action if (action.id === ToggleMenuAction.ID) { - - // Create new - this.toggleMenuActionViewItem.value = new DropdownMenuActionViewItem( + this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( action, (action).menuActions, contextMenuProvider, - this.options.actionViewItemProvider, - this.actionRunner, - this.options.getKeyBinding, - 'codicon-more', - this.options.anchorAlignmentProvider + { + actionViewItemProvider: this.options.actionViewItemProvider, + actionRunner: this.actionRunner, + keybindingProvider: this.options.getKeyBinding, + classNames: toolBarMoreIcon.classNamesArray, + anchorAlignmentProvider: this.options.anchorAlignmentProvider, + menuAsChild: !!this.options.renderDropdownAsChildElement + } ); - this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context); + this.toggleMenuActionViewItem.setActionContext(this.actionBar.context); + this.disposables.add(this._onDidChangeDropdownVisibility.add(this.toggleMenuActionViewItem.onDidChangeVisibility)); - return this.toggleMenuActionViewItem.value; + return this.toggleMenuActionViewItem; } - return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; + if (options.actionViewItemProvider) { + const result = options.actionViewItemProvider(action); + + if (result) { + return result; + } + } + + if (action instanceof SubmenuAction) { + const result = new DropdownMenuActionViewItem( + action, + action.actions, + contextMenuProvider, + { + actionViewItemProvider: this.options.actionViewItemProvider, + actionRunner: this.actionRunner, + keybindingProvider: this.options.getKeyBinding, + classNames: action.class, + anchorAlignmentProvider: this.options.anchorAlignmentProvider, + menuAsChild: true + } + ); + result.setActionContext(this.actionBar.context); + this.submenuActionViewItems.push(result); + this.disposables.add(this._onDidChangeDropdownVisibility.add(result.onDidChangeVisibility)); + + return result; + } + + return undefined; } })); } @@ -86,15 +125,18 @@ export class ToolBar extends Disposable { return this.actionBar.actionRunner; } - set context(context: any) { + set context(context: unknown) { this.actionBar.context = context; - if (this.toggleMenuActionViewItem.value) { - this.toggleMenuActionViewItem.value.setActionContext(context); + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.setActionContext(context); + } + for (const actionViewItem of this.submenuActionViewItems) { + actionViewItem.setActionContext(context); } } - getContainer(): HTMLElement { - return this.actionBar.getContainer(); + getElement(): HTMLElement { + return this.element; } getItemsWidth(): number { @@ -109,23 +151,21 @@ export class ToolBar extends Disposable { this.actionBar.setAriaLabel(label); } - setActions(primaryActions: ReadonlyArray, secondaryActions?: ReadonlyArray): () => void { - return () => { - let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; + setActions(primaryActions: ReadonlyArray, secondaryActions?: ReadonlyArray): void { + this.clear(); - // Inject additional action to open secondary actions if present - this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0); - if (this.hasSecondaryActions && secondaryActions) { - this.toggleMenuAction.menuActions = secondaryActions.slice(0); - primaryActionsToSet.push(this.toggleMenuAction); - } + let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; - this.actionBar.clear(); + // Inject additional action to open secondary actions if present + this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0); + if (this.hasSecondaryActions && secondaryActions) { + this.toggleMenuAction.menuActions = secondaryActions.slice(0); + primaryActionsToSet.push(this.toggleMenuAction); + } - primaryActionsToSet.forEach(action => { - this.actionBar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) }); - }); - }; + primaryActionsToSet.forEach(action => { + this.actionBar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) }); + }); } private getKeybindingLabel(action: IAction): string | undefined { @@ -134,20 +174,15 @@ export class ToolBar extends Disposable { return withNullAsUndefined(key?.getLabel()); } - addPrimaryAction(primaryAction: IAction): () => void { - return () => { + private clear(): void { + this.submenuActionViewItems = []; + this.disposables.clear(); + this.actionBar.clear(); + } - // Add after the "..." action if we have secondary actions - if (this.hasSecondaryActions) { - let itemCount = this.actionBar.length(); - this.actionBar.push(primaryAction, { icon: true, label: false, index: itemCount, keybinding: this.getKeybindingLabel(primaryAction) }); - } - - // Otherwise just add to the end - else { - this.actionBar.push(primaryAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(primaryAction) }); - } - }; + dispose(): void { + this.clear(); + super.dispose(); } } @@ -166,10 +201,8 @@ class ToggleMenuAction extends Action { this.toggleDropdownMenu = toggleDropdownMenu; } - run(): Promise { + async run(): Promise { this.toggleDropdownMenu(); - - return Promise.resolve(true); } get menuActions(): ReadonlyArray { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 2b2ed1d052d..8eb81d59f07 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -5,16 +5,16 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode, addClasses, removeClasses } from 'vs/base/browser/dom'; +import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate, isInputElement, isMonacoEditor } from 'vs/base/browser/ui/list/listWidget'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list'; +import { append, $, getDomNodePagePosition, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent, TreeMouseEventTarget } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; -import { range, equals, distinctES6, fromSet } from 'vs/base/common/arrays'; +import { range, equals, distinctES6 } from 'vs/base/common/arrays'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { domEvent } from 'vs/base/browser/event'; import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; @@ -22,10 +22,10 @@ import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTr import { localize } from 'vs/nls'; import { disposableTimeout } from 'vs/base/common/async'; import { isMacintosh } from 'vs/base/common/platform'; -import { values } from 'vs/base/common/map'; import { clamp } from 'vs/base/common/numbers'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { SetMap } from 'vs/base/common/collections'; +import { treeItemExpandedIcon, treeFilterOnTypeOnIcon, treeFilterOnTypeOffIcon, treeFilterClearIcon } from 'vs/base/browser/ui/tree/treeIcons'; class TreeElementsDragAndDropData extends ElementsDragAndDropData { @@ -162,10 +162,31 @@ function asListOptions(modelProvider: () => ITreeModel { + return options.accessibilityProvider!.isChecked!(node.element); + } : undefined, + getRole: options.accessibilityProvider && options.accessibilityProvider.getRole ? (node) => { + return options.accessibilityProvider!.getRole!(node.element); + } : () => 'treeitem', getAriaLabel(e) { return options.accessibilityProvider!.getAriaLabel(e.element); }, - getAriaLevel(node) { + getWidgetAriaLabel() { + return options.accessibilityProvider!.getWidgetAriaLabel(); + }, + getWidgetRole: options.accessibilityProvider && options.accessibilityProvider.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree', + getAriaLevel: options.accessibilityProvider && options.accessibilityProvider.getAriaLevel ? (node) => options.accessibilityProvider!.getAriaLevel!(node.element) : (node) => { return node.depth; }, getActiveDescendantId: options.accessibilityProvider.getActiveDescendantId && (node => { @@ -178,26 +199,7 @@ function asListOptions(modelProvider: () => ITreeModel { - return options.ariaProvider!.isChecked!(node.element); - } : undefined, - getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => { - return options.ariaProvider!.getRole!(node.element); - } : undefined - } + enableKeyboardNavigation: options.simpleKeyboardNavigation }; } @@ -403,10 +405,10 @@ class TreeRenderer implements IListRenderer } if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) { - addClasses(templateData.twistie, 'codicon', 'codicon-chevron-down', 'collapsible'); - toggleClass(templateData.twistie, 'collapsed', node.collapsed); + templateData.twistie.classList.add(...treeItemExpandedIcon.classNamesArray, 'collapsible'); + templateData.twistie.classList.toggle('collapsed', node.collapsed); } else { - removeClasses(templateData.twistie, 'codicon', 'codicon-chevron-down', 'collapsible', 'collapsed'); + templateData.twistie.classList.remove(...treeItemExpandedIcon.classNamesArray, 'collapsible', 'collapsed'); } if (node.collapsible) { @@ -441,7 +443,7 @@ class TreeRenderer implements IListRenderer const guide = $('.indent-guide', { style: `width: ${this.indent}px` }); if (this.activeIndentNodes.has(parent)) { - addClass(guide, 'active'); + guide.classList.add('active'); } if (templateData.indent.childElementCount === 0) { @@ -484,13 +486,13 @@ class TreeRenderer implements IListRenderer this.activeIndentNodes.forEach(node => { if (!set.has(node)) { - this.renderedIndentGuides.forEach(node, line => removeClass(line, 'active')); + this.renderedIndentGuides.forEach(node, line => line.classList.remove('active')); } }); set.forEach(node => { if (!this.activeIndentNodes.has(node)) { - this.renderedIndentGuides.forEach(node, line => addClass(line, 'active')); + this.renderedIndentGuides.forEach(node, line => line.classList.add('active')); } }); @@ -505,8 +507,9 @@ class TreeRenderer implements IListRenderer } } -class TypeFilter implements ITreeFilter, IDisposable { +export type LabelFuzzyScore = { label: string; score: FuzzyScore }; +class TypeFilter implements ITreeFilter, IDisposable { private _totalCount = 0; get totalCount(): number { return this._totalCount; } private _matchCount = 0; @@ -529,7 +532,7 @@ class TypeFilter implements ITreeFilter, IDisposable { tree.onWillRefilter(this.reset, this, this.disposables); } - filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { + filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { if (this._filter) { const result = this._filter.filter(element, parentVisibility); @@ -560,27 +563,28 @@ class TypeFilter implements ITreeFilter, IDisposable { } const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element); - const labelStr = label && label.toString(); + const labels = Array.isArray(label) ? label : [label]; - if (typeof labelStr === 'undefined') { - return { data: FuzzyScore.Default, visibility: true }; - } - - const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0, true); - - if (!score) { - if (this.tree.options.filterOnType) { - return TreeVisibility.Recurse; - } else { + for (const l of labels) { + const labelStr = l && l.toString(); + if (typeof labelStr === 'undefined') { return { data: FuzzyScore.Default, visibility: true }; } - // DEMO: smarter filter ? - // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0, true); + if (score) { + this._matchCount++; + return labels.length === 1 ? + { data: score, visibility: true } : + { data: { label: labelStr, score: score }, visibility: true }; + } } - this._matchCount++; - return { data: score, visibility: true }; + if (this.tree.options.filterOnType) { + return TreeVisibility.Recurse; + } else { + return { data: FuzzyScore.Default, visibility: true }; + } } private reset(): void { @@ -644,14 +648,14 @@ class TypeFilterController implements IDisposable { const controls = append(this.domNode, $('.controls')); this._filterOnType = !!tree.options.filterOnType; - this.filterOnTypeDomNode = append(controls, $('input.filter.codicon.codicon-list-selection')); + this.filterOnTypeDomNode = append(controls, $('input.filter')); this.filterOnTypeDomNode.type = 'checkbox'; this.filterOnTypeDomNode.checked = this._filterOnType; this.filterOnTypeDomNode.tabIndex = -1; - this.updateFilterOnTypeTitle(); + this.updateFilterOnTypeTitleAndIcon(); domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); - this.clearDomNode = append(controls, $('button.clear.codicon.codicon-close')); + this.clearDomNode = append(controls, $('button.clear' + treeFilterClearIcon.cssSelector)); this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); @@ -704,7 +708,7 @@ class TypeFilterController implements IDisposable { .map(e => new StandardKeyboardEvent(e)) .filter(this.keyboardNavigationEventFilter || (() => true)) .filter(() => this.automaticKeyboardNavigation || this.triggered) - .filter(e => this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey))) + .filter(e => (this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) && !(e.keyCode === KeyCode.DownArrow || e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.LeftArrow || e.keyCode === KeyCode.RightArrow)) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey))) .forEach(e => { e.stopPropagation(); e.preventDefault(); }) .event; @@ -807,7 +811,7 @@ class TypeFilterController implements IDisposable { const onDragOver = (event: DragEvent) => { event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - const x = event.screenX - left; + const x = event.clientX - left; if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; } @@ -831,10 +835,10 @@ class TypeFilterController implements IDisposable { }; updatePosition(); - removeClass(this.domNode, positionClassName); + this.domNode.classList.remove(positionClassName); - addClass(this.domNode, 'dragging'); - disposables.add(toDisposable(() => removeClass(this.domNode, 'dragging'))); + this.domNode.classList.add('dragging'); + disposables.add(toDisposable(() => this.domNode.classList.remove('dragging'))); domEvent(document, 'dragover')(onDragOver, null, disposables); domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); @@ -857,13 +861,17 @@ class TypeFilterController implements IDisposable { this.tree.refilter(); this.tree.domFocus(); this.render(); - this.updateFilterOnTypeTitle(); + this.updateFilterOnTypeTitleAndIcon(); } - private updateFilterOnTypeTitle(): void { + private updateFilterOnTypeTitleAndIcon(): void { if (this.filterOnType) { + this.filterOnTypeDomNode.classList.remove(...treeFilterOnTypeOffIcon.classNamesArray); + this.filterOnTypeDomNode.classList.add(...treeFilterOnTypeOnIcon.classNamesArray); this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); } else { + this.filterOnTypeDomNode.classList.remove(...treeFilterOnTypeOnIcon.classNamesArray); + this.filterOnTypeDomNode.classList.add(...treeFilterOnTypeOffIcon.classNamesArray); this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); } } @@ -875,11 +883,11 @@ class TypeFilterController implements IDisposable { this.messageDomNode.textContent = localize('empty', "No elements found"); this._empty = true; } else { - this.messageDomNode.innerHTML = ''; + this.messageDomNode.innerText = ''; this._empty = false; } - toggleClass(this.domNode, 'no-matches', noMatches); + this.domNode.classList.toggle('no-matches', noMatches); this.domNode.title = localize('found', "Matched {0} out of {1} elements", this.filter.matchCount, this.filter.totalCount); this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern; @@ -911,17 +919,6 @@ class TypeFilterController implements IDisposable { } } -function isInputElement(e: HTMLElement): boolean { - return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; -} - -function asTreeEvent(event: IListEvent>): ITreeEvent { - return { - elements: event.elements.map(node => node.element), - browserEvent: event.browserEvent - }; -} - function asTreeMouseEvent(event: IListMouseEvent>): ITreeMouseEvent { let target: TreeMouseEventTarget = TreeMouseEventTarget.Unknown; @@ -954,7 +951,10 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly automaticKeyboardNavigation?: boolean; readonly simpleKeyboardNavigation?: boolean; readonly filterOnType?: boolean; - readonly openOnSingleClick?: boolean; + readonly smoothScrolling?: boolean; + readonly horizontalScrolling?: boolean; + readonly expandOnlyOnDoubleClick?: boolean; + readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -962,7 +962,6 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; - readonly expandOnlyOnTwistieClick?: boolean | ((e: T) => boolean); readonly additionalScrollHeight?: number; } @@ -995,7 +994,7 @@ class Trait { constructor(private identityProvider?: IIdentityProvider) { } set(nodes: ITreeNode[], browserEvent?: UIEvent): void { - if (equals(this.nodes, nodes)) { + if (!(browserEvent as any)?.__forceEvent && equals(this.nodes, nodes)) { return; } @@ -1034,7 +1033,7 @@ class Trait { const set = this.createNodeSet(); const visit = (node: ITreeNode) => set.delete(node); deletedNodes.forEach(node => dfs(node, visit)); - this.set(values(set)); + this.set([...set.values()]); return; } @@ -1083,26 +1082,24 @@ class TreeNodeListMouseController extends MouseController< super(list); } - protected onPointer(e: IListMouseEvent>): void { - if (isInputElement(e.browserEvent.target as HTMLElement)) { + protected onViewPointer(e: IListMouseEvent>): void { + if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) { return; } const node = e.element; if (!node) { - return super.onPointer(e); + return super.onViewPointer(e); } if (this.isSelectionRangeChangeEvent(e) || this.isSelectionSingleChangeEvent(e)) { - return super.onPointer(e); + return super.onViewPointer(e); } - const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); - - if (!this.tree.openOnSingleClick && e.browserEvent.detail !== 2 && !onTwistie) { - return super.onPointer(e); - } + const target = e.browserEvent.target as HTMLElement; + const onTwistie = target.classList.contains('monaco-tl-twistie') + || (target.classList.contains('monaco-icon-label') && target.classList.contains('folder-icon') && e.browserEvent.offsetX < 16); let expandOnlyOnTwistieClick = false; @@ -1112,8 +1109,14 @@ class TreeNodeListMouseController extends MouseController< expandOnlyOnTwistieClick = !!this.tree.expandOnlyOnTwistieClick; } - if (expandOnlyOnTwistieClick && !onTwistie) { - return super.onPointer(e); + const clickedOnFocus = this.tree.getFocus()[0] === node.element; + + if (expandOnlyOnTwistieClick && !onTwistie && e.browserEvent.detail !== 2 && !(clickedOnFocus && !node.collapsed)) { + return super.onViewPointer(e); + } + + if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) { + return super.onViewPointer(e); } if (node.collapsible) { @@ -1127,11 +1130,11 @@ class TreeNodeListMouseController extends MouseController< } } - super.onPointer(e); + super.onViewPointer(e); } protected onDoubleClick(e: IListMouseEvent>): void { - const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); + const onTwistie = (e.browserEvent.target as HTMLElement).classList.contains('monaco-tl-twistie'); if (onTwistie) { return; @@ -1230,12 +1233,12 @@ export abstract class AbstractTree implements IDisposable get onDidChangeFocus(): Event> { return this.eventBufferer.wrapEvent(this.focus.onDidChange); } get onDidChangeSelection(): Event> { return this.eventBufferer.wrapEvent(this.selection.onDidChange); } - get onDidOpen(): Event> { return Event.map(this.view.onDidOpen, asTreeEvent); } - get onDidPin(): Event> { return Event.map(this.view.onDidPin, asTreeEvent); } get onMouseClick(): Event> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); } get onMouseDblClick(): Event> { return Event.map(this.view.onMouseDblClick, asTreeMouseEvent); } get onContextMenu(): Event> { return Event.map(this.view.onContextMenu, asTreeContextMenuEvent); } + get onTap(): Event> { return Event.map(this.view.onTap, asTreeMouseEvent); } + get onPointer(): Event> { return Event.map(this.view.onPointer, asTreeMouseEvent); } get onKeyDown(): Event { return this.view.onKeyDown; } get onKeyUp(): Event { return this.view.onKeyUp; } @@ -1253,7 +1256,7 @@ export abstract class AbstractTree implements IDisposable get filterOnType(): boolean { return !!this._options.filterOnType; } get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } - get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; } + get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } private readonly _onDidUpdateOptions = new Emitter>(); @@ -1320,7 +1323,7 @@ export abstract class AbstractTree implements IDisposable set.add(node); } - return fromSet(set); + return [...set.values()]; }).event; if (_options.keyboardSupport !== false) { @@ -1341,7 +1344,7 @@ export abstract class AbstractTree implements IDisposable } this.styleElement = createStyleSheet(this.view.getHTMLElement()); - toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always); + this.getHTMLElement().classList.toggle('always', this._options.renderIndentGuides === RenderIndentGuides.Always); } updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void { @@ -1353,7 +1356,9 @@ export abstract class AbstractTree implements IDisposable this.view.updateOptions({ enableKeyboardNavigation: this._options.simpleKeyboardNavigation, - automaticKeyboardNavigation: this._options.automaticKeyboardNavigation + automaticKeyboardNavigation: this._options.automaticKeyboardNavigation, + smoothScrolling: this._options.smoothScrolling, + horizontalScrolling: this._options.horizontalScrolling }); if (this.typeFilterController) { @@ -1362,7 +1367,7 @@ export abstract class AbstractTree implements IDisposable this._onDidUpdateOptions.fire(this._options); - toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always); + this.getHTMLElement().classList.toggle('always', this._options.renderIndentGuides === RenderIndentGuides.Always); } get options(): IAbstractTreeOptions { @@ -1444,6 +1449,14 @@ export abstract class AbstractTree implements IDisposable return node.element; } + get ariaLabel(): string { + return this.view.ariaLabel; + } + + set ariaLabel(value: string) { + this.view.ariaLabel = value; + } + domFocus(): void { this.view.domFocus(); } @@ -1465,11 +1478,7 @@ export abstract class AbstractTree implements IDisposable content.push(`.monaco-list${suffix} .monaco-tl-indent > .indent-guide.active { border-color: ${styles.treeIndentGuidesStroke}; }`); } - const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; - } - + this.styleElement.textContent = content.join('\n'); this.view.style(styles); } @@ -1584,11 +1593,6 @@ export abstract class AbstractTree implements IDisposable return this.focus.get(); } - open(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.open(indexes, browserEvent); - } - reveal(location: TRef, relativeTop?: number): void { this.model.expandTo(location); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 3786e910663..970eddf5c85 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -11,16 +11,15 @@ import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle' import { Emitter, Event } from 'vs/base/common/event'; import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; -import { toggleClass } from 'vs/base/browser/dom'; -import { values } from 'vs/base/common/map'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { IThemable } from 'vs/base/common/styler'; import { isFilterResult, getVisibleState } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { treeItemLoadingIcon } from 'vs/base/browser/ui/tree/treeIcons'; interface IAsyncDataTreeNode { element: TInput | T; @@ -109,7 +108,11 @@ class AsyncDataTreeRenderer implements IT } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { - toggleClass(twistieElement, 'codicon-loading', element.slow); + if (element.slow) { + twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + } else { + twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + } return false; } @@ -128,14 +131,14 @@ class AsyncDataTreeRenderer implements IT } } -function asTreeEvent(e: ITreeEvent>): ITreeEvent { +function asTreeEvent(e: ITreeEvent | null>): ITreeEvent { return { browserEvent: e.browserEvent, - elements: e.elements.map(e => e.element as T) + elements: e.elements.map(e => e!.element as T) }; } -function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent { +function asTreeMouseEvent(e: ITreeMouseEvent | null>): ITreeMouseEvent { return { browserEvent: e.browserEvent, element: e.element && e.element.element as T, @@ -143,7 +146,7 @@ function asTreeMouseEvent(e: ITreeMouseEvent(e: ITreeContextMenuEvent>): ITreeContextMenuEvent { +function asTreeContextMenuEvent(e: ITreeContextMenuEvent | null>): ITreeContextMenuEvent { return { browserEvent: e.browserEvent, element: e.element && e.element.element as T, @@ -231,9 +234,21 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt }, accessibilityProvider: options.accessibilityProvider && { ...options.accessibilityProvider, + getPosInSet: undefined, + getSetSize: undefined, + getRole: options.accessibilityProvider!.getRole ? (el) => { + return options.accessibilityProvider!.getRole!(el.element as T); + } : () => 'treeitem', + isChecked: options.accessibilityProvider!.isChecked ? (e) => { + return !!(options.accessibilityProvider?.isChecked!(e.element as T)); + } : undefined, getAriaLabel(e) { return options.accessibilityProvider!.getAriaLabel(e.element as T); }, + getWidgetAriaLabel() { + return options.accessibilityProvider!.getWidgetAriaLabel(); + }, + getWidgetRole: options.accessibilityProvider!.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree', getAriaLevel: options.accessibilityProvider!.getAriaLevel && (node => { return options.accessibilityProvider!.getAriaLevel!(node.element as T); }), @@ -258,20 +273,6 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) ) ), - ariaProvider: options.ariaProvider && { - getPosInSet(el, index) { - return options.ariaProvider!.getPosInSet(el.element as T, index); - }, - getSetSize(el, index, listLength) { - return options.ariaProvider!.getSetSize(el.element as T, index, listLength); - }, - getRole: options.ariaProvider!.getRole ? (el) => { - return options.ariaProvider!.getRole!(el.element as T); - } : undefined, - isChecked: options.ariaProvider!.isChecked ? (e) => { - return options.ariaProvider?.isChecked!(e.element as T); - } : undefined - }, additionalScrollHeight: options.additionalScrollHeight }; } @@ -312,7 +313,7 @@ export class AsyncDataTree implements IDisposable private readonly collapseByDefault?: { (e: T): boolean; }; private readonly subTreeRefreshPromises = new Map, Promise>(); - private readonly refreshPromises = new Map, CancelablePromise>(); + private readonly refreshPromises = new Map, CancelablePromise>>(); protected readonly identityProvider?: IIdentityProvider; private readonly autoExpandSingleChildren: boolean; @@ -328,12 +329,13 @@ export class AsyncDataTree implements IDisposable get onDidChangeFocus(): Event> { return Event.map(this.tree.onDidChangeFocus, asTreeEvent); } get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } - get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } get onKeyDown(): Event { return this.tree.onKeyDown; } get onMouseClick(): Event> { return Event.map(this.tree.onMouseClick, asTreeMouseEvent); } get onMouseDblClick(): Event> { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); } get onContextMenu(): Event> { return Event.map(this.tree.onContextMenu, asTreeContextMenuEvent); } + get onTap(): Event> { return Event.map(this.tree.onTap, asTreeMouseEvent); } + get onPointer(): Event> { return Event.map(this.tree.onPointer, asTreeMouseEvent); } get onDidFocus(): Event { return this.tree.onDidFocus; } get onDidBlur(): Event { return this.tree.onDidBlur; } @@ -342,7 +344,6 @@ export class AsyncDataTree implements IDisposable get onDidUpdateOptions(): Event { return this.tree.onDidUpdateOptions; } get filterOnType(): boolean { return this.tree.filterOnType; } - get openOnSingleClick(): boolean { return this.tree.openOnSingleClick; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { if (typeof this.tree.expandOnlyOnTwistieClick === 'boolean') { return this.tree.expandOnlyOnTwistieClick; @@ -405,6 +406,10 @@ export class AsyncDataTree implements IDisposable this.tree.updateOptions(options); } + get options(): IAsyncDataTreeOptions { + return this.tree.options as IAsyncDataTreeOptions; + } + // Widget getHTMLElement(): HTMLElement { @@ -447,6 +452,14 @@ export class AsyncDataTree implements IDisposable return this.tree.lastVisibleElement!.element as T; } + get ariaLabel(): string { + return this.tree.ariaLabel; + } + + set ariaLabel(value: string) { + this.tree.ariaLabel = value; + } + domFocus(): void { this.tree.domFocus(); } @@ -562,6 +575,10 @@ export class AsyncDataTree implements IDisposable const node = this.getDataNode(element); + if (this.tree.hasElement(node) && !this.tree.isCollapsible(node)) { + return false; + } + if (node.refreshPromise) { await this.root.refreshPromise; await Event.toPromise(this._onDidRender.event); @@ -653,11 +670,6 @@ export class AsyncDataTree implements IDisposable return nodes.map(n => n!.element as T); } - open(elements: T[], browserEvent?: UIEvent): void { - const nodes = elements.map(e => this.getDataNode(e)); - this.tree.open(nodes, browserEvent); - } - reveal(element: T, relativeTop?: number): void { this.tree.reveal(this.getDataNode(element), relativeTop); } @@ -735,10 +747,10 @@ export class AsyncDataTree implements IDisposable private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise[]> { node.hasChildren = !!this.dataSource.hasChildren(node.element!); - let childrenPromise: Promise; + let childrenPromise: Promise>; if (!node.hasChildren) { - childrenPromise = Promise.resolve([]); + childrenPromise = Promise.resolve(Iterable.empty()); } else { const slowTimeout = timeout(800); @@ -772,7 +784,7 @@ export class AsyncDataTree implements IDisposable } } - private doGetChildren(node: IAsyncDataTreeNode): Promise { + private doGetChildren(node: IAsyncDataTreeNode): Promise> { let result = this.refreshPromises.get(node); if (result) { @@ -786,10 +798,14 @@ export class AsyncDataTree implements IDisposable this.refreshPromises.set(node, result); - return result.finally(() => this.refreshPromises.delete(node)); + return result.finally(() => { this.refreshPromises.delete(node); }); } - private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { + private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent | null, any>): void { + if (node.element === null) { + return; + } + if (!node.collapsed && node.element.stale) { if (deep) { this.collapse(node.element.element as T); @@ -800,7 +816,9 @@ export class AsyncDataTree implements IDisposable } } - private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): IAsyncDataTreeNode[] { + private setChildren(node: IAsyncDataTreeNode, childrenElementsIterable: Iterable, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): IAsyncDataTreeNode[] { + const childrenElements = [...childrenElementsIterable]; + // perf: if the node was and still is a leaf, avoid all this hassle if (node.children.length === 0 && childrenElements.length === 0) { return []; @@ -883,7 +901,7 @@ export class AsyncDataTree implements IDisposable return childAsyncDataTreeNode; }); - for (const node of values(nodesToForget)) { + for (const node of nodesToForget.values()) { dfs(node, node => this.nodes.delete(node.element as T)); } @@ -934,15 +952,15 @@ export class AsyncDataTree implements IDisposable return { element: node, - children: node.hasChildren ? Iterator.map(Iterator.fromArray(node.children), child => this.asTreeElement(child, viewStateContext)) : [], + children: node.hasChildren ? Iterable.map(node.children, child => this.asTreeElement(child, viewStateContext)) : [], collapsible: node.hasChildren, collapsed }; } - protected processChildren(children: T[]): T[] { + protected processChildren(children: Iterable): Iterable { if (this.sorter) { - children.sort(this.sorter.compare.bind(this.sorter)); + children = [...children].sort(this.sorter.compare.bind(this.sorter)); } return children; @@ -1033,7 +1051,11 @@ class CompressibleAsyncDataTreeRenderer i } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { - toggleClass(twistieElement, 'codicon-loading', element.slow); + if (element.slow) { + twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + } else { + twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + } return false; } @@ -1234,9 +1256,9 @@ export class CompressibleAsyncDataTree extends As // For compressed async data trees, `TreeVisibility.Recurse` doesn't currently work // and we have to filter everything beforehand // Related to #85193 and #85835 - protected processChildren(children: T[]): T[] { + protected processChildren(children: Iterable): Iterable { if (this.filter) { - children = children.filter(e => { + children = Iterable.filter(children, e => { const result = this.filter!.filter(e, TreeVisibility.Visible); const visibility = getVisibility(result); diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index f1da4355047..150f49ce53f 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree'; import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; // Exported only for test reasons, do not use directly export interface ICompressedTreeElement extends ITreeElement { - readonly children?: ISequence>; + readonly children?: Iterable>; readonly incompressible?: boolean; } @@ -27,7 +27,7 @@ function noCompress(element: ICompressedTreeElement): ITreeElement(element: ICompressedTreeElement): ITreeElement>; + let childrenIterator: Iterable>; let children: ITreeElement[]; while (true) { - childrenIterator = Iterator.from(element.children); - children = Iterator.collect(childrenIterator, 2); + [children, childrenIterator] = Iterable.consume(Iterable.from(element.children), 2); if (children.length !== 1) { break; @@ -60,19 +59,19 @@ export function compress(element: ICompressedTreeElement): ITreeElement(element: ITreeElement>, index = 0): ICompressedTreeElement { - let children: Iterator>; + let children: Iterable>; if (index < element.element.elements.length - 1) { - children = Iterator.single(_decompress(element, index + 1)); + children = [_decompress(element, index + 1)]; } else { - children = Iterator.map(Iterator.from(element.children), el => _decompress(el, 0)); + children = Iterable.map(Iterable.from(element.children), el => _decompress(el, 0)); } if (index === 0 && element.element.incompressible) { @@ -98,12 +97,12 @@ export function decompress(element: ITreeElement>): IC return _decompress(element, 0); } -function splice(treeElement: ICompressedTreeElement, element: T, children: Iterator>): ICompressedTreeElement { +function splice(treeElement: ICompressedTreeElement, element: T, children: Iterable>): ICompressedTreeElement { if (treeElement.element === element) { return { ...treeElement, children }; } - return { ...treeElement, children: Iterator.map(Iterator.from(treeElement.children), e => splice(e, element, children)) }; + return { ...treeElement, children: Iterable.map(Iterable.from(treeElement.children), e => splice(e, element, children)) }; } interface ICompressedObjectTreeModelOptions extends IObjectTreeModelOptions, TFilterData> { @@ -127,7 +126,7 @@ export class CompressedObjectTreeModel, TFilterData e constructor( private user: string, - list: ISpliceable, TFilterData>>, + list: IList, TFilterData>>, options: ICompressedObjectTreeModelOptions = {} ) { this.model = new ObjectTreeModel(user, list, options); @@ -136,10 +135,10 @@ export class CompressedObjectTreeModel, TFilterData e setChildren( element: T | null, - children: ISequence> | undefined + children: Iterable> = Iterable.empty() ): void { if (element === null) { - const compressedChildren = Iterator.map(Iterator.from(children), this.enabled ? compress : noCompress); + const compressedChildren = Iterable.map(children, this.enabled ? compress : noCompress); this._setChildren(null, compressedChildren); return; } @@ -155,7 +154,7 @@ export class CompressedObjectTreeModel, TFilterData e const parent = this.model.getNode(compressedParentNode) as ITreeNode, TFilterData>; const decompressedElement = decompress(node); - const splicedElement = splice(decompressedElement, element, Iterator.from(children)); + const splicedElement = splice(decompressedElement, element, children); const recompressedElement = (this.enabled ? compress : noCompress)(splicedElement); const parentChildren = parent.children @@ -176,15 +175,15 @@ export class CompressedObjectTreeModel, TFilterData e this.enabled = enabled; const root = this.model.getNode(); - const rootChildren = Iterator.from(root.children as ITreeNode>[]); - const decompressedRootChildren = Iterator.map(rootChildren, decompress); - const recompressedRootChildren = Iterator.map(decompressedRootChildren, enabled ? compress : noCompress); + const rootChildren = root.children as ITreeNode>[]; + const decompressedRootChildren = Iterable.map(rootChildren, decompress); + const recompressedRootChildren = Iterable.map(decompressedRootChildren, enabled ? compress : noCompress); this._setChildren(null, recompressedRootChildren); } private _setChildren( node: ICompressedTreeNode | null, - children: ISequence>> | undefined + children: Iterable>> ): void { const insertedElements = new Set(); const _onDidCreateNode = (node: ITreeNode, TFilterData>) => { @@ -205,6 +204,10 @@ export class CompressedObjectTreeModel, TFilterData e this.model.setChildren(node, children, _onDidCreateNode, _onDidDeleteNode); } + has(element: T | null): boolean { + return this.nodes.has(element); + } + getListIndex(location: T | null): number { const node = this.getCompressedNode(location); return this.model.getListIndex(node); @@ -287,6 +290,16 @@ export class CompressedObjectTreeModel, TFilterData e this.model.rerender(compressedNode); } + updateElementHeight(element: T, height: number): void { + const compressedNode = this.getCompressedNode(element); + + if (!compressedNode) { + return; + } + + this.model.updateElementHeight(compressedNode, height); + } + refilter(): void { this.model.refilter(); } @@ -337,10 +350,13 @@ class CompressedTreeNodeWrapper implements ITreeNode(nodeMapper: CompressedNodeWeakMapper, list: ISpliceable>): ISpliceable, TFilterData>> { +function mapList(nodeMapper: CompressedNodeWeakMapper, list: IList>): IList, TFilterData>> { return { splice(start: number, deleteCount: number, toInsert: ITreeNode, TFilterData>[]): void { list.splice(start, deleteCount, toInsert.map(node => nodeMapper.map(node)) as ITreeNode[]); + }, + updateElementHeight(index: number, height: number): void { + list.updateElementHeight(index, height); } }; } @@ -399,7 +415,7 @@ export class CompressibleObjectTreeModel, TFilterData constructor( user: string, - list: ISpliceable>, + list: IList>, options: ICompressibleObjectTreeModelOptions = {} ) { this.elementMapper = options.elementMapper || DefaultElementMapper; @@ -409,7 +425,7 @@ export class CompressibleObjectTreeModel, TFilterData this.model = new CompressedObjectTreeModel(user, mapList(this.nodeMapper, list), mapOptions(compressedNodeUnwrapper, options)); } - setChildren(element: T | null, children?: ISequence>): void { + setChildren(element: T | null, children: Iterable> = Iterable.empty()): void { this.model.setChildren(element, children); } @@ -421,6 +437,10 @@ export class CompressibleObjectTreeModel, TFilterData this.model.setCompressionEnabled(enabled); } + has(location: T | null): boolean { + return this.model.has(location); + } + getListIndex(location: T | null): number { return this.model.getListIndex(location); } @@ -485,6 +505,10 @@ export class CompressibleObjectTreeModel, TFilterData return this.model.rerender(location); } + updateElementHeight(element: T, height: number): void { + this.model.updateElementHeight(element, height); + } + refilter(): void { return this.model.refilter(); } diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 2dbfc990a61..4e97f4ed9b6 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; -import { ISpliceable } from 'vs/base/common/sequence'; import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource, TreeError } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; export interface IDataTreeOptions extends IAbstractTreeOptions { readonly sorter?: ITreeSorter; @@ -37,7 +37,7 @@ export class DataTree extends AbstractTree, options: IDataTreeOptions = {} ) { - super(user, container, delegate, renderers, options); + super(user, container, delegate, renderers, options as IDataTreeOptions); this.identityProvider = options.identityProvider; } @@ -158,9 +158,9 @@ export class DataTree extends AbstractTree boolean | undefined): { elements: Iterator>, size: number } { - const children = this.dataSource.getChildren(element); - const elements = Iterator.map>(Iterator.fromArray(children), element => { + private iterate(element: TInput | T, isCollapsed?: (el: T) => boolean | undefined): { elements: Iterable>, size: number } { + const children = [...this.dataSource.getChildren(element)]; + const elements = Iterable.map(children, element => { const { elements: children, size } = this.iterate(element, isCollapsed); const collapsible = this.dataSource.hasChildren ? this.dataSource.hasChildren(element) : undefined; const collapsed = size === 0 ? undefined : (isCollapsed && isCollapsed(element)); @@ -171,7 +171,7 @@ export class DataTree extends AbstractTree>, options: IDataTreeOptions): ITreeModel { + protected createModel(user: string, view: IList>, options: IDataTreeOptions): ITreeModel { return new ObjectTreeModel(user, view, options); } @@ -182,7 +182,7 @@ export class DataTree extends AbstractTree this.identityProvider!.getId(element).toString(); + const getId = (element: T | null) => this.identityProvider!.getId(element!).toString(); const focus = this.getFocus().map(getId); const selection = this.getSelection().map(getId); diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index 7cf73ee5fd7..5272fa1971a 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; -import { ISpliceable } from 'vs/base/common/sequence'; -import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { IndexTreeModel, IList } from 'vs/base/browser/ui/tree/indexTreeModel'; import { ITreeElement, ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -28,7 +27,7 @@ export class IndexTree extends AbstractTree> = Iterator.empty()): void { + splice(location: number[], deleteCount: number, toInsert: Iterable> = Iterable.empty()): void { this.model.splice(location, deleteCount, toInsert); } @@ -41,7 +40,11 @@ export class IndexTree extends AbstractTree>, options: IIndexTreeOptions): ITreeModel { + updateElementHeight(location: number[], height: number): void { + this.model.updateElementHeight(location, height); + } + + protected createModel(user: string, view: IList>, options: IIndexTreeOptions): ITreeModel { return new IndexTreeModel(user, view, this.rootElement, options); } } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index c7f287e873b..362ef7e1a30 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -6,7 +6,7 @@ import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree'; import { tail2 } from 'vs/base/common/arrays'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; -import { ISequence, Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { ISpliceable } from 'vs/base/common/sequence'; // Exported for tests @@ -18,6 +18,7 @@ export interface IIndexTreeNode extends ITreeNode extends ISpliceable { + updateElementHeight(index: number, height: number): void; +} + export class IndexTreeModel, TFilterData = void> implements ITreeModel { readonly rootRef = []; @@ -77,7 +82,7 @@ export class IndexTreeModel, TFilterData = voi constructor( private user: string, - private list: ISpliceable>, + private list: IList>, rootElement: T, options: IIndexTreeModelOptions = {} ) { @@ -95,6 +100,7 @@ export class IndexTreeModel, TFilterData = voi collapsible: false, collapsed: false, renderNodeCount: 0, + visibility: TreeVisibility.Visible, visible: true, filterData: undefined }; @@ -103,7 +109,7 @@ export class IndexTreeModel, TFilterData = voi splice( location: number[], deleteCount: number, - toInsert?: ISequence>, + toInsert: Iterable> = Iterable.empty(), onDidCreateNode?: (node: ITreeNode) => void, onDidDeleteNode?: (node: ITreeNode) => void ): void { @@ -113,7 +119,7 @@ export class IndexTreeModel, TFilterData = voi const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const treeListElementsToInsert: ITreeNode[] = []; - const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); + const nodesToInsertIterator = Iterable.map(toInsert, el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); const lastIndex = location[location.length - 1]; @@ -134,14 +140,14 @@ export class IndexTreeModel, TFilterData = voi let insertedVisibleChildrenCount = 0; let renderNodeCount = 0; - Iterator.forEach(nodesToInsertIterator, child => { + for (const child of nodesToInsertIterator) { nodesToInsert.push(child); renderNodeCount += child.renderNodeCount; if (child.visible) { child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++; } - }); + } const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); @@ -185,6 +191,17 @@ export class IndexTreeModel, TFilterData = voi } this._onDidSplice.fire({ insertedNodes: nodesToInsert, deletedNodes }); + + let node: IIndexTreeNode | undefined = parentNode; + + while (node) { + if (node.visibility === TreeVisibility.Recurse) { + this.refilter(); + break; + } + + node = node.parent; + } } rerender(location: number[]): void { @@ -194,11 +211,24 @@ export class IndexTreeModel, TFilterData = voi const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); - if (revealed) { + if (node.visible && revealed) { this.list.splice(listIndex, 1, [node]); } } + updateElementHeight(location: number[], height: number): void { + if (location.length === 0) { + throw new TreeError(this.user, 'Invalid tree location'); + } + + const { listIndex } = this.getTreeNodeWithListIndex(location); + this.list.updateElementHeight(listIndex, height); + } + + has(location: number[]): boolean { + return this.hasTreeNode(location); + } + getListIndex(location: number[]): number { const { listIndex, visible, revealed } = this.getTreeNodeWithListIndex(location); return visible && revealed ? listIndex : -1; @@ -291,6 +321,8 @@ export class IndexTreeModel, TFilterData = voi if (isCollapsibleStateUpdate(update)) { result = node.collapsible !== update.collapsible; node.collapsible = update.collapsible; + } else if (!node.collapsible) { + result = false; } else { result = node.collapsed !== update.collapsed; node.collapsed = update.collapsed; @@ -349,31 +381,33 @@ export class IndexTreeModel, TFilterData = voi collapsible: typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'), collapsed: typeof treeElement.collapsed === 'undefined' ? this.collapseByDefault : treeElement.collapsed, renderNodeCount: 1, + visibility: TreeVisibility.Visible, visible: true, filterData: undefined }; const visibility = this._filterNode(node, parentVisibility); + node.visibility = visibility; if (revealed) { treeListElements.push(node); } - const childElements = Iterator.from(treeElement.children); + const childElements = treeElement.children || Iterable.empty(); const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed; - const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); + const childNodes = Iterable.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); let visibleChildrenCount = 0; let renderNodeCount = 1; - Iterator.forEach(childNodes, child => { + for (const child of childNodes) { node.children.push(child); renderNodeCount += child.renderNodeCount; if (child.visible) { child.visibleChildIndex = visibleChildrenCount++; } - }); + } node.collapsible = node.collapsible || node.children.length > 0; node.visibleChildrenCount = visibleChildrenCount; @@ -516,6 +550,21 @@ export class IndexTreeModel, TFilterData = voi } } + // cheap + private hasTreeNode(location: number[], node: IIndexTreeNode = this.root): boolean { + if (!location || location.length === 0) { + return true; + } + + const [index, ...rest] = location; + + if (index < 0 || index > node.children.length) { + return false; + } + + return this.hasTreeNode(rest, node.children[index]); + } + // cheap private getTreeNode(location: number[], node: IIndexTreeNode = this.root): IIndexTreeNode { if (!location || location.length === 0) { diff --git a/src/vs/base/browser/ui/tree/media/loading-dark.svg b/src/vs/base/browser/ui/tree/media/loading-dark.svg deleted file mode 100644 index 7dc1ebd8cf0..00000000000 --- a/src/vs/base/browser/ui/tree/media/loading-dark.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/tree/media/loading-hc.svg b/src/vs/base/browser/ui/tree/media/loading-hc.svg deleted file mode 100644 index c3633c0ddab..00000000000 --- a/src/vs/base/browser/ui/tree/media/loading-hc.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/tree/media/loading.svg b/src/vs/base/browser/ui/tree/media/loading.svg deleted file mode 100644 index e762f06d5e6..00000000000 --- a/src/vs/base/browser/ui/tree/media/loading.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg b/src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg deleted file mode 100644 index c2c2298dd5c..00000000000 --- a/src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg b/src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg deleted file mode 100644 index 3732cbc04b8..00000000000 --- a/src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg b/src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg deleted file mode 100644 index 1952ad63f84..00000000000 --- a/src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg b/src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg deleted file mode 100644 index 5570923e175..00000000000 --- a/src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg b/src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg deleted file mode 100644 index b370009330c..00000000000 --- a/src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/media/tree-expanded-light.svg b/src/vs/base/browser/ui/tree/media/tree-expanded-light.svg deleted file mode 100644 index 939ebc8b969..00000000000 --- a/src/vs/base/browser/ui/tree/media/tree-expanded-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 66cd61d0b87..06f775d6246 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -60,6 +60,7 @@ transform: rotate(-90deg); } -.monaco-tl-twistie.codicon-loading::before { - animation: codicon-spin 1.25s linear infinite; +.monaco-tl-twistie.codicon-tree-item-loading::before { + /* Use steps to throttle FPS to reduce CPU usage */ + animation: codicon-spin 1.25s steps(30) infinite; } diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 757683c18af..60601a51775 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISequence } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { AbstractTree, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; -import { ISpliceable } from 'vs/base/common/sequence'; import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { Event } from 'vs/base/common/event'; import { CompressibleObjectTreeModel, ElementMapper, ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { memoize } from 'vs/base/common/decorators'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; export interface IObjectTreeOptions extends IAbstractTreeOptions { readonly sorter?: ITreeSorter; @@ -30,10 +30,10 @@ export class ObjectTree, TFilterData = void> extends renderers: ITreeRenderer[], options: IObjectTreeOptions = {} ) { - super(user, container, delegate, renderers, options); + super(user, container, delegate, renderers, options as IObjectTreeOptions); } - setChildren(element: T | null, children?: ISequence>): void { + setChildren(element: T | null, children: Iterable> = Iterable.empty()): void { this.model.setChildren(element, children); } @@ -46,11 +46,19 @@ export class ObjectTree, TFilterData = void> extends this.model.rerender(element); } + updateElementHeight(element: T, height: number): void { + this.model.updateElementHeight(element, height); + } + resort(element: T, recursive = true): void { this.model.resort(element, recursive); } - protected createModel(user: string, view: ISpliceable>, options: IObjectTreeOptions): ITreeModel { + hasElement(element: T): boolean { + return this.model.has(element); + } + + protected createModel(user: string, view: IList>, options: IObjectTreeOptions): ITreeModel { return new ObjectTreeModel(user, view, options); } } @@ -177,14 +185,14 @@ export class CompressibleObjectTree, TFilterData = vo ) { const compressedTreeNodeProvider = () => this; const compressibleRenderers = renderers.map(r => new CompressibleRenderer(compressedTreeNodeProvider, r)); - super(user, container, delegate, compressibleRenderers, asObjectTreeOptions(compressedTreeNodeProvider, options)); + super(user, container, delegate, compressibleRenderers, asObjectTreeOptions(compressedTreeNodeProvider, options)); } - setChildren(element: T | null, children?: ISequence>): void { + setChildren(element: T | null, children: Iterable> = Iterable.empty()): void { this.model.setChildren(element, children); } - protected createModel(user: string, view: ISpliceable>, options: ICompressibleObjectTreeOptions): ITreeModel { + protected createModel(user: string, view: IList>, options: ICompressibleObjectTreeOptions): ITreeModel { return new CompressibleObjectTreeModel(user, view, options); } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 9d6b9749510..59afa8d4b98 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterator'; -import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { Iterable } from 'vs/base/common/iterator'; +import { IndexTreeModel, IIndexTreeModelOptions, IList } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree'; import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; @@ -14,8 +13,9 @@ import { mergeSort } from 'vs/base/common/arrays'; export type ITreeNodeCallback = (node: ITreeNode) => void; export interface IObjectTreeModel, TFilterData extends NonNullable = void> extends ITreeModel { - setChildren(element: T | null, children: ISequence> | undefined): void; + setChildren(element: T | null, children: Iterable> | undefined): void; resort(element?: T | null, recursive?: boolean): void; + updateElementHeight(element: T, height: number): void; } export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { @@ -41,7 +41,7 @@ export class ObjectTreeModel, TFilterData extends Non constructor( private user: string, - list: ISpliceable>, + list: IList>, options: IObjectTreeModelOptions = {} ) { this.model = new IndexTreeModel(user, list, null, options); @@ -62,7 +62,7 @@ export class ObjectTreeModel, TFilterData extends Non setChildren( element: T | null, - children: ISequence> | undefined, + children: Iterable> = Iterable.empty(), onDidCreateNode?: ITreeNodeCallback, onDidDeleteNode?: ITreeNodeCallback ): void { @@ -72,42 +72,54 @@ export class ObjectTreeModel, TFilterData extends Non private _setChildren( location: number[], - children: ISequence> | undefined, + children: Iterable> = Iterable.empty(), onDidCreateNode?: ITreeNodeCallback, onDidDeleteNode?: ITreeNodeCallback ): void { const insertedElements = new Set(); const insertedElementIds = new Set(); - const _onDidCreateNode = (node: ITreeNode) => { - insertedElements.add(node.element); - this.nodes.set(node.element, node); + const _onDidCreateNode = (node: ITreeNode) => { + if (node.element === null) { + return; + } + + const tnode = node as ITreeNode; + + insertedElements.add(tnode.element); + this.nodes.set(tnode.element, tnode); if (this.identityProvider) { - const id = this.identityProvider.getId(node.element).toString(); + const id = this.identityProvider.getId(tnode.element).toString(); insertedElementIds.add(id); - this.nodesByIdentity.set(id, node); + this.nodesByIdentity.set(id, tnode); } if (onDidCreateNode) { - onDidCreateNode(node); + onDidCreateNode(tnode); } }; - const _onDidDeleteNode = (node: ITreeNode) => { - if (!insertedElements.has(node.element)) { - this.nodes.delete(node.element); + const _onDidDeleteNode = (node: ITreeNode) => { + if (node.element === null) { + return; + } + + const tnode = node as ITreeNode; + + if (!insertedElements.has(tnode.element)) { + this.nodes.delete(tnode.element); } if (this.identityProvider) { - const id = this.identityProvider.getId(node.element).toString(); + const id = this.identityProvider.getId(tnode.element).toString(); if (!insertedElementIds.has(id)) { this.nodesByIdentity.delete(id); } } if (onDidDeleteNode) { - onDidDeleteNode(node); + onDidDeleteNode(tnode); } }; @@ -120,14 +132,12 @@ export class ObjectTreeModel, TFilterData extends Non ); } - private preserveCollapseState(elements: ISequence> | undefined): ISequence> { - let iterator = elements ? getSequenceIterator(elements) : Iterator.empty>(); - + private preserveCollapseState(elements: Iterable> = Iterable.empty()): Iterable> { if (this.sorter) { - iterator = Iterator.fromArray(mergeSort(Iterator.collect(iterator), this.sorter.compare.bind(this.sorter))); + elements = mergeSort([...elements], this.sorter.compare.bind(this.sorter)); } - return Iterator.map(iterator, treeElement => { + return Iterable.map(elements, treeElement => { let node = this.nodes.get(treeElement.element); if (!node && this.identityProvider) { @@ -159,6 +169,11 @@ export class ObjectTreeModel, TFilterData extends Non this.model.rerender(location); } + updateElementHeight(element: T, height: number): void { + const location = this.getElementLocation(element); + this.model.updateElementHeight(location, height); + } + resort(element: T | null = null, recursive = true): void { if (!this.sorter) { return; @@ -170,14 +185,14 @@ export class ObjectTreeModel, TFilterData extends Non this._setChildren(location, this.resortChildren(node, recursive)); } - private resortChildren(node: ITreeNode, recursive: boolean, first = true): ISequence> { - let childrenNodes = Iterator.fromArray(node.children as ITreeNode[]); + private resortChildren(node: ITreeNode, recursive: boolean, first = true): Iterable> { + let childrenNodes = [...node.children] as ITreeNode[]; if (recursive || first) { - childrenNodes = Iterator.fromArray(Iterator.collect(childrenNodes).sort(this.sorter!.compare.bind(this.sorter))); + childrenNodes = mergeSort(childrenNodes, this.sorter!.compare.bind(this.sorter)); } - return Iterator.map, ITreeElement>(childrenNodes, node => ({ + return Iterable.map, ITreeElement>(childrenNodes, node => ({ element: node.element as T, collapsible: node.collapsible, collapsed: node.collapsed, @@ -195,6 +210,10 @@ export class ObjectTreeModel, TFilterData extends Non return this.model.getLastElementAncestor(location); } + has(element: T | null): boolean { + return this.nodes.has(element); + } + getListIndex(element: T | null): number { const location = this.getElementLocation(element); return this.model.getListIndex(location); diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 374296fa573..085bb3d90b7 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { Iterator } from 'vs/base/common/iterator'; import { IListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IDragAndDropData } from 'vs/base/browser/dnd'; @@ -74,7 +73,7 @@ export interface ITreeSorter { export interface ITreeElement { readonly element: T; - readonly children?: Iterator> | ITreeElement[]; + readonly children?: Iterable>; readonly collapsible?: boolean; readonly collapsed?: boolean; } @@ -108,6 +107,8 @@ export interface ITreeModel { readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; + has(location: TRef): boolean; + getListIndex(location: TRef): number; getListRenderCount(location: TRef): number; getNode(location?: TRef): ITreeNode; @@ -165,12 +166,12 @@ export interface ITreeNavigator { export interface IDataSource { hasChildren?(element: TInput | T): boolean; - getChildren(element: TInput | T): T[]; + getChildren(element: TInput | T): Iterable; } export interface IAsyncDataSource { hasChildren(element: TInput | T): boolean; - getChildren(element: TInput | T): T[] | Promise; + getChildren(element: TInput | T): Iterable | Promise>; } export const enum TreeDragOverBubble { diff --git a/src/vs/base/browser/ui/tree/treeDefaults.ts b/src/vs/base/browser/ui/tree/treeDefaults.ts index e74f249c17d..834ab449fdd 100644 --- a/src/vs/base/browser/ui/tree/treeDefaults.ts +++ b/src/vs/base/browser/ui/tree/treeDefaults.ts @@ -10,16 +10,14 @@ import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; export class CollapseAllAction extends Action { constructor(private viewer: AsyncDataTree, enabled: boolean) { - super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled); + super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'collapse-all', enabled); } - public run(context?: any): Promise { + async run(): Promise { this.viewer.collapseAll(); this.viewer.setSelection([]); this.viewer.setFocus([]); this.viewer.domFocus(); this.viewer.focusFirst(); - - return Promise.resolve(); } } diff --git a/src/vs/base/browser/ui/tree/treeIcons.ts b/src/vs/base/browser/ui/tree/treeIcons.ts new file mode 100644 index 00000000000..bccd5188923 --- /dev/null +++ b/src/vs/base/browser/ui/tree/treeIcons.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + +export const treeItemExpandedIcon = registerIcon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation + +export const treeFilterOnTypeOnIcon = registerIcon('tree-filter-on-type-on', Codicon.listFilter); +export const treeFilterOnTypeOffIcon = registerIcon('tree-filter-on-type-off', Codicon.listSelection); +export const treeFilterClearIcon = registerIcon('tree-filter-clear', Codicon.close); + +export const treeItemLoadingIcon = registerIcon('tree-item-loading', Codicon.loading); diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 135008aa81d..cb4b12d5cc1 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; @@ -39,14 +40,18 @@ export interface IActionRunner extends IDisposable { } export interface IActionViewItem extends IDisposable { - readonly actionRunner: IActionRunner; + actionRunner: IActionRunner; setActionContext(context: any): void; render(element: any /* HTMLElement */): void; isEnabled(): boolean; - focus(): void; + focus(fromRight?: boolean): void; // TODO@isidorn what is this? blur(): void; } +export interface IActionViewItemProvider { + (action: IAction): IActionViewItem | undefined; +} + export interface IActionChangeEvent { readonly label?: string; readonly tooltip?: string; @@ -218,3 +223,32 @@ export class RadioGroup extends Disposable { } } } + +export class Separator extends Action { + + static readonly ID = 'vs.actions.separator'; + + constructor(label?: string) { + super(Separator.ID, label, label ? 'separator text' : 'separator'); + this.checked = false; + this.enabled = false; + } +} + +export class SubmenuAction extends Action { + + get actions(): IAction[] { + return this._actions; + } + + constructor(id: string, label: string, private _actions: IAction[], cssClass?: string) { + super(id, label, cssClass, true); + } +} + +export class EmptySubmenuAction extends Action { + static readonly ID = 'vs.actions.empty'; + constructor() { + super(EmptySubmenuAction.ID, nls.localize('submenu.empty', '(empty)'), undefined, false); + } +} diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index b44b19ddd0d..d8ce68b55e2 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -5,19 +5,16 @@ import { URI } from 'vs/base/common/uri'; +/** + * @deprecated use `FileAccess.asFileUri(relativePath, requireFn).fsPath` + */ export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { return getUriFromAmdModule(requirefn, relativePath).fsPath; } +/** + * @deprecated use `FileAccess.asFileUri()` for node.js contexts or `FileAccess.asBrowserUri` for browser contexts. + */ export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI { return URI.parse(requirefn.toUrl(relativePath)); } - -/** - * Reference a resource that might be inlined. - * Do not inline icons that will be used by the native mac touchbar. - * Do not rename this method unless you adopt the build scripts. - */ -export function registerAndGetAmdImageURL(absolutePath: string): string { - return require.toUrl(absolutePath); -} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 8ddd98a5a82..bc0aa40a685 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -87,6 +87,40 @@ export function findFirstInSorted(array: ReadonlyArray, p: (x: T) => boole type Compare = (a: T, b: T) => number; + +export function quickSelect(nth: number, data: T[], compare: Compare): T { + + nth = nth | 0; + + if (nth >= data.length) { + throw new TypeError('invalid index'); + } + + let pivotValue = data[Math.floor(data.length * Math.random())]; + let lower: T[] = []; + let higher: T[] = []; + let pivots: T[] = []; + + for (let value of data) { + const val = compare(value, pivotValue); + if (val < 0) { + lower.push(value); + } else if (val > 0) { + higher.push(value); + } else { + pivots.push(value); + } + } + + if (nth < lower.length) { + return quickSelect(nth, lower, compare); + } else if (nth < lower.length + pivots.length) { + return pivots[0]; + } else { + return quickSelect(nth - (lower.length + pivots.length), higher, compare); + } +} + /** * Like `Array#sort` but always stable. Usually runs a little slower `than Array#sort` * so only use this when actually needing stable sort. @@ -372,12 +406,6 @@ export function distinctES6(array: ReadonlyArray): T[] { }); } -export function fromSet(set: Set): T[] { - const result: T[] = []; - set.forEach(o => result.push(o)); - return result; -} - export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { const seen: { [key: string]: boolean; } = Object.create(null); @@ -405,25 +433,6 @@ export function lastIndex(array: ReadonlyArray, fn: (item: T) => boolean): return -1; } -export function firstIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { - for (let i = 0; i < array.length; i++) { - const element = array[i]; - - if (fn(element)) { - return i; - } - } - - return -1; -} - -export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; -export function first(array: ReadonlyArray, fn: (item: T) => boolean): T | undefined; -export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | undefined = undefined): T | undefined { - const index = firstIndex(array, fn); - return index < 0 ? notFoundValue : array[index]; -} - export function firstOrDefault(array: ReadonlyArray, notFoundValue: NotFound): T | NotFound; export function firstOrDefault(array: ReadonlyArray): T | undefined; export function firstOrDefault(array: ReadonlyArray, notFoundValue?: NotFound): T | NotFound | undefined { @@ -471,20 +480,11 @@ export function range(arg: number, to?: number): number[] { return result; } -export function fill(num: number, value: T, arr: T[] = []): T[] { - for (let i = 0; i < num; i++) { - arr[i] = value; - } - - return arr; -} - export function index(array: ReadonlyArray, indexer: (t: T) => string): { [key: string]: T; }; -export function index(array: ReadonlyArray, indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; }; -export function index(array: ReadonlyArray, indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } { +export function index(array: ReadonlyArray, indexer: (t: T) => string, mapper: (t: T) => R): { [key: string]: R; }; +export function index(array: ReadonlyArray, indexer: (t: T) => string, mapper?: (t: T) => R): { [key: string]: R; } { return array.reduce((r, t) => { - const key = indexer(t); - r[key] = merger(t, r[key]); + r[indexer(t)] = mapper ? mapper(t) : t; return r; }, Object.create(null)); } @@ -496,12 +496,21 @@ export function index(array: ReadonlyArray, indexer: (t: T) => string, export function insert(array: T[], element: T): () => void { array.push(element); - return () => { - const index = array.indexOf(element); - if (index > -1) { - array.splice(index, 1); - } - }; + return () => remove(array, element); +} + +/** + * Removes an element from an array if it can be found. + */ +export function remove(array: T[], element: T): T | undefined { + const index = array.indexOf(element); + if (index > -1) { + array.splice(index, 1); + + return element; + } + + return undefined; } /** @@ -564,23 +573,18 @@ export function pushToEnd(arr: T[], value: T): void { } } -export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { - for (let i = 0; i < arr.length; i++) { - const element = arr[i]; - if (predicate(element, i, arr)) { - return element; - } - } - - return undefined; -} - export function mapArrayOrNot(items: T | T[], fn: (_: T) => U): U | U[] { return Array.isArray(items) ? items.map(fn) : fn(items); } +export function asArray(x: T | T[]): T[]; +export function asArray(x: T | readonly T[]): readonly T[]; export function asArray(x: T | T[]): T[] { return Array.isArray(x) ? x : [x]; } + +export function getRandomElement(arr: T[]): T | undefined { + return arr[Math.floor(Math.random() * arr.length)]; +} diff --git a/src/vs/base/common/assert.ts b/src/vs/base/common/assert.ts index 1e227df640e..9e2510b4d0b 100644 --- a/src/vs/base/common/assert.ts +++ b/src/vs/base/common/assert.ts @@ -6,8 +6,8 @@ /** * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value. */ -export function ok(value?: any, message?: string) { +export function ok(value?: unknown, message?: string) { if (!value) { - throw new Error(message ? 'Assertion failed (' + message + ')' : 'Assertion Failed'); + throw new Error(message ? `Assertion failed (${message})` : 'Assertion Failed'); } } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 81c29e6731b..632c1989fca 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -52,8 +52,37 @@ export function createCancelablePromise(callback: (token: CancellationToken) export function raceCancellation(promise: Promise, token: CancellationToken): Promise; export function raceCancellation(promise: Promise, token: CancellationToken, defaultValue: T): Promise; -export function raceCancellation(promise: Promise, token: CancellationToken, defaultValue?: T): Promise { - return Promise.race([promise, new Promise(resolve => token.onCancellationRequested(() => resolve(defaultValue)))]); +export function raceCancellation(promise: Promise, token: CancellationToken, defaultValue?: T): Promise { + return Promise.race([promise, new Promise(resolve => token.onCancellationRequested(() => resolve(defaultValue)))]); +} + +/** + * Returns as soon as one of the promises is resolved and cancels remaining promises + */ +export async function raceCancellablePromises(cancellablePromises: CancelablePromise[]): Promise { + let resolvedPromiseIndex = -1; + const promises = cancellablePromises.map((promise, index) => promise.then(result => { resolvedPromiseIndex = index; return result; })); + const result = await Promise.race(promises); + cancellablePromises.forEach((cancellablePromise, index) => { + if (index !== resolvedPromiseIndex) { + cancellablePromise.cancel(); + } + }); + return result; +} + +export function raceTimeout(promise: Promise, timeout: number, onTimeout?: () => void): Promise { + let promiseResolve: ((value: T | undefined) => void) | undefined = undefined; + + const timer = setTimeout(() => { + promiseResolve?.(undefined); + onTimeout?.(); + }, timeout); + + return Promise.race([ + promise.finally(() => clearTimeout(timer)), + new Promise(resolve => promiseResolve = resolve) + ]); } export function asPromise(callback: () => T | Thenable): Promise { @@ -135,13 +164,13 @@ export class Throttler { this.activePromise = promiseFactory(); - return new Promise((c, e) => { + return new Promise((resolve, reject) => { this.activePromise!.then((result: any) => { this.activePromise = null; - c(result); + resolve(result); }, (err: any) => { this.activePromise = null; - e(err); + reject(err); }); }); } @@ -156,6 +185,25 @@ export class Sequencer { } } +export class SequencerByKey { + + private promiseMap = new Map>(); + + queue(key: TKey, promiseTask: ITask>): Promise { + const runningPromise = this.promiseMap.get(key) ?? Promise.resolve(); + const newPromise = runningPromise + .catch(() => { }) + .then(promiseTask) + .finally(() => { + if (this.promiseMap.get(key) === newPromise) { + this.promiseMap.delete(key); + } + }); + this.promiseMap.set(key, newPromise); + return newPromise; + } +} + /** * A helper to delay execution of a task that is being requested often. * @@ -399,7 +447,7 @@ export function first(promiseFactories: ITask>[], shouldStop: (t: interface ILimitedTaskFactory { factory: ITask>; - c: (value?: T | Promise) => void; + c: (value: T | Promise) => void; e: (error?: any) => void; } @@ -585,10 +633,10 @@ export class RunOnceScheduler { private timeout: number; private timeoutHandler: () => void; - constructor(runner: (...args: any[]) => void, timeout: number) { + constructor(runner: (...args: any[]) => void, delay: number) { this.timeoutToken = -1; this.runner = runner; - this.timeout = timeout; + this.timeout = delay; this.timeoutHandler = this.onTimeout.bind(this); } @@ -618,6 +666,14 @@ export class RunOnceScheduler { this.timeoutToken = setTimeout(this.timeoutHandler, delay); } + get delay(): number { + return this.timeout; + } + + set delay(value: number) { + this.timeout = value; + } + /** * Returns true if scheduled. */ @@ -750,7 +806,7 @@ export class IdleValue { this._handle.dispose(); } - getValue(): T { + get value(): T { if (!this._didRun) { this._handle.dispose(); this._executor(); @@ -779,3 +835,142 @@ export async function retry(task: ITask>, delay: number, retries: throw lastError; } + +//#region Task Sequentializer + +interface IPendingTask { + taskId: number; + cancel: () => void; + promise: Promise; +} + +interface ISequentialTask { + promise: Promise; + promiseResolve: () => void; + promiseReject: (error: Error) => void; + run: () => Promise; +} + +export interface ITaskSequentializerWithPendingTask { + readonly pending: Promise; +} + +export class TaskSequentializer { + private _pending?: IPendingTask; + private _next?: ISequentialTask; + + hasPending(taskId?: number): this is ITaskSequentializerWithPendingTask { + if (!this._pending) { + return false; + } + + if (typeof taskId === 'number') { + return this._pending.taskId === taskId; + } + + return !!this._pending; + } + + get pending(): Promise | undefined { + return this._pending ? this._pending.promise : undefined; + } + + cancelPending(): void { + this._pending?.cancel(); + } + + setPending(taskId: number, promise: Promise, onCancel?: () => void,): Promise { + this._pending = { taskId: taskId, cancel: () => onCancel?.(), promise }; + + promise.then(() => this.donePending(taskId), () => this.donePending(taskId)); + + return promise; + } + + private donePending(taskId: number): void { + if (this._pending && taskId === this._pending.taskId) { + + // only set pending to done if the promise finished that is associated with that taskId + this._pending = undefined; + + // schedule the next task now that we are free if we have any + this.triggerNext(); + } + } + + private triggerNext(): void { + if (this._next) { + const next = this._next; + this._next = undefined; + + // Run next task and complete on the associated promise + next.run().then(next.promiseResolve, next.promiseReject); + } + } + + setNext(run: () => Promise): Promise { + + // this is our first next task, so we create associated promise with it + // so that we can return a promise that completes when the task has + // completed. + if (!this._next) { + let promiseResolve: () => void; + let promiseReject: (error: Error) => void; + const promise = new Promise((resolve, reject) => { + promiseResolve = resolve; + promiseReject = reject; + }); + + this._next = { + run, + promise, + promiseResolve: promiseResolve!, + promiseReject: promiseReject! + }; + } + + // we have a previous next task, just overwrite it + else { + this._next.run = run; + } + + return this._next.promise; + } +} + +//#endregion + +//#region + +/** + * The `IntervalCounter` allows to count the number + * of calls to `increment()` over a duration of + * `interval`. This utility can be used to conditionally + * throttle a frequent task when a certain threshold + * is reached. + */ +export class IntervalCounter { + + private lastIncrementTime = 0; + + private value = 0; + + constructor(private readonly interval: number) { } + + increment(): number { + const now = Date.now(); + + // We are outside of the range of `interval` and as such + // start counting from 0 and remember the time + if (now - this.lastIncrementTime > this.interval) { + this.lastIncrementTime = now; + this.value = 0; + } + + this.value++; + + return this.value; + } +} + +//#endregion diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index 1126f89c7d8..6c6285e0c3b 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -94,8 +94,14 @@ export class VSBuffer { return new VSBuffer(this.buffer.subarray(start!/*bad lib.d.ts*/, end)); } - set(array: VSBuffer, offset?: number): void { - this.buffer.set(array.buffer, offset); + set(array: VSBuffer, offset?: number): void; + set(array: Uint8Array, offset?: number): void; + set(array: VSBuffer | Uint8Array, offset?: number): void { + if (array instanceof VSBuffer) { + this.buffer.set(array.buffer, offset); + } else { + this.buffer.set(array, offset); + } } readUInt32BE(offset: number): number { @@ -106,6 +112,14 @@ export class VSBuffer { writeUInt32BE(this.buffer, value, offset); } + readUInt32LE(offset: number): number { + return readUInt32LE(this.buffer, offset); + } + + writeUInt32LE(value: number, offset: number): void { + writeUInt32LE(this.buffer, value, offset); + } + readUInt8(offset: number): number { return readUInt8(this.buffer, offset); } @@ -115,6 +129,19 @@ export class VSBuffer { } } +export function readUInt16LE(source: Uint8Array, offset: number): number { + return ( + ((source[offset + 0] << 0) >>> 0) | + ((source[offset + 1] << 8) >>> 0) + ); +} + +export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void { + destination[offset + 0] = (value & 0b11111111); + value = value >>> 8; + destination[offset + 1] = (value & 0b11111111); +} + export function readUInt32BE(source: Uint8Array, offset: number): number { return ( source[offset] * 2 ** 24 @@ -134,11 +161,30 @@ export function writeUInt32BE(destination: Uint8Array, value: number, offset: nu destination[offset] = value; } -function readUInt8(source: Uint8Array, offset: number): number { +export function readUInt32LE(source: Uint8Array, offset: number): number { + return ( + ((source[offset + 0] << 0) >>> 0) | + ((source[offset + 1] << 8) >>> 0) | + ((source[offset + 2] << 16) >>> 0) | + ((source[offset + 3] << 24) >>> 0) + ); +} + +export function writeUInt32LE(destination: Uint8Array, value: number, offset: number): void { + destination[offset + 0] = (value & 0b11111111); + value = value >>> 8; + destination[offset + 1] = (value & 0b11111111); + value = value >>> 8; + destination[offset + 2] = (value & 0b11111111); + value = value >>> 8; + destination[offset + 3] = (value & 0b11111111); +} + +export function readUInt8(source: Uint8Array, offset: number): number { return source[offset]; } -function writeUInt8(destination: Uint8Array, value: number, offset: number): void { +export function writeUInt8(destination: Uint8Array, value: number, offset: number): void { destination[offset] = value; } @@ -148,6 +194,8 @@ export interface VSBufferReadableStream extends streams.ReadableStream export interface VSBufferWriteableStream extends streams.WriteableStream { } +export interface VSBufferReadableBufferedStream extends streams.ReadableBufferedStream { } + export function readableToBuffer(readable: VSBufferReadable): VSBuffer { return streams.consumeReadable(readable, chunks => VSBuffer.concat(chunks)); } @@ -160,6 +208,21 @@ export function streamToBuffer(stream: streams.ReadableStream): Promis return streams.consumeStream(stream, chunks => VSBuffer.concat(chunks)); } +export async function bufferedStreamToBuffer(bufferedStream: streams.ReadableBufferedStream): Promise { + if (bufferedStream.ended) { + return VSBuffer.concat(bufferedStream.buffer); + } + + return VSBuffer.concat([ + + // Include already read chunks... + ...bufferedStream.buffer, + + // ...and all additional chunks + await streamToBuffer(bufferedStream.stream) + ]); +} + export function bufferToStream(buffer: VSBuffer): streams.ReadableStream { return streams.toStream(buffer, chunks => VSBuffer.concat(chunks)); } @@ -168,6 +231,6 @@ export function streamToBufferReadableStream(stream: streams.ReadableStreamEvent return streams.transform(stream, { data: data => typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data) }, chunks => VSBuffer.concat(chunks)); } -export function newWriteableBufferStream(): streams.WriteableStream { - return streams.newWriteableStream(chunks => VSBuffer.concat(chunks)); +export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream { + return streams.newWriteableStream(chunks => VSBuffer.concat(chunks), options); } diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts index f741f6b7af1..cb8c8c6da2d 100644 --- a/src/vs/base/common/cancellation.ts +++ b/src/vs/base/common/cancellation.ts @@ -7,22 +7,31 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; export interface CancellationToken { - readonly isCancellationRequested: boolean; + /** - * An event emitted when cancellation is requested + * A flag signalling is cancellation has been requested. + */ + readonly isCancellationRequested: boolean; + + /** + * An event which fires when cancellation is requested. This event + * only ever fires `once` as cancellation can only happen once. Listeners + * that are registered after cancellation will be called (next event loop run), + * but also only once. + * * @event */ - readonly onCancellationRequested: Event; + readonly onCancellationRequested: (listener: (e: any) => any, thisArgs?: any, disposables?: IDisposable[]) => IDisposable; } -const shortcutEvent = Object.freeze(function (callback, context?): IDisposable { +const shortcutEvent: Event = Object.freeze(function (callback, context?): IDisposable { const handle = setTimeout(callback.bind(context), 0); return { dispose() { clearTimeout(handle); } }; -} as Event); +}); export namespace CancellationToken { - export function isCancellationToken(thing: any): thing is CancellationToken { + export function isCancellationToken(thing: unknown): thing is CancellationToken { if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) { return true; } diff --git a/src/vs/base/common/charCode.ts b/src/vs/base/common/charCode.ts index 3b8c6426710..23b840b2942 100644 --- a/src/vs/base/common/charCode.ts +++ b/src/vs/base/common/charCode.ts @@ -342,7 +342,17 @@ export const enum CharCode { * Unicode Character 'LINE SEPARATOR' (U+2028) * http://www.fileformat.info/info/unicode/char/2028/index.htm */ - LINE_SEPARATOR_2028 = 8232, + LINE_SEPARATOR = 0x2028, + /** + * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029) + * http://www.fileformat.info/info/unicode/char/2029/index.htm + */ + PARAGRAPH_SEPARATOR = 0x2029, + /** + * Unicode Character 'NEXT LINE' (U+0085) + * http://www.fileformat.info/info/unicode/char/0085/index.htm + */ + NEXT_LINE = 0x0085, // http://www.fileformat.info/info/unicode/category/Sk/list.htm U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX @@ -422,4 +432,4 @@ export const enum CharCode { * http://www.fileformat.info/info/unicode/char/feff/index.htm */ UTF8_BOM = 65279 -} \ No newline at end of file +} diff --git a/src/vs/base/common/codicon.ts b/src/vs/base/common/codicon.ts index 51189279780..5b3541125e0 100644 --- a/src/vs/base/common/codicon.ts +++ b/src/vs/base/common/codicon.ts @@ -6,7 +6,7 @@ import { matchesFuzzy, IMatch } from 'vs/base/common/filters'; import { ltrim } from 'vs/base/common/strings'; -const codiconStartMarker = '$('; +export const codiconStartMarker = '$('; export interface IParsedCodicons { readonly text: string; diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 00b2f8b1c17..3c4fffd3b5c 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -3,6 +3,492 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { codiconStartMarker } from 'vs/base/common/codicon'; +import { Emitter, Event } from 'vs/base/common/event'; + +export interface IIconRegistry { + readonly all: IterableIterator; + readonly onDidRegister: Event; + get(id: string): Codicon | undefined; +} + +class Registry implements IIconRegistry { + + private readonly _icons = new Map(); + private readonly _onDidRegister = new Emitter(); + + public add(icon: Codicon) { + if (!this._icons.has(icon.id)) { + this._icons.set(icon.id, icon); + this._onDidRegister.fire(icon); + } else { + console.error(`Duplicate registration of codicon ${icon.id}`); + } + } + + public get(id: string): Codicon | undefined { + return this._icons.get(id); + } + + public get all(): IterableIterator { + return this._icons.values(); + } + + public get onDidRegister(): Event { + return this._onDidRegister.event; + } +} + +const _registry = new Registry(); + +export const iconRegistry: IIconRegistry = _registry; + +export function registerIcon(id: string, def: Codicon, description?: string) { + return new Codicon(id, def); +} + +export class Codicon { + constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition, public description?: string) { + _registry.add(this); + } + public get classNames() { return 'codicon codicon-' + this.id; } + // classNamesArray is useful for migrating to ES6 classlist + public get classNamesArray() { return ['codicon', 'codicon-' + this.id]; } + public get cssSelector() { return '.codicon.codicon-' + this.id; } +} + +interface IconDefinition { + character: string; +} + +export namespace Codicon { + + // built-in icons, with image name + export const add = new Codicon('add', { character: '\\ea60' }); + export const plus = new Codicon('plus', { character: '\\ea60' }); + export const gistNew = new Codicon('gist-new', { character: '\\ea60' }); + export const repoCreate = new Codicon('repo-create', { character: '\\ea60' }); + export const lightbulb = new Codicon('lightbulb', { character: '\\ea61' }); + export const lightBulb = new Codicon('light-bulb', { character: '\\ea61' }); + export const repo = new Codicon('repo', { character: '\\ea62' }); + export const repoDelete = new Codicon('repo-delete', { character: '\\ea62' }); + export const gistFork = new Codicon('gist-fork', { character: '\\ea63' }); + export const repoForked = new Codicon('repo-forked', { character: '\\ea63' }); + export const gitPullRequest = new Codicon('git-pull-request', { character: '\\ea64' }); + export const gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { character: '\\ea64' }); + export const recordKeys = new Codicon('record-keys', { character: '\\ea65' }); + export const keyboard = new Codicon('keyboard', { character: '\\ea65' }); + export const tag = new Codicon('tag', { character: '\\ea66' }); + export const tagAdd = new Codicon('tag-add', { character: '\\ea66' }); + export const tagRemove = new Codicon('tag-remove', { character: '\\ea66' }); + export const person = new Codicon('person', { character: '\\ea67' }); + export const personAdd = new Codicon('person-add', { character: '\\ea67' }); + export const personFollow = new Codicon('person-follow', { character: '\\ea67' }); + export const personOutline = new Codicon('person-outline', { character: '\\ea67' }); + export const personFilled = new Codicon('person-filled', { character: '\\ea67' }); + export const gitBranch = new Codicon('git-branch', { character: '\\ea68' }); + export const gitBranchCreate = new Codicon('git-branch-create', { character: '\\ea68' }); + export const gitBranchDelete = new Codicon('git-branch-delete', { character: '\\ea68' }); + export const sourceControl = new Codicon('source-control', { character: '\\ea68' }); + export const mirror = new Codicon('mirror', { character: '\\ea69' }); + export const mirrorPublic = new Codicon('mirror-public', { character: '\\ea69' }); + export const star = new Codicon('star', { character: '\\ea6a' }); + export const starAdd = new Codicon('star-add', { character: '\\ea6a' }); + export const starDelete = new Codicon('star-delete', { character: '\\ea6a' }); + export const starEmpty = new Codicon('star-empty', { character: '\\ea6a' }); + export const comment = new Codicon('comment', { character: '\\ea6b' }); + export const commentAdd = new Codicon('comment-add', { character: '\\ea6b' }); + export const alert = new Codicon('alert', { character: '\\ea6c' }); + export const warning = new Codicon('warning', { character: '\\ea6c' }); + export const search = new Codicon('search', { character: '\\ea6d' }); + export const searchSave = new Codicon('search-save', { character: '\\ea6d' }); + export const logOut = new Codicon('log-out', { character: '\\ea6e' }); + export const signOut = new Codicon('sign-out', { character: '\\ea6e' }); + export const logIn = new Codicon('log-in', { character: '\\ea6f' }); + export const signIn = new Codicon('sign-in', { character: '\\ea6f' }); + export const eye = new Codicon('eye', { character: '\\ea70' }); + export const eyeUnwatch = new Codicon('eye-unwatch', { character: '\\ea70' }); + export const eyeWatch = new Codicon('eye-watch', { character: '\\ea70' }); + export const circleFilled = new Codicon('circle-filled', { character: '\\ea71' }); + export const primitiveDot = new Codicon('primitive-dot', { character: '\\ea71' }); + export const closeDirty = new Codicon('close-dirty', { character: '\\ea71' }); + export const debugBreakpoint = new Codicon('debug-breakpoint', { character: '\\ea71' }); + export const debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { character: '\\ea71' }); + export const debugHint = new Codicon('debug-hint', { character: '\\ea71' }); + export const primitiveSquare = new Codicon('primitive-square', { character: '\\ea72' }); + export const edit = new Codicon('edit', { character: '\\ea73' }); + export const pencil = new Codicon('pencil', { character: '\\ea73' }); + export const info = new Codicon('info', { character: '\\ea74' }); + export const issueOpened = new Codicon('issue-opened', { character: '\\ea74' }); + export const gistPrivate = new Codicon('gist-private', { character: '\\ea75' }); + export const gitForkPrivate = new Codicon('git-fork-private', { character: '\\ea75' }); + export const lock = new Codicon('lock', { character: '\\ea75' }); + export const mirrorPrivate = new Codicon('mirror-private', { character: '\\ea75' }); + export const close = new Codicon('close', { character: '\\ea76' }); + export const removeClose = new Codicon('remove-close', { character: '\\ea76' }); + export const x = new Codicon('x', { character: '\\ea76' }); + export const repoSync = new Codicon('repo-sync', { character: '\\ea77' }); + export const sync = new Codicon('sync', { character: '\\ea77' }); + export const clone = new Codicon('clone', { character: '\\ea78' }); + export const desktopDownload = new Codicon('desktop-download', { character: '\\ea78' }); + export const beaker = new Codicon('beaker', { character: '\\ea79' }); + export const microscope = new Codicon('microscope', { character: '\\ea79' }); + export const vm = new Codicon('vm', { character: '\\ea7a' }); + export const deviceDesktop = new Codicon('device-desktop', { character: '\\ea7a' }); + export const file = new Codicon('file', { character: '\\ea7b' }); + export const fileText = new Codicon('file-text', { character: '\\ea7b' }); + export const more = new Codicon('more', { character: '\\ea7c' }); + export const ellipsis = new Codicon('ellipsis', { character: '\\ea7c' }); + export const kebabHorizontal = new Codicon('kebab-horizontal', { character: '\\ea7c' }); + export const mailReply = new Codicon('mail-reply', { character: '\\ea7d' }); + export const reply = new Codicon('reply', { character: '\\ea7d' }); + export const organization = new Codicon('organization', { character: '\\ea7e' }); + export const organizationFilled = new Codicon('organization-filled', { character: '\\ea7e' }); + export const organizationOutline = new Codicon('organization-outline', { character: '\\ea7e' }); + export const newFile = new Codicon('new-file', { character: '\\ea7f' }); + export const fileAdd = new Codicon('file-add', { character: '\\ea7f' }); + export const newFolder = new Codicon('new-folder', { character: '\\ea80' }); + export const fileDirectoryCreate = new Codicon('file-directory-create', { character: '\\ea80' }); + export const trash = new Codicon('trash', { character: '\\ea81' }); + export const trashcan = new Codicon('trashcan', { character: '\\ea81' }); + export const history = new Codicon('history', { character: '\\ea82' }); + export const clock = new Codicon('clock', { character: '\\ea82' }); + export const folder = new Codicon('folder', { character: '\\ea83' }); + export const fileDirectory = new Codicon('file-directory', { character: '\\ea83' }); + export const symbolFolder = new Codicon('symbol-folder', { character: '\\ea83' }); + export const logoGithub = new Codicon('logo-github', { character: '\\ea84' }); + export const markGithub = new Codicon('mark-github', { character: '\\ea84' }); + export const github = new Codicon('github', { character: '\\ea84' }); + export const terminal = new Codicon('terminal', { character: '\\ea85' }); + export const console = new Codicon('console', { character: '\\ea85' }); + export const repl = new Codicon('repl', { character: '\\ea85' }); + export const zap = new Codicon('zap', { character: '\\ea86' }); + export const symbolEvent = new Codicon('symbol-event', { character: '\\ea86' }); + export const error = new Codicon('error', { character: '\\ea87' }); + export const stop = new Codicon('stop', { character: '\\ea87' }); + export const variable = new Codicon('variable', { character: '\\ea88' }); + export const symbolVariable = new Codicon('symbol-variable', { character: '\\ea88' }); + export const array = new Codicon('array', { character: '\\ea8a' }); + export const symbolArray = new Codicon('symbol-array', { character: '\\ea8a' }); + export const symbolModule = new Codicon('symbol-module', { character: '\\ea8b' }); + export const symbolPackage = new Codicon('symbol-package', { character: '\\ea8b' }); + export const symbolNamespace = new Codicon('symbol-namespace', { character: '\\ea8b' }); + export const symbolObject = new Codicon('symbol-object', { character: '\\ea8b' }); + export const symbolMethod = new Codicon('symbol-method', { character: '\\ea8c' }); + export const symbolFunction = new Codicon('symbol-function', { character: '\\ea8c' }); + export const symbolConstructor = new Codicon('symbol-constructor', { character: '\\ea8c' }); + export const symbolBoolean = new Codicon('symbol-boolean', { character: '\\ea8f' }); + export const symbolNull = new Codicon('symbol-null', { character: '\\ea8f' }); + export const symbolNumeric = new Codicon('symbol-numeric', { character: '\\ea90' }); + export const symbolNumber = new Codicon('symbol-number', { character: '\\ea90' }); + export const symbolStructure = new Codicon('symbol-structure', { character: '\\ea91' }); + export const symbolStruct = new Codicon('symbol-struct', { character: '\\ea91' }); + export const symbolParameter = new Codicon('symbol-parameter', { character: '\\ea92' }); + export const symbolTypeParameter = new Codicon('symbol-type-parameter', { character: '\\ea92' }); + export const symbolKey = new Codicon('symbol-key', { character: '\\ea93' }); + export const symbolText = new Codicon('symbol-text', { character: '\\ea93' }); + export const symbolReference = new Codicon('symbol-reference', { character: '\\ea94' }); + export const goToFile = new Codicon('go-to-file', { character: '\\ea94' }); + export const symbolEnum = new Codicon('symbol-enum', { character: '\\ea95' }); + export const symbolValue = new Codicon('symbol-value', { character: '\\ea95' }); + export const symbolRuler = new Codicon('symbol-ruler', { character: '\\ea96' }); + export const symbolUnit = new Codicon('symbol-unit', { character: '\\ea96' }); + export const activateBreakpoints = new Codicon('activate-breakpoints', { character: '\\ea97' }); + export const archive = new Codicon('archive', { character: '\\ea98' }); + export const arrowBoth = new Codicon('arrow-both', { character: '\\ea99' }); + export const arrowDown = new Codicon('arrow-down', { character: '\\ea9a' }); + export const arrowLeft = new Codicon('arrow-left', { character: '\\ea9b' }); + export const arrowRight = new Codicon('arrow-right', { character: '\\ea9c' }); + export const arrowSmallDown = new Codicon('arrow-small-down', { character: '\\ea9d' }); + export const arrowSmallLeft = new Codicon('arrow-small-left', { character: '\\ea9e' }); + export const arrowSmallRight = new Codicon('arrow-small-right', { character: '\\ea9f' }); + export const arrowSmallUp = new Codicon('arrow-small-up', { character: '\\eaa0' }); + export const arrowUp = new Codicon('arrow-up', { character: '\\eaa1' }); + export const bell = new Codicon('bell', { character: '\\eaa2' }); + export const bold = new Codicon('bold', { character: '\\eaa3' }); + export const book = new Codicon('book', { character: '\\eaa4' }); + export const bookmark = new Codicon('bookmark', { character: '\\eaa5' }); + export const debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { character: '\\eaa6' }); + export const debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { character: '\\eaa7' }); + export const debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { character: '\\eaa7' }); + export const debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { character: '\\eaa8' }); + export const debugBreakpointData = new Codicon('debug-breakpoint-data', { character: '\\eaa9' }); + export const debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { character: '\\eaa9' }); + export const debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { character: '\\eaaa' }); + export const debugBreakpointLog = new Codicon('debug-breakpoint-log', { character: '\\eaab' }); + export const debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { character: '\\eaab' }); + export const briefcase = new Codicon('briefcase', { character: '\\eaac' }); + export const broadcast = new Codicon('broadcast', { character: '\\eaad' }); + export const browser = new Codicon('browser', { character: '\\eaae' }); + export const bug = new Codicon('bug', { character: '\\eaaf' }); + export const calendar = new Codicon('calendar', { character: '\\eab0' }); + export const caseSensitive = new Codicon('case-sensitive', { character: '\\eab1' }); + export const check = new Codicon('check', { character: '\\eab2' }); + export const checklist = new Codicon('checklist', { character: '\\eab3' }); + export const chevronDown = new Codicon('chevron-down', { character: '\\eab4' }); + export const chevronLeft = new Codicon('chevron-left', { character: '\\eab5' }); + export const chevronRight = new Codicon('chevron-right', { character: '\\eab6' }); + export const chevronUp = new Codicon('chevron-up', { character: '\\eab7' }); + export const chromeClose = new Codicon('chrome-close', { character: '\\eab8' }); + export const chromeMaximize = new Codicon('chrome-maximize', { character: '\\eab9' }); + export const chromeMinimize = new Codicon('chrome-minimize', { character: '\\eaba' }); + export const chromeRestore = new Codicon('chrome-restore', { character: '\\eabb' }); + export const circleOutline = new Codicon('circle-outline', { character: '\\eabc' }); + export const debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { character: '\\eabc' }); + export const circleSlash = new Codicon('circle-slash', { character: '\\eabd' }); + export const circuitBoard = new Codicon('circuit-board', { character: '\\eabe' }); + export const clearAll = new Codicon('clear-all', { character: '\\eabf' }); + export const clippy = new Codicon('clippy', { character: '\\eac0' }); + export const closeAll = new Codicon('close-all', { character: '\\eac1' }); + export const cloudDownload = new Codicon('cloud-download', { character: '\\eac2' }); + export const cloudUpload = new Codicon('cloud-upload', { character: '\\eac3' }); + export const code = new Codicon('code', { character: '\\eac4' }); + export const collapseAll = new Codicon('collapse-all', { character: '\\eac5' }); + export const colorMode = new Codicon('color-mode', { character: '\\eac6' }); + export const commentDiscussion = new Codicon('comment-discussion', { character: '\\eac7' }); + export const compareChanges = new Codicon('compare-changes', { character: '\\eafd' }); + export const creditCard = new Codicon('credit-card', { character: '\\eac9' }); + export const dash = new Codicon('dash', { character: '\\eacc' }); + export const dashboard = new Codicon('dashboard', { character: '\\eacd' }); + export const database = new Codicon('database', { character: '\\eace' }); + export const debugContinue = new Codicon('debug-continue', { character: '\\eacf' }); + export const debugDisconnect = new Codicon('debug-disconnect', { character: '\\ead0' }); + export const debugPause = new Codicon('debug-pause', { character: '\\ead1' }); + export const debugRestart = new Codicon('debug-restart', { character: '\\ead2' }); + export const debugStart = new Codicon('debug-start', { character: '\\ead3' }); + export const debugStepInto = new Codicon('debug-step-into', { character: '\\ead4' }); + export const debugStepOut = new Codicon('debug-step-out', { character: '\\ead5' }); + export const debugStepOver = new Codicon('debug-step-over', { character: '\\ead6' }); + export const debugStop = new Codicon('debug-stop', { character: '\\ead7' }); + export const debug = new Codicon('debug', { character: '\\ead8' }); + export const deviceCameraVideo = new Codicon('device-camera-video', { character: '\\ead9' }); + export const deviceCamera = new Codicon('device-camera', { character: '\\eada' }); + export const deviceMobile = new Codicon('device-mobile', { character: '\\eadb' }); + export const diffAdded = new Codicon('diff-added', { character: '\\eadc' }); + export const diffIgnored = new Codicon('diff-ignored', { character: '\\eadd' }); + export const diffModified = new Codicon('diff-modified', { character: '\\eade' }); + export const diffRemoved = new Codicon('diff-removed', { character: '\\eadf' }); + export const diffRenamed = new Codicon('diff-renamed', { character: '\\eae0' }); + export const diff = new Codicon('diff', { character: '\\eae1' }); + export const discard = new Codicon('discard', { character: '\\eae2' }); + export const editorLayout = new Codicon('editor-layout', { character: '\\eae3' }); + export const emptyWindow = new Codicon('empty-window', { character: '\\eae4' }); + export const exclude = new Codicon('exclude', { character: '\\eae5' }); + export const extensions = new Codicon('extensions', { character: '\\eae6' }); + export const eyeClosed = new Codicon('eye-closed', { character: '\\eae7' }); + export const fileBinary = new Codicon('file-binary', { character: '\\eae8' }); + export const fileCode = new Codicon('file-code', { character: '\\eae9' }); + export const fileMedia = new Codicon('file-media', { character: '\\eaea' }); + export const filePdf = new Codicon('file-pdf', { character: '\\eaeb' }); + export const fileSubmodule = new Codicon('file-submodule', { character: '\\eaec' }); + export const fileSymlinkDirectory = new Codicon('file-symlink-directory', { character: '\\eaed' }); + export const fileSymlinkFile = new Codicon('file-symlink-file', { character: '\\eaee' }); + export const fileZip = new Codicon('file-zip', { character: '\\eaef' }); + export const files = new Codicon('files', { character: '\\eaf0' }); + export const filter = new Codicon('filter', { character: '\\eaf1' }); + export const flame = new Codicon('flame', { character: '\\eaf2' }); + export const foldDown = new Codicon('fold-down', { character: '\\eaf3' }); + export const foldUp = new Codicon('fold-up', { character: '\\eaf4' }); + export const fold = new Codicon('fold', { character: '\\eaf5' }); + export const folderActive = new Codicon('folder-active', { character: '\\eaf6' }); + export const folderOpened = new Codicon('folder-opened', { character: '\\eaf7' }); + export const gear = new Codicon('gear', { character: '\\eaf8' }); + export const gift = new Codicon('gift', { character: '\\eaf9' }); + export const gistSecret = new Codicon('gist-secret', { character: '\\eafa' }); + export const gist = new Codicon('gist', { character: '\\eafb' }); + export const gitCommit = new Codicon('git-commit', { character: '\\eafc' }); + export const gitCompare = new Codicon('git-compare', { character: '\\eafd' }); + export const gitMerge = new Codicon('git-merge', { character: '\\eafe' }); + export const githubAction = new Codicon('github-action', { character: '\\eaff' }); + export const githubAlt = new Codicon('github-alt', { character: '\\eb00' }); + export const globe = new Codicon('globe', { character: '\\eb01' }); + export const grabber = new Codicon('grabber', { character: '\\eb02' }); + export const graph = new Codicon('graph', { character: '\\eb03' }); + export const gripper = new Codicon('gripper', { character: '\\eb04' }); + export const heart = new Codicon('heart', { character: '\\eb05' }); + export const home = new Codicon('home', { character: '\\eb06' }); + export const horizontalRule = new Codicon('horizontal-rule', { character: '\\eb07' }); + export const hubot = new Codicon('hubot', { character: '\\eb08' }); + export const inbox = new Codicon('inbox', { character: '\\eb09' }); + export const issueClosed = new Codicon('issue-closed', { character: '\\eb0a' }); + export const issueReopened = new Codicon('issue-reopened', { character: '\\eb0b' }); + export const issues = new Codicon('issues', { character: '\\eb0c' }); + export const italic = new Codicon('italic', { character: '\\eb0d' }); + export const jersey = new Codicon('jersey', { character: '\\eb0e' }); + export const json = new Codicon('json', { character: '\\eb0f' }); + export const kebabVertical = new Codicon('kebab-vertical', { character: '\\eb10' }); + export const key = new Codicon('key', { character: '\\eb11' }); + export const law = new Codicon('law', { character: '\\eb12' }); + export const lightbulbAutofix = new Codicon('lightbulb-autofix', { character: '\\eb13' }); + export const linkExternal = new Codicon('link-external', { character: '\\eb14' }); + export const link = new Codicon('link', { character: '\\eb15' }); + export const listOrdered = new Codicon('list-ordered', { character: '\\eb16' }); + export const listUnordered = new Codicon('list-unordered', { character: '\\eb17' }); + export const liveShare = new Codicon('live-share', { character: '\\eb18' }); + export const loading = new Codicon('loading', { character: '\\eb19' }); + export const location = new Codicon('location', { character: '\\eb1a' }); + export const mailRead = new Codicon('mail-read', { character: '\\eb1b' }); + export const mail = new Codicon('mail', { character: '\\eb1c' }); + export const markdown = new Codicon('markdown', { character: '\\eb1d' }); + export const megaphone = new Codicon('megaphone', { character: '\\eb1e' }); + export const mention = new Codicon('mention', { character: '\\eb1f' }); + export const milestone = new Codicon('milestone', { character: '\\eb20' }); + export const mortarBoard = new Codicon('mortar-board', { character: '\\eb21' }); + export const move = new Codicon('move', { character: '\\eb22' }); + export const multipleWindows = new Codicon('multiple-windows', { character: '\\eb23' }); + export const mute = new Codicon('mute', { character: '\\eb24' }); + export const noNewline = new Codicon('no-newline', { character: '\\eb25' }); + export const note = new Codicon('note', { character: '\\eb26' }); + export const octoface = new Codicon('octoface', { character: '\\eb27' }); + export const openPreview = new Codicon('open-preview', { character: '\\eb28' }); + export const package_ = new Codicon('package', { character: '\\eb29' }); + export const paintcan = new Codicon('paintcan', { character: '\\eb2a' }); + export const pin = new Codicon('pin', { character: '\\eb2b' }); + export const play = new Codicon('play', { character: '\\eb2c' }); + export const run = new Codicon('run', { character: '\\eb2c' }); + export const plug = new Codicon('plug', { character: '\\eb2d' }); + export const preserveCase = new Codicon('preserve-case', { character: '\\eb2e' }); + export const preview = new Codicon('preview', { character: '\\eb2f' }); + export const project = new Codicon('project', { character: '\\eb30' }); + export const pulse = new Codicon('pulse', { character: '\\eb31' }); + export const question = new Codicon('question', { character: '\\eb32' }); + export const quote = new Codicon('quote', { character: '\\eb33' }); + export const radioTower = new Codicon('radio-tower', { character: '\\eb34' }); + export const reactions = new Codicon('reactions', { character: '\\eb35' }); + export const references = new Codicon('references', { character: '\\eb36' }); + export const refresh = new Codicon('refresh', { character: '\\eb37' }); + export const regex = new Codicon('regex', { character: '\\eb38' }); + export const remoteExplorer = new Codicon('remote-explorer', { character: '\\eb39' }); + export const remote = new Codicon('remote', { character: '\\eb3a' }); + export const remove = new Codicon('remove', { character: '\\eb3b' }); + export const replaceAll = new Codicon('replace-all', { character: '\\eb3c' }); + export const replace = new Codicon('replace', { character: '\\eb3d' }); + export const repoClone = new Codicon('repo-clone', { character: '\\eb3e' }); + export const repoForcePush = new Codicon('repo-force-push', { character: '\\eb3f' }); + export const repoPull = new Codicon('repo-pull', { character: '\\eb40' }); + export const repoPush = new Codicon('repo-push', { character: '\\eb41' }); + export const report = new Codicon('report', { character: '\\eb42' }); + export const requestChanges = new Codicon('request-changes', { character: '\\eb43' }); + export const rocket = new Codicon('rocket', { character: '\\eb44' }); + export const rootFolderOpened = new Codicon('root-folder-opened', { character: '\\eb45' }); + export const rootFolder = new Codicon('root-folder', { character: '\\eb46' }); + export const rss = new Codicon('rss', { character: '\\eb47' }); + export const ruby = new Codicon('ruby', { character: '\\eb48' }); + export const saveAll = new Codicon('save-all', { character: '\\eb49' }); + export const saveAs = new Codicon('save-as', { character: '\\eb4a' }); + export const save = new Codicon('save', { character: '\\eb4b' }); + export const screenFull = new Codicon('screen-full', { character: '\\eb4c' }); + export const screenNormal = new Codicon('screen-normal', { character: '\\eb4d' }); + export const searchStop = new Codicon('search-stop', { character: '\\eb4e' }); + export const server = new Codicon('server', { character: '\\eb50' }); + export const settingsGear = new Codicon('settings-gear', { character: '\\eb51' }); + export const settings = new Codicon('settings', { character: '\\eb52' }); + export const shield = new Codicon('shield', { character: '\\eb53' }); + export const smiley = new Codicon('smiley', { character: '\\eb54' }); + export const sortPrecedence = new Codicon('sort-precedence', { character: '\\eb55' }); + export const splitHorizontal = new Codicon('split-horizontal', { character: '\\eb56' }); + export const splitVertical = new Codicon('split-vertical', { character: '\\eb57' }); + export const squirrel = new Codicon('squirrel', { character: '\\eb58' }); + export const starFull = new Codicon('star-full', { character: '\\eb59' }); + export const starHalf = new Codicon('star-half', { character: '\\eb5a' }); + export const symbolClass = new Codicon('symbol-class', { character: '\\eb5b' }); + export const symbolColor = new Codicon('symbol-color', { character: '\\eb5c' }); + export const symbolConstant = new Codicon('symbol-constant', { character: '\\eb5d' }); + export const symbolEnumMember = new Codicon('symbol-enum-member', { character: '\\eb5e' }); + export const symbolField = new Codicon('symbol-field', { character: '\\eb5f' }); + export const symbolFile = new Codicon('symbol-file', { character: '\\eb60' }); + export const symbolInterface = new Codicon('symbol-interface', { character: '\\eb61' }); + export const symbolKeyword = new Codicon('symbol-keyword', { character: '\\eb62' }); + export const symbolMisc = new Codicon('symbol-misc', { character: '\\eb63' }); + export const symbolOperator = new Codicon('symbol-operator', { character: '\\eb64' }); + export const symbolProperty = new Codicon('symbol-property', { character: '\\eb65' }); + export const wrench = new Codicon('wrench', { character: '\\eb65' }); + export const wrenchSubaction = new Codicon('wrench-subaction', { character: '\\eb65' }); + export const symbolSnippet = new Codicon('symbol-snippet', { character: '\\eb66' }); + export const tasklist = new Codicon('tasklist', { character: '\\eb67' }); + export const telescope = new Codicon('telescope', { character: '\\eb68' }); + export const textSize = new Codicon('text-size', { character: '\\eb69' }); + export const threeBars = new Codicon('three-bars', { character: '\\eb6a' }); + export const thumbsdown = new Codicon('thumbsdown', { character: '\\eb6b' }); + export const thumbsup = new Codicon('thumbsup', { character: '\\eb6c' }); + export const tools = new Codicon('tools', { character: '\\eb6d' }); + export const triangleDown = new Codicon('triangle-down', { character: '\\eb6e' }); + export const triangleLeft = new Codicon('triangle-left', { character: '\\eb6f' }); + export const triangleRight = new Codicon('triangle-right', { character: '\\eb70' }); + export const triangleUp = new Codicon('triangle-up', { character: '\\eb71' }); + export const twitter = new Codicon('twitter', { character: '\\eb72' }); + export const unfold = new Codicon('unfold', { character: '\\eb73' }); + export const unlock = new Codicon('unlock', { character: '\\eb74' }); + export const unmute = new Codicon('unmute', { character: '\\eb75' }); + export const unverified = new Codicon('unverified', { character: '\\eb76' }); + export const verified = new Codicon('verified', { character: '\\eb77' }); + export const versions = new Codicon('versions', { character: '\\eb78' }); + export const vmActive = new Codicon('vm-active', { character: '\\eb79' }); + export const vmOutline = new Codicon('vm-outline', { character: '\\eb7a' }); + export const vmRunning = new Codicon('vm-running', { character: '\\eb7b' }); + export const watch = new Codicon('watch', { character: '\\eb7c' }); + export const whitespace = new Codicon('whitespace', { character: '\\eb7d' }); + export const wholeWord = new Codicon('whole-word', { character: '\\eb7e' }); + export const window = new Codicon('window', { character: '\\eb7f' }); + export const wordWrap = new Codicon('word-wrap', { character: '\\eb80' }); + export const zoomIn = new Codicon('zoom-in', { character: '\\eb81' }); + export const zoomOut = new Codicon('zoom-out', { character: '\\eb82' }); + export const listFilter = new Codicon('list-filter', { character: '\\eb83' }); + export const listFlat = new Codicon('list-flat', { character: '\\eb84' }); + export const listSelection = new Codicon('list-selection', { character: '\\eb85' }); + export const selection = new Codicon('selection', { character: '\\eb85' }); + export const listTree = new Codicon('list-tree', { character: '\\eb86' }); + export const debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { character: '\\eb87' }); + export const debugBreakpointFunction = new Codicon('debug-breakpoint-function', { character: '\\eb88' }); + export const debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { character: '\\eb88' }); + export const debugStackframeActive = new Codicon('debug-stackframe-active', { character: '\\eb89' }); + export const debugStackframeDot = new Codicon('debug-stackframe-dot', { character: '\\eb8a' }); + export const debugStackframe = new Codicon('debug-stackframe', { character: '\\eb8b' }); + export const debugStackframeFocused = new Codicon('debug-stackframe-focused', { character: '\\eb8b' }); + export const debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { character: '\\eb8c' }); + export const symbolString = new Codicon('symbol-string', { character: '\\eb8d' }); + export const debugReverseContinue = new Codicon('debug-reverse-continue', { character: '\\eb8e' }); + export const debugStepBack = new Codicon('debug-step-back', { character: '\\eb8f' }); + export const debugRestartFrame = new Codicon('debug-restart-frame', { character: '\\eb90' }); + export const callIncoming = new Codicon('call-incoming', { character: '\\eb92' }); + export const callOutgoing = new Codicon('call-outgoing', { character: '\\eb93' }); + export const menu = new Codicon('menu', { character: '\\eb94' }); + export const expandAll = new Codicon('expand-all', { character: '\\eb95' }); + export const feedback = new Codicon('feedback', { character: '\\eb96' }); + export const groupByRefType = new Codicon('group-by-ref-type', { character: '\\eb97' }); + export const ungroupByRefType = new Codicon('ungroup-by-ref-type', { character: '\\eb98' }); + export const account = new Codicon('account', { character: '\\eb99' }); + export const bellDot = new Codicon('bell-dot', { character: '\\eb9a' }); + export const debugConsole = new Codicon('debug-console', { character: '\\eb9b' }); + export const library = new Codicon('library', { character: '\\eb9c' }); + export const output = new Codicon('output', { character: '\\eb9d' }); + export const runAll = new Codicon('run-all', { character: '\\eb9e' }); + export const syncIgnored = new Codicon('sync-ignored', { character: '\\eb9f' }); + export const pinned = new Codicon('pinned', { character: '\\eba0' }); + export const githubInverted = new Codicon('github-inverted', { character: '\\eba1' }); + export const debugAlt = new Codicon('debug-alt', { character: '\\eb91' }); + export const serverProcess = new Codicon('server-process', { character: '\\eba2' }); + export const serverEnvironment = new Codicon('server-environment', { character: '\\eba3' }); + export const pass = new Codicon('pass', { character: '\\eba4' }); + export const stopCircle = new Codicon('stop-circle', { character: '\\eba5' }); + export const playCircle = new Codicon('play-circle', { character: '\\eba6' }); + export const record = new Codicon('record', { character: '\\eba7' }); + export const debugAltSmall = new Codicon('debug-alt-small', { character: '\\eba8' }); + export const vmConnect = new Codicon('vm-connect', { character: '\\eba9' }); + export const cloud = new Codicon('cloud', { character: '\\ebaa' }); + export const merge = new Codicon('merge', { character: '\\ebab' }); + export const exportIcon = new Codicon('export', { character: '\\ebac' }); + export const graphLeft = new Codicon('graph-left', { character: '\\ebad' }); + export const magnet = new Codicon('magnet', { character: '\\ebae' }); + export const notebook = new Codicon('notebook', { character: '\\ebaf' }); + export const redo = new Codicon('redo', { character: '\\ebb0' }); + export const checkAll = new Codicon('check-all', { character: '\\ebb1' }); + export const pinnedDirty = new Codicon('pinned-dirty', { character: '\\ebb2' }); +} + + + + const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi; export function escapeCodicons(text: string): string { return text.replace(escapeCodiconsRegex, (match, escaped) => escaped ? match : `\\${match}`); @@ -19,11 +505,11 @@ export function markdownUnescapeCodicons(text: string): string { return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${codicon})`); } -const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi; -export function renderCodicons(text: string): string { - return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => { - return escaped - ? `$(${codicon})` - : ``; - }); +const stripCodiconsRegex = /(\s)?(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)(\s)?/gi; +export function stripCodicons(text: string): string { + if (text.indexOf(codiconStartMarker) === -1) { + return text; + } + + return text.replace(stripCodiconsRegex, (match, preWhitespace, escaped, postWhitespace) => escaped ? match : preWhitespace || postWhitespace || ''); } diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index f185d439651..8729b7303ae 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -20,7 +20,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty; /** * Returns an array which contains all values that reside - * in the given set. + * in the given dictionary. */ export function values(from: IStringDictionary | INumberDictionary): T[] { const result: T[] = []; @@ -32,27 +32,8 @@ export function values(from: IStringDictionary | INumberDictionary): T[ return result; } -export function size(from: IStringDictionary | INumberDictionary): number { - let count = 0; - for (let key in from) { - if (hasOwnProperty.call(from, key)) { - count += 1; - } - } - return count; -} - -export function first(from: IStringDictionary | INumberDictionary): T | undefined { - for (const key in from) { - if (hasOwnProperty.call(from, key)) { - return (from as any)[key]; - } - } - return undefined; -} - /** - * Iterates over each entry in the provided set. The iterator allows + * Iterates over each entry in the provided dictionary. The iterator allows * to remove elements and will stop when the callback returns {{false}}. */ export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any): void { @@ -95,11 +76,6 @@ export function fromMap(original: Map): IStringDictionary { return result; } -export function mapValues(map: Map): V[] { - const result: V[] = []; - map.forEach(v => result.push(v)); - return result; -} export class SetMap { diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 58397cfbcae..eb5fd3b079a 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -240,7 +240,7 @@ export class HSVA { } else if (h < 300) { r = x; b = c; - } else if (h < 360) { + } else if (h <= 360) { r = c; b = x; } diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index d6dbb1784f1..7b45ce5abb6 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -3,11 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from 'vs/base/common/strings'; import { sep } from 'vs/base/common/path'; import { IdleValue } from 'vs/base/common/async'; -const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => { +// When comparing large numbers of strings, such as in sorting large arrays, is better for +// performance to create an Intl.Collator object and use the function provided by its compare +// property than it is to use String.prototype.localeCompare() + +// A collator with numeric sorting enabled, and no sensitivity to case or to accents +const intlFileNameCollatorBaseNumeric: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => { const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); return { collator: collator, @@ -15,21 +19,44 @@ const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumer }; }); +// A collator with numeric sorting enabled. +const intlFileNameCollatorNumeric: IdleValue<{ collator: Intl.Collator }> = new IdleValue(() => { + const collator = new Intl.Collator(undefined, { numeric: true }); + return { + collator: collator + }; +}); + +// A collator with numeric sorting enabled, and sensitivity to accents and diacritics but not case. +const intlFileNameCollatorNumericCaseInsenstive: IdleValue<{ collator: Intl.Collator }> = new IdleValue(() => { + const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'accent' }); + return { + collator: collator + }; +});/** Compares filenames without distinguishing the name from the extension. Disambiguates by unicode comparison. */ export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number { const a = one || ''; const b = other || ''; - const result = intlFileNameCollator.getValue().collator.compare(a, b); + const result = intlFileNameCollatorBaseNumeric.value.collator.compare(a, b); // Using the numeric option in the collator will // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) { + if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && a !== b) { return a < b ? -1 : 1; } return result; } -const FileNameMatch = /^(.*?)(\.([^.]*))?$/; +/** Compares filenames without distinguishing the name from the extension. Disambiguates by length, not unicode comparison. */ +export function compareFileNamesDefault(one: string | null, other: string | null): number { + const collatorNumeric = intlFileNameCollatorNumeric.value.collator; + one = one || ''; + other = other || ''; + + // Compare the entire filename - both name and extension - and disambiguate by length if needed + return compareAndDisambiguateByLength(collatorNumeric, one, other); +} export function noIntlCompareFileNames(one: string | null, other: string | null, caseSensitive = false): number { if (!caseSensitive) { @@ -55,19 +82,19 @@ export function compareFileExtensions(one: string | null, other: string | null): const [oneName, oneExtension] = extractNameAndExtension(one); const [otherName, otherExtension] = extractNameAndExtension(other); - let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension); + let result = intlFileNameCollatorBaseNumeric.value.collator.compare(oneExtension, otherExtension); if (result === 0) { // Using the numeric option in the collator will // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) { + if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && oneExtension !== otherExtension) { return oneExtension < otherExtension ? -1 : 1; } // Extensions are equal, compare filenames - result = intlFileNameCollator.getValue().collator.compare(oneName, otherName); + result = intlFileNameCollatorBaseNumeric.value.collator.compare(oneName, otherName); - if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) { + if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && oneName !== otherName) { return oneName < otherName ? -1 : 1; } } @@ -75,10 +102,64 @@ export function compareFileExtensions(one: string | null, other: string | null): return result; } -function extractNameAndExtension(str?: string | null): [string, string] { +/** Compares filenames by extenson, then by full filename */ +export function compareFileExtensionsDefault(one: string | null, other: string | null): number { + one = one || ''; + other = other || ''; + const oneExtension = extractExtension(one); + const otherExtension = extractExtension(other); + const collatorNumeric = intlFileNameCollatorNumeric.value.collator; + const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator; + let result; + + // Check for extension differences, ignoring differences in case and comparing numbers numerically. + result = compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension); + if (result !== 0) { + return result; + } + + // Compare full filenames + return compareAndDisambiguateByLength(collatorNumeric, one, other); +} + +const FileNameMatch = /^(.*?)(\.([^.]*))?$/; + +/** Extracts the name and extension from a full filename, with optional special handling for dotfiles */ +function extractNameAndExtension(str?: string | null, dotfilesAsNames = false): [string, string] { const match = str ? FileNameMatch.exec(str) as Array : ([] as Array); - return [(match && match[1]) || '', (match && match[3]) || '']; + let result: [string, string] = [(match && match[1]) || '', (match && match[3]) || '']; + + // if the dotfilesAsNames option is selected, treat an empty filename with an extension, + // or a filename that starts with a dot, as a dotfile name + if (dotfilesAsNames && (!result[0] && result[1] || result[0] && result[0].charAt(0) === '.')) { + result = [result[0] + '.' + result[1], '']; + } + + return result; +} + +/** Extracts the extension from a full filename. Treats dotfiles as names, not extensions. */ +function extractExtension(str?: string | null): string { + const match = str ? FileNameMatch.exec(str) as Array : ([] as Array); + + return (match && match[1] && match[1].charAt(0) !== '.' && match[3]) || ''; +} + +function compareAndDisambiguateByLength(collator: Intl.Collator, one: string, other: string) { + // Check for differences + let result = collator.compare(one, other); + if (result !== 0) { + return result; + } + + // In a numeric comparison, `foo1` and `foo01` will compare as equivalent. + // Disambiguate by sorting the shorter string first. + if (one.length !== other.length) { + return one.length < other.length ? -1 : 1; + } + + return 0; } function comparePathComponents(one: string, other: string, caseSensitive = false): number { @@ -133,8 +214,8 @@ export function compareAnything(one: string, other: string, lookFor: string): nu } // Sort suffix matches over non suffix matches - const elementASuffixMatch = strings.endsWith(elementAName, lookFor); - const elementBSuffixMatch = strings.endsWith(elementBName, lookFor); + const elementASuffixMatch = elementAName.endsWith(lookFor); + const elementBSuffixMatch = elementBName.endsWith(lookFor); if (elementASuffixMatch !== elementBSuffixMatch) { return elementASuffixMatch ? -1 : 1; } @@ -154,8 +235,8 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu const elementBName = other.toLowerCase(); // Sort prefix matches over non prefix matches - const elementAPrefixMatch = strings.startsWith(elementAName, lookFor); - const elementBPrefixMatch = strings.startsWith(elementBName, lookFor); + const elementAPrefixMatch = elementAName.startsWith(lookFor); + const elementBPrefixMatch = elementBName.startsWith(lookFor); if (elementAPrefixMatch !== elementBPrefixMatch) { return elementAPrefixMatch ? -1 : 1; } diff --git a/src/vs/base/common/date.ts b/src/vs/base/common/date.ts index a6da9d5ae33..a948b8ddb1a 100644 --- a/src/vs/base/common/date.ts +++ b/src/vs/base/common/date.ts @@ -3,15 +3,128 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { pad } from './strings'; +import { localize } from 'vs/nls'; + +const minute = 60; +const hour = minute * 60; +const day = hour * 24; +const week = day * 7; +const month = day * 30; +const year = day * 365; + +export function fromNow(date: number | Date, appendAgoLabel?: boolean): string { + if (typeof date !== 'number') { + date = date.getTime(); + } + + const seconds = Math.round((new Date().getTime() - date) / 1000); + if (seconds < -30) { + return localize('date.fromNow.in', 'in {0}', fromNow(new Date().getTime() + seconds * 1000, false)); + } + + if (seconds < 30) { + return localize('date.fromNow.now', 'now'); + } + + let value: number; + if (seconds < minute) { + value = seconds; + + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.seconds.singular.ago', '{0} sec ago', value) + : localize('date.fromNow.seconds.plural.ago', '{0} secs ago', value); + } else { + return value === 1 + ? localize('date.fromNow.seconds.singular', '{0} sec', value) + : localize('date.fromNow.seconds.plural', '{0} secs', value); + } + } + + if (seconds < hour) { + value = Math.floor(seconds / minute); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.minutes.singular.ago', '{0} min ago', value) + : localize('date.fromNow.minutes.plural.ago', '{0} mins ago', value); + } else { + return value === 1 + ? localize('date.fromNow.minutes.singular', '{0} min', value) + : localize('date.fromNow.minutes.plural', '{0} mins', value); + } + } + + if (seconds < day) { + value = Math.floor(seconds / hour); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.hours.singular.ago', '{0} hr ago', value) + : localize('date.fromNow.hours.plural.ago', '{0} hrs ago', value); + } else { + return value === 1 + ? localize('date.fromNow.hours.singular', '{0} hr', value) + : localize('date.fromNow.hours.plural', '{0} hrs', value); + } + } + + if (seconds < week) { + value = Math.floor(seconds / day); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.days.singular.ago', '{0} day ago', value) + : localize('date.fromNow.days.plural.ago', '{0} days ago', value); + } else { + return value === 1 + ? localize('date.fromNow.days.singular', '{0} day', value) + : localize('date.fromNow.days.plural', '{0} days', value); + } + } + + if (seconds < month) { + value = Math.floor(seconds / week); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.weeks.singular.ago', '{0} wk ago', value) + : localize('date.fromNow.weeks.plural.ago', '{0} wks ago', value); + } else { + return value === 1 + ? localize('date.fromNow.weeks.singular', '{0} wk', value) + : localize('date.fromNow.weeks.plural', '{0} wks', value); + } + } + + if (seconds < year) { + value = Math.floor(seconds / month); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.months.singular.ago', '{0} mo ago', value) + : localize('date.fromNow.months.plural.ago', '{0} mos ago', value); + } else { + return value === 1 + ? localize('date.fromNow.months.singular', '{0} mo', value) + : localize('date.fromNow.months.plural', '{0} mos', value); + } + } + + value = Math.floor(seconds / year); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.years.singular.ago', '{0} yr ago', value) + : localize('date.fromNow.years.plural.ago', '{0} yrs ago', value); + } else { + return value === 1 + ? localize('date.fromNow.years.singular', '{0} yr', value) + : localize('date.fromNow.years.plural', '{0} yrs', value); + } +} export function toLocalISOString(date: Date): string { return date.getFullYear() + - '-' + pad(date.getMonth() + 1, 2) + - '-' + pad(date.getDate(), 2) + - 'T' + pad(date.getHours(), 2) + - ':' + pad(date.getMinutes(), 2) + - ':' + pad(date.getSeconds(), 2) + + '-' + String(date.getMonth() + 1).padStart(2, '0') + + '-' + String(date.getDate()).padStart(2, '0') + + 'T' + String(date.getHours()).padStart(2, '0') + + ':' + String(date.getMinutes()).padStart(2, '0') + + ':' + String(date.getSeconds()).padStart(2, '0') + '.' + (date.getMilliseconds() / 1000).toFixed(3).slice(2, 5) + 'Z'; } diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index b2440204634..f6d4f5cf893 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -203,3 +203,12 @@ export class NotImplementedError extends Error { } } } + +export class NotSupportedError extends Error { + constructor(message?: string) { + super('NotSupported'); + if (message) { + this.message = message; + } + } +} diff --git a/src/vs/base/common/errorsWithActions.ts b/src/vs/base/common/errorsWithActions.ts index 133febb8453..fa92b7f4526 100644 --- a/src/vs/base/common/errorsWithActions.ts +++ b/src/vs/base/common/errorsWithActions.ts @@ -13,7 +13,7 @@ export interface IErrorWithActions { actions?: ReadonlyArray; } -export function isErrorWithActions(obj: any): obj is IErrorWithActions { +export function isErrorWithActions(obj: unknown): obj is IErrorWithActions { return obj instanceof Error && Array.isArray((obj as IErrorWithActions).actions); } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 49b7b449862..f70aa9d8d2f 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -85,6 +85,8 @@ export namespace Event { * Given a collection of events, returns a single event which emits * whenever any of the provided events emit. */ + export function any(...events: Event[]): Event; + export function any(...events: Event[]): Event; export function any(...events: Event[]): Event { return (listener, thisArgs = null, disposables?) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); } @@ -271,6 +273,7 @@ export namespace Event { map(fn: (i: T) => O): IChainableEvent; forEach(fn: (i: T) => void): IChainableEvent; filter(fn: (e: T) => boolean): IChainableEvent; + filter(fn: (e: T | R) => e is R): IChainableEvent; reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent; latch(): IChainableEvent; debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent; @@ -291,6 +294,8 @@ export namespace Event { return new ChainableEvent(forEach(this.event, fn)); } + filter(fn: (e: T) => boolean): IChainableEvent; + filter(fn: (e: T | R) => e is R): IChainableEvent; filter(fn: (e: T) => boolean): IChainableEvent { return new ChainableEvent(filter(this.event, fn)); } @@ -323,8 +328,8 @@ export namespace Event { } export interface NodeEventEmitter { - on(event: string | symbol, listener: Function): this; - removeListener(event: string | symbol, listener: Function): this; + on(event: string | symbol, listener: Function): unknown; + removeListener(event: string | symbol, listener: Function): unknown; } export function fromNodeEventEmitter(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event { @@ -435,14 +440,14 @@ class LeakageMonitor { this._warnCountdown = threshold * 0.5; // find most frequent listener and print warning - let topStack: string; + let topStack: string | undefined; let topCount: number = 0; - this._stacks.forEach((count, stack) => { + for (const [stack, count] of this._stacks) { if (!topStack || topCount < count) { topStack = stack; topCount = count; } - }); + } console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`); console.warn(topStack!); @@ -571,8 +576,8 @@ export class Emitter { this._deliveryQueue = new LinkedList(); } - for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - this._deliveryQueue.push([e.value, event]); + for (let listener of this._listeners) { + this._deliveryQueue.push([listener, event]); } while (this._deliveryQueue.size > 0) { @@ -666,8 +671,8 @@ export class AsyncEmitter extends Emitter { this._asyncDeliveryQueue = new LinkedList(); } - for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - this._asyncDeliveryQueue.push([e.value, data]); + for (const listener of this._listeners) { + this._asyncDeliveryQueue.push([listener, data]); } while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) { diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts index 907dc51ff93..ad10163e752 100644 --- a/src/vs/base/common/extpath.ts +++ b/src/vs/base/common/extpath.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { isWindows } from 'vs/base/common/platform'; -import { startsWithIgnoreCase, equalsIgnoreCase, endsWith, rtrim } from 'vs/base/common/strings'; +import { startsWithIgnoreCase, equalsIgnoreCase, rtrim } from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; import { sep, posix, isAbsolute, join, normalize } from 'vs/base/common/path'; +import { isNumber } from 'vs/base/common/types'; export function isPathSeparator(code: number) { return code === CharCode.Slash || code === CharCode.Backslash; @@ -141,7 +142,7 @@ export function isUNC(path: string): boolean { // Reference: https://en.wikipedia.org/wiki/Filename const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g; const UNIX_INVALID_FILE_CHARS = /[\\/]/g; -const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; +const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i; export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean { const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS; @@ -190,42 +191,42 @@ export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boo return equalsIgnoreCase(pathA, pathB); } -export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean, separator = sep): boolean { - if (path === candidate) { +export function isEqualOrParent(base: string, parentCandidate: string, ignoreCase?: boolean, separator = sep): boolean { + if (base === parentCandidate) { return true; } - if (!path || !candidate) { + if (!base || !parentCandidate) { return false; } - if (candidate.length > path.length) { + if (parentCandidate.length > base.length) { return false; } if (ignoreCase) { - const beginsWith = startsWithIgnoreCase(path, candidate); + const beginsWith = startsWithIgnoreCase(base, parentCandidate); if (!beginsWith) { return false; } - if (candidate.length === path.length) { + if (parentCandidate.length === base.length) { return true; // same path, different casing } - let sepOffset = candidate.length; - if (candidate.charAt(candidate.length - 1) === separator) { + let sepOffset = parentCandidate.length; + if (parentCandidate.charAt(parentCandidate.length - 1) === separator) { sepOffset--; // adjust the expected sep offset in case our candidate already ends in separator character } - return path.charAt(sepOffset) === separator; + return base.charAt(sepOffset) === separator; } - if (candidate.charAt(candidate.length - 1) !== separator) { - candidate += separator; + if (parentCandidate.charAt(parentCandidate.length - 1) !== separator) { + parentCandidate += separator; } - return path.indexOf(candidate) === 0; + return base.indexOf(parentCandidate) === 0; } export function isWindowsDriveLetter(char0: number): boolean { @@ -235,7 +236,7 @@ export function isWindowsDriveLetter(char0: number): boolean { export function sanitizeFilePath(candidate: string, cwd: string): string { // Special case: allow to open a drive letter without trailing backslash - if (isWindows && endsWith(candidate, ':')) { + if (isWindows && candidate.endsWith(':')) { candidate += sep; } @@ -252,7 +253,7 @@ export function sanitizeFilePath(candidate: string, cwd: string): string { candidate = rtrim(candidate, sep); // Special case: allow to open drive root ('C:\') - if (endsWith(candidate, ':')) { + if (candidate.endsWith(':')) { candidate += sep; } @@ -283,3 +284,55 @@ export function isRootOrDriveLetter(path: string): boolean { return pathNormalized === posix.sep; } + +export function indexOfPath(path: string, candidate: string, ignoreCase?: boolean): number { + if (candidate.length > path.length) { + return -1; + } + + if (path === candidate) { + return 0; + } + + if (ignoreCase) { + path = path.toLowerCase(); + candidate = candidate.toLowerCase(); + } + + return path.indexOf(candidate); +} + +export interface IPathWithLineAndColumn { + path: string; + line?: number; + column?: number; +} + +export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn { + const segments = rawPath.split(':'); // C:\file.txt:: + + let path: string | undefined = undefined; + let line: number | undefined = undefined; + let column: number | undefined = undefined; + + segments.forEach(segment => { + const segmentAsNumber = Number(segment); + if (!isNumber(segmentAsNumber)) { + path = !!path ? [path, segment].join(':') : segment; // a colon can well be part of a path (e.g. C:\...) + } else if (line === undefined) { + line = segmentAsNumber; + } else if (column === undefined) { + column = segmentAsNumber; + } + }); + + if (!path) { + throw new Error('Format for `--goto` should be: `FILE:LINE(:COLUMN)`'); + } + + return { + path, + line: line !== undefined ? line : undefined, + column: column !== undefined ? column : line !== undefined ? 1 : undefined // if we have a line, make sure column is also set + }; +} diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 3740a128f1a..adff0517356 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -559,6 +559,8 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu let patternPos = patternStart; let wordPos = wordStart; + let hasStrongFirstMatch = false; + // There will be a match, fill in tables for (row = 1, patternPos = patternStart; patternPos < patternLen; row++, patternPos++) { @@ -566,6 +568,10 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu const score = _doScore(pattern, patternLow, patternPos, patternStart, word, wordLow, wordPos); + if (patternPos === patternStart && score > 1) { + hasStrongFirstMatch = true; + } + _scores[row][column] = score; const diag = _table[row - 1][column - 1] + (score > 1 ? 1 : score); @@ -604,6 +610,10 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu printTables(pattern, patternStart, word, wordStart); } + if (!hasStrongFirstMatch && !firstMatchCanBeWeak) { + return undefined; + } + _matchesCount = 0; _topScore = -100; _wordStart = wordStart; diff --git a/src/vs/base/common/functional.ts b/src/vs/base/common/functional.ts index 4587a5b7542..b437cc98c46 100644 --- a/src/vs/base/common/functional.ts +++ b/src/vs/base/common/functional.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export function once(this: any, fn: T): T { +export function once(this: unknown, fn: T): T { const _this = this; let didCall = false; - let result: any; + let result: unknown; return function () { if (didCall) { @@ -17,5 +17,5 @@ export function once(this: any, fn: T): T { result = fn.apply(_this, arguments); return result; - } as any as T; -} \ No newline at end of file + } as unknown as T; +} diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/common/fuzzyScorer.ts similarity index 51% rename from src/vs/base/parts/quickopen/common/quickOpenScorer.ts rename to src/vs/base/common/fuzzyScorer.ts index 21cb08caace..11fc4bc0ed3 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -4,22 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, matchesCamelCase, isUpper } from 'vs/base/common/filters'; +import { matchesPrefix, IMatch, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters'; import { sep } from 'vs/base/common/path'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; -export type Score = [number /* score */, number[] /* match positions */]; -export type ScorerCache = { [key: string]: IItemScore }; +//#region Fuzzy scorer + +export type FuzzyScore = [number /* score */, number[] /* match positions */]; +export type FuzzyScorerCache = { [key: string]: IItemScore }; const NO_MATCH = 0; -const NO_SCORE: Score = [NO_MATCH, []]; +const NO_SCORE: FuzzyScore = [NO_MATCH, []]; // const DEBUG = false; // const DEBUG_MATRIX = false; -export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score { +export function scoreFuzzy(target: string, query: string, queryLower: string, fuzzy: boolean): FuzzyScore { if (!target || !query) { return NO_SCORE; // return early if target or query are undefined } @@ -40,8 +42,7 @@ export function score(target: string, query: string, queryLower: string, fuzzy: // When not searching fuzzy, we require the query to be contained fully // in the target string contiguously. if (!fuzzy) { - const indexOfQueryInTarget = targetLower.indexOf(queryLower); - if (indexOfQueryInTarget === -1) { + if (!targetLower.includes(queryLower)) { // if (DEBUG) { // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); // } @@ -50,7 +51,7 @@ export function score(target: string, query: string, queryLower: string, fuzzy: } } - const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength); + const res = doScoreFuzzy(query, queryLower, queryLength, target, targetLower, targetLength); // if (DEBUG) { // console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold'); @@ -60,7 +61,7 @@ export function score(target: string, query: string, queryLower: string, fuzzy: return res; } -function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): Score { +function doScoreFuzzy(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): FuzzyScore { const scores: number[] = []; const matches: number[] = []; @@ -159,7 +160,7 @@ function doScore(query: string, queryLower: string, queryLength: number, target: function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { let score = 0; - if (queryLowerCharAtIndex !== targetLower[targetIndex]) { + if (!considerAsEqual(queryLowerCharAtIndex, targetLower[targetIndex])) { return score; // no match of characters } @@ -167,7 +168,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += 1; // if (DEBUG) { - // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); + // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLowerCharAtIndex} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); // } // Consecutive match bonus @@ -175,7 +176,7 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += (matchesSequenceLength * 5); // if (DEBUG) { - // console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5)); + // console.log(`Consecutive match bonus: +${matchesSequenceLength * 5}`); // } } @@ -205,16 +206,16 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin score += separatorBonus; // if (DEBUG) { - // console.log('After separtor bonus: +4'); + // console.log(`After separtor bonus: +${separatorBonus}`); // } } // Inside word upper case bonus (camel case) else if (isUpper(target.charCodeAt(targetIndex))) { - score += 1; + score += 2; // if (DEBUG) { - // console.log('Inside word upper case bonus: +1'); + // console.log('Inside word upper case bonus: +2'); // } } } @@ -226,6 +227,19 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin return score; } +function considerAsEqual(a: string, b: string): boolean { + if (a === b) { + return true; + } + + // Special case path spearators: ignore platform differences + if (a === '/' || a === '\\') { + return b === '/' || b === '\\'; + } + + return false; +} + function scoreSeparatorAtPos(charCode: number): number { switch (charCode) { case CharCode.Slash: @@ -257,6 +271,62 @@ function scoreSeparatorAtPos(charCode: number): number { // } // } +//#endregion + + +//#region Alternate fuzzy scorer implementation that is e.g. used for symbols + +export type FuzzyScore2 = [number | undefined /* score */, IMatch[]]; + +const NO_SCORE2: FuzzyScore2 = [undefined, []]; + +export function scoreFuzzy2(target: string, query: IPreparedQuery | IPreparedQueryPiece, patternStart = 0, wordStart = 0): FuzzyScore2 { + + // Score: multiple inputs + const preparedQuery = query as IPreparedQuery; + if (preparedQuery.values && preparedQuery.values.length > 1) { + return doScoreFuzzy2Multiple(target, preparedQuery.values, patternStart, wordStart); + } + + // Score: single input + return doScoreFuzzy2Single(target, query, patternStart, wordStart); +} + +function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], patternStart: number, wordStart: number): FuzzyScore2 { + let totalScore = 0; + const totalMatches: IMatch[] = []; + + for (const queryPiece of query) { + const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, wordStart); + if (typeof score !== 'number') { + // if a single query value does not match, return with + // no score entirely, we require all queries to match + return NO_SCORE2; + } + + totalScore += score; + totalMatches.push(...matches); + } + + // if we have a score, ensure that the positions are + // sorted in ascending order and distinct + return [totalScore, normalizeMatches(totalMatches)]; +} + +function doScoreFuzzy2Single(target: string, query: IPreparedQueryPiece, patternStart: number, wordStart: number): FuzzyScore2 { + const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), wordStart, true); + if (!score) { + return NO_SCORE2; + } + + return [score[0], createFuzzyMatches(score)]; +} + +//#endregion + + +//#region Item (label, description, path) scorer + /** * Scoring on structural items that have a label and optional description. */ @@ -285,52 +355,25 @@ export interface IItemAccessor { /** * Just the label of the item to score on. */ - getItemLabel(item: T): string | null; + getItemLabel(item: T): string | undefined; /** - * The optional description of the item to score on. Can be null. + * The optional description of the item to score on. */ - getItemDescription(item: T): string | null; + getItemDescription(item: T): string | undefined; /** - * If the item is a file, the path of the file to score on. Can be null. + * If the item is a file, the path of the file to score on. */ getItemPath(file: T): string | undefined; } const PATH_IDENTITY_SCORE = 1 << 18; -const LABEL_PREFIX_SCORE = 1 << 17; -const LABEL_CAMELCASE_SCORE = 1 << 16; -const LABEL_SCORE_THRESHOLD = 1 << 15; +const LABEL_PREFIX_SCORE_THRESHOLD = 1 << 17; +const LABEL_SCORE_THRESHOLD = 1 << 16; -export interface IPreparedQuery { - original: string; - value: string; - lowercase: string; - containsPathSeparator: boolean; -} - -/** - * Helper function to prepare a search value for scoring in quick open by removing unwanted characters. - */ -export function prepareQuery(original: string): IPreparedQuery { - if (!original) { - original = ''; - } - - let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace - if (isWindows) { - value = value.replace(/\//g, sep); // Help Windows users to search for paths when using slash - } - - const lowercase = value.toLowerCase(); - const containsPathSeparator = value.indexOf(sep) >= 0; - - return { original, value, lowercase, containsPathSeparator }; -} - -export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache): IItemScore { - if (!item || !query.value) { +export function scoreItemFuzzy(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: FuzzyScorerCache): IItemScore { + if (!item || !query.normalized) { return NO_ITEM_SCORE; // we need an item and query to score on at least } @@ -341,11 +384,17 @@ export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, acc const description = accessor.getItemDescription(item); + // in order to speed up scoring, we cache the score with a unique hash based on: + // - label + // - description (if provided) + // - query (normalized) + // - number of query pieces (i.e. 'hello world' and 'helloworld' are different) + // - wether fuzzy matching is enabled or not let cacheHash: string; if (description) { - cacheHash = `${label}${description}${query.value}${fuzzy}`; + cacheHash = `${label}${description}${query.normalized}${Array.isArray(query.values) ? query.values.length : ''}${fuzzy}`; } else { - cacheHash = `${label}${query.value}${fuzzy}`; + cacheHash = `${label}${query.normalized}${Array.isArray(query.values) ? query.values.length : ''}${fuzzy}`; } const cached = cache[cacheHash]; @@ -353,60 +402,94 @@ export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, acc return cached; } - const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, fuzzy); + const itemScore = doScoreItemFuzzy(label, description, accessor.getItemPath(item), query, fuzzy); cache[cacheHash] = itemScore; return itemScore; } -function createMatches(offsets: undefined | number[]): IMatch[] { - let ret: IMatch[] = []; - if (!offsets) { - return ret; - } - let last: IMatch | undefined; - for (const pos of offsets) { - if (last && last.end === pos) { - last.end += 1; - } else { - last = { start: pos, end: pos + 1 }; - ret.push(last); - } - } - return ret; -} +function doScoreItemFuzzy(label: string, description: string | undefined, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore { + const preferLabelMatches = !path || !query.containsPathSeparator; -function doScoreItem(label: string, description: string | null, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore { - - // 1.) treat identity matches on full path highest - if (path && (isLinux ? query.original === path : equalsIgnoreCase(query.original, path))) { + // Treat identity matches on full path highest + if (path && (isLinux ? query.pathNormalized === path : equalsIgnoreCase(query.pathNormalized, path))) { return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined }; } - // We only consider label matches if the query is not including file path separators - const preferLabelMatches = !path || !query.containsPathSeparator; - if (preferLabelMatches) { + // Score: multiple inputs + if (query.values && query.values.length > 1) { + return doScoreItemFuzzyMultiple(label, description, path, query.values, preferLabelMatches, fuzzy); + } - // 2.) treat prefix matches on the label second highest - const prefixLabelMatch = matchesPrefix(query.value, label); - if (prefixLabelMatch) { - return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch }; + // Score: single input + return doScoreItemFuzzySingle(label, description, path, query, preferLabelMatches, fuzzy); +} + +function doScoreItemFuzzyMultiple(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece[], preferLabelMatches: boolean, fuzzy: boolean): IItemScore { + let totalScore = 0; + const totalLabelMatches: IMatch[] = []; + const totalDescriptionMatches: IMatch[] = []; + + for (const queryPiece of query) { + const { score, labelMatch, descriptionMatch } = doScoreItemFuzzySingle(label, description, path, queryPiece, preferLabelMatches, fuzzy); + if (score === NO_MATCH) { + // if a single query value does not match, return with + // no score entirely, we require all queries to match + return NO_ITEM_SCORE; } - // 3.) treat camelcase matches on the label third highest - const camelcaseLabelMatch = matchesCamelCase(query.value, label); - if (camelcaseLabelMatch) { - return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch }; + totalScore += score; + if (labelMatch) { + totalLabelMatches.push(...labelMatch); } - // 4.) prefer scores on the label if any - const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy); - if (labelScore) { - return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) }; + if (descriptionMatch) { + totalDescriptionMatches.push(...descriptionMatch); } } - // 5.) finally compute description + label scores if we have a description + // if we have a score, ensure that the positions are + // sorted in ascending order and distinct + return { + score: totalScore, + labelMatch: normalizeMatches(totalLabelMatches), + descriptionMatch: normalizeMatches(totalDescriptionMatches) + }; +} + +function doScoreItemFuzzySingle(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece, preferLabelMatches: boolean, fuzzy: boolean): IItemScore { + + // Prefer label matches if told so or we have no description + if (preferLabelMatches || !description) { + const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy); + if (labelScore) { + + // If we have a prefix match on the label, we give a much + // higher baseScore to elevate these matches over others + // This ensures that typing a file name wins over results + // that are present somewhere in the label, but not the + // beginning. + const labelPrefixMatch = matchesPrefix(query.normalized, label); + let baseScore: number; + if (labelPrefixMatch) { + baseScore = LABEL_PREFIX_SCORE_THRESHOLD; + + // We give another boost to labels that are short, e.g. given + // files "window.ts" and "windowActions.ts" and a query of + // "window", we want "window.ts" to receive a higher score. + // As such we compute the percentage the query has within the + // label and add that to the baseScore. + const prefixLengthBoost = Math.round((query.normalized.length / label.length) * 100); + baseScore += prefixLengthBoost; + } else { + baseScore = LABEL_SCORE_THRESHOLD; + } + + return { score: baseScore + labelScore, labelMatch: labelPrefixMatch || createMatches(labelPositions) }; + } + } + + // Finally compute description + label scores if we have a description if (description) { let descriptionPrefix = description; if (!!path) { @@ -416,7 +499,7 @@ function doScoreItem(label: string, description: string | null, path: string | u const descriptionPrefixLength = descriptionPrefix.length; const descriptionAndLabel = `${descriptionPrefix}${label}`; - const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy); + const [labelDescriptionScore, labelDescriptionPositions] = scoreFuzzy(descriptionAndLabel, query.normalized, query.normalizedLowercase, fuzzy); if (labelDescriptionScore) { const labelDescriptionMatches = createMatches(labelDescriptionPositions); const labelMatch: IMatch[] = []; @@ -449,82 +532,132 @@ function doScoreItem(label: string, description: string | null, path: string | u return NO_ITEM_SCORE; } -export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache, fallbackComparer = fallbackCompare): number { - const itemScoreA = scoreItem(itemA, query, fuzzy, accessor, cache); - const itemScoreB = scoreItem(itemB, query, fuzzy, accessor, cache); +function createMatches(offsets: number[] | undefined): IMatch[] { + const ret: IMatch[] = []; + if (!offsets) { + return ret; + } + + let last: IMatch | undefined; + for (const pos of offsets) { + if (last && last.end === pos) { + last.end += 1; + } else { + last = { start: pos, end: pos + 1 }; + ret.push(last); + } + } + + return ret; +} + +function normalizeMatches(matches: IMatch[]): IMatch[] { + + // sort matches by start to be able to normalize + const sortedMatches = matches.sort((matchA, matchB) => { + return matchA.start - matchB.start; + }); + + // merge matches that overlap + const normalizedMatches: IMatch[] = []; + let currentMatch: IMatch | undefined = undefined; + for (const match of sortedMatches) { + + // if we have no current match or the matches + // do not overlap, we take it as is and remember + // it for future merging + if (!currentMatch || !matchOverlaps(currentMatch, match)) { + currentMatch = match; + normalizedMatches.push(match); + } + + // otherwise we merge the matches + else { + currentMatch.start = Math.min(currentMatch.start, match.start); + currentMatch.end = Math.max(currentMatch.end, match.end); + } + } + + return normalizedMatches; +} + +function matchOverlaps(matchA: IMatch, matchB: IMatch): boolean { + if (matchA.end < matchB.start) { + return false; // A ends before B starts + } + + if (matchB.end < matchA.start) { + return false; // B ends before A starts + } + + return true; +} + +//#endregion + + +//#region Comparers + +export function compareItemsByFuzzyScore(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: FuzzyScorerCache): number { + const itemScoreA = scoreItemFuzzy(itemA, query, fuzzy, accessor, cache); + const itemScoreB = scoreItemFuzzy(itemB, query, fuzzy, accessor, cache); const scoreA = itemScoreA.score; const scoreB = itemScoreB.score; - // 1.) prefer identity matches + // 1.) identity matches have highest score if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) { if (scoreA !== scoreB) { return scoreA === PATH_IDENTITY_SCORE ? -1 : 1; } } - // 2.) prefer label prefix matches - if (scoreA === LABEL_PREFIX_SCORE || scoreB === LABEL_PREFIX_SCORE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_PREFIX_SCORE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer shorter names when both match on label prefix - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 3.) prefer camelcase matches - if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - // prefer more compact camel case matches over longer - const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); - if (comparedByMatchLength !== 0) { - return comparedByMatchLength; - } - - // prefer shorter names when both match on label camelcase - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 4.) prefer label scores + // 2.) matches on label are considered higher compared to label+description matches if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { - if (scoreB < LABEL_SCORE_THRESHOLD) { - return -1; + if (scoreA !== scoreB) { + return scoreA > scoreB ? -1 : 1; } - if (scoreA < LABEL_SCORE_THRESHOLD) { - return 1; + // prefer more compact matches over longer in label (unless this is a prefix match where + // longer prefix matches are actually preferred) + if (scoreA < LABEL_PREFIX_SCORE_THRESHOLD && scoreB < LABEL_PREFIX_SCORE_THRESHOLD) { + const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); + if (comparedByMatchLength !== 0) { + return comparedByMatchLength; + } + } + + // prefer shorter labels over longer labels + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; + if (labelA.length !== labelB.length) { + return labelA.length - labelB.length; } } - // 5.) compare by score + // 3.) compare by score in label+description if (scoreA !== scoreB) { return scoreA > scoreB ? -1 : 1; } - // 6.) scores are identical, prefer more compact matches (label and description) + // 4.) scores are identical: prefer matches in label over non-label matches + const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0; + const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0; + if (itemAHasLabelMatches && !itemBHasLabelMatches) { + return -1; + } else if (itemBHasLabelMatches && !itemAHasLabelMatches) { + return 1; + } + + // 5.) scores are identical: prefer more compact matches (label and description) const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor); const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor); if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) { return itemBMatchDistance > itemAMatchDistance ? -1 : 1; } - // 7.) at this point, scores are identical and match compactness as well - // for both items so we start to use the fallback compare - return fallbackComparer(itemA, itemB, query, accessor); + // 6.) scores are identical: start to use the fallback compare + return fallbackCompare(itemA, itemB, query, accessor); } function computeLabelAndDescriptionMatchDistance(item: T, score: IItemScore, accessor: IItemAccessor): number { @@ -589,7 +722,7 @@ function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1; } -export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { +function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { // check for label + description length and prefer shorter const labelA = accessor.getItemLabel(itemA) || ''; @@ -617,19 +750,133 @@ export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, ac // compare by label if (labelA !== labelB) { - return compareAnything(labelA, labelB, query.value); + return compareAnything(labelA, labelB, query.normalized); } // compare by description if (descriptionA && descriptionB && descriptionA !== descriptionB) { - return compareAnything(descriptionA, descriptionB, query.value); + return compareAnything(descriptionA, descriptionB, query.normalized); } // compare by path if (pathA && pathB && pathA !== pathB) { - return compareAnything(pathA, pathB, query.value); + return compareAnything(pathA, pathB, query.normalized); } // equal return 0; } + +//#endregion + + +//#region Query Normalizer + +export interface IPreparedQueryPiece { + + /** + * The original query as provided as input. + */ + original: string; + originalLowercase: string; + + /** + * Original normalized to platform separators: + * - Windows: \ + * - Posix: / + */ + pathNormalized: string; + + /** + * In addition to the normalized path, will have + * whitespace and wildcards removed. + */ + normalized: string; + normalizedLowercase: string; +} + +export interface IPreparedQuery extends IPreparedQueryPiece { + + /** + * Query split by spaces into pieces. + */ + values: IPreparedQueryPiece[] | undefined; + + /** + * Whether the query contains path separator(s) or not. + */ + containsPathSeparator: boolean; +} + +/** + * Helper function to prepare a search value for scoring by removing unwanted characters + * and allowing to score on multiple pieces separated by whitespace character. + */ +const MULTIPLE_QUERY_VALUES_SEPARATOR = ' '; +export function prepareQuery(original: string): IPreparedQuery { + if (typeof original !== 'string') { + original = ''; + } + + const originalLowercase = original.toLowerCase(); + const { pathNormalized, normalized, normalizedLowercase } = normalizeQuery(original); + const containsPathSeparator = pathNormalized.indexOf(sep) >= 0; + + let values: IPreparedQueryPiece[] | undefined = undefined; + + const originalSplit = original.split(MULTIPLE_QUERY_VALUES_SEPARATOR); + if (originalSplit.length > 1) { + for (const originalPiece of originalSplit) { + const { + pathNormalized: pathNormalizedPiece, + normalized: normalizedPiece, + normalizedLowercase: normalizedLowercasePiece + } = normalizeQuery(originalPiece); + + if (normalizedPiece) { + if (!values) { + values = []; + } + + values.push({ + original: originalPiece, + originalLowercase: originalPiece.toLowerCase(), + pathNormalized: pathNormalizedPiece, + normalized: normalizedPiece, + normalizedLowercase: normalizedLowercasePiece + }); + } + } + } + + return { original, originalLowercase, pathNormalized, normalized, normalizedLowercase, values, containsPathSeparator }; +} + +function normalizeQuery(original: string): { pathNormalized: string, normalized: string, normalizedLowercase: string } { + let pathNormalized: string; + if (isWindows) { + pathNormalized = original.replace(/\//g, sep); // Help Windows users to search for paths when using slash + } else { + pathNormalized = original.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash + } + + const normalized = stripWildcards(pathNormalized).replace(/\s/g, ''); + + return { + pathNormalized, + normalized, + normalizedLowercase: normalized.toLowerCase() + }; +} + +export function pieceToQuery(piece: IPreparedQueryPiece): IPreparedQuery; +export function pieceToQuery(pieces: IPreparedQueryPiece[]): IPreparedQuery; +export function pieceToQuery(arg1: IPreparedQueryPiece | IPreparedQueryPiece[]): IPreparedQuery { + if (Array.isArray(arg1)) { + return prepareQuery(arg1.map(piece => piece.original).join(MULTIPLE_QUERY_VALUES_SEPARATOR)); + } + + return prepareQuery(arg1.original); +} + +//#endregion diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 78fc5e97ce1..b8aaab0a049 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from 'vs/base/common/arrays'; import * as strings from 'vs/base/common/strings'; import * as extpath from 'vs/base/common/extpath'; import * as paths from 'vs/base/common/path'; @@ -251,14 +250,14 @@ export interface IGlobOptions { } interface ParsedStringPattern { - (path: string, basename: string): string | null | Promise /* the matching pattern */; + (path: string, basename?: string): string | null | Promise /* the matching pattern */; basenames?: string[]; patterns?: string[]; allBasenames?: string[]; allPaths?: string[]; } interface ParsedExpressionPattern { - (path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise /* the matching pattern */; + (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise /* the matching pattern */; requiresSiblings?: boolean; allBasenames?: string[]; allPaths?: string[]; @@ -302,7 +301,7 @@ function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): P if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check const base = pattern.substr(4); // '**/*'.length === 4 parsedPattern = function (path, basename) { - return typeof path === 'string' && strings.endsWith(path, base) ? pattern : null; + return typeof path === 'string' && path.endsWith(base) ? pattern : null; }; } else if (match = T2.exec(trimForExclusions(pattern, options))) { // common pattern: **/some.txt just need basename check parsedPattern = trivia2(match[1], pattern); @@ -339,7 +338,7 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string | } function trimForExclusions(pattern: string, options: IGlobOptions): string { - return options.trimForExclusions && strings.endsWith(pattern, '/**') ? pattern.substr(0, pattern.length - 2) : pattern; // dropping **, tailing / is dropped later + return options.trimForExclusions && pattern.endsWith('/**') ? pattern.substr(0, pattern.length - 2) : pattern; // dropping **, tailing / is dropped later } // common pattern: **/some.txt just need basename check @@ -353,7 +352,7 @@ function trivia2(base: string, originalPattern: string): ParsedStringPattern { if (basename) { return basename === base ? originalPattern : null; } - return path === base || strings.endsWith(path, slashBase) || strings.endsWith(path, backslashBase) ? originalPattern : null; + return path === base || path.endsWith(slashBase) || path.endsWith(backslashBase) ? originalPattern : null; }; const basenames = [base]; parsedPattern.basenames = basenames; @@ -374,7 +373,7 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern { if (n === 1) { return parsedPatterns[0]; } - const parsedPattern: ParsedStringPattern = function (path: string, basename: string) { + const parsedPattern: ParsedStringPattern = function (path: string, basename?: string) { for (let i = 0, n = parsedPatterns.length; i < n; i++) { if ((parsedPatterns[i])(path, basename)) { return pattern; @@ -382,7 +381,7 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern { } return null; }; - const withBasenames = arrays.first(parsedPatterns, pattern => !!(pattern).allBasenames); + const withBasenames = parsedPatterns.find(pattern => !!(pattern).allBasenames); if (withBasenames) { parsedPattern.allBasenames = (withBasenames).allBasenames; } @@ -398,7 +397,7 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par const nativePath = paths.sep !== paths.posix.sep ? path.replace(ALL_FORWARD_SLASHES, paths.sep) : path; const nativePathEnd = paths.sep + nativePath; const parsedPattern: ParsedStringPattern = matchPathEnds ? function (path, basename) { - return typeof path === 'string' && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null; + return typeof path === 'string' && (path === nativePath || path.endsWith(nativePathEnd)) ? pattern : null; } : function (path, basename) { return typeof path === 'string' && path === nativePath ? pattern : null; }; @@ -409,7 +408,7 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par function toRegExp(pattern: string): ParsedStringPattern { try { const regExp = new RegExp(`^${parseRegExp(pattern)}$`); - return function (path: string, basename: string) { + return function (path: string) { regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it! return typeof path === 'string' && regExp.test(path) ? pattern : null; }; @@ -457,7 +456,7 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG if (parsedPattern === NULL) { return FALSE; } - const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename: string) { + const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename?: string) { return !!parsedPattern(path, basename); }; if (parsedPattern.allBasenames) { @@ -540,7 +539,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return parsedPatterns[0]; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string) { + const resultExpression: ParsedStringPattern = function (path: string, basename?: string) { for (let i = 0, n = parsedPatterns.length; i < n; i++) { // Pattern matches path const result = (parsedPatterns[i])(path, basename); @@ -552,7 +551,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return null; }; - const withBasenames = arrays.first(parsedPatterns, pattern => !!(pattern).allBasenames); + const withBasenames = parsedPatterns.find(pattern => !!(pattern).allBasenames); if (withBasenames) { resultExpression.allBasenames = (withBasenames).allBasenames; } @@ -565,7 +564,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return resultExpression; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | Promise) { + const resultExpression: ParsedStringPattern = function (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise) { let name: string | undefined = undefined; for (let i = 0, n = parsedPatterns.length; i < n; i++) { @@ -588,7 +587,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return null; }; - const withBasenames = arrays.first(parsedPatterns, pattern => !!(pattern).allBasenames); + const withBasenames = parsedPatterns.find(pattern => !!(pattern).allBasenames); if (withBasenames) { resultExpression.allBasenames = (withBasenames).allBasenames; } @@ -620,12 +619,12 @@ function parseExpressionPattern(pattern: string, value: boolean | SiblingClause, if (value) { const when = (value).when; if (typeof when === 'string') { - const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | Promise) => { + const result: ParsedExpressionPattern = (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise) => { if (!hasSibling || !parsedPattern(path, basename)) { return null; } - const clausePattern = when.replace('$(basename)', name); + const clausePattern = when.replace('$(basename)', name!); const matched = hasSibling(clausePattern); return isThenable(matched) ? matched.then(m => m ? pattern : null) : diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 1902e82c312..13a1847e064 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -3,10 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as strings from 'vs/base/common/strings'; + /** * Return a hash value for an object. */ -export function hash(obj: any, hashVal = 0): number { +export function hash(obj: any): number { + return doHash(obj, 0); +} + +export function doHash(obj: any, hashVal: number): number { switch (typeof obj) { case 'object': if (obj === null) { @@ -22,9 +28,9 @@ export function hash(obj: any, hashVal = 0): number { case 'number': return numberHash(obj, hashVal); case 'undefined': - return numberHash(0, 937); + return numberHash(937, hashVal); default: - return numberHash(0, 617); + return numberHash(617, hashVal); } } @@ -46,14 +52,14 @@ export function stringHash(s: string, hashVal: number) { function arrayHash(arr: any[], initialHashVal: number): number { initialHashVal = numberHash(104579, initialHashVal); - return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal); + return arr.reduce((hashVal, item) => doHash(item, hashVal), initialHashVal); } function objectHash(obj: any, initialHashVal: number): number { initialHashVal = numberHash(181387, initialHashVal); return Object.keys(obj).sort().reduce((hashVal, key) => { hashVal = stringHash(key, hashVal); - return hash(obj[key], hashVal); + return doHash(obj[key], hashVal); }, initialHashVal); } @@ -66,7 +72,239 @@ export class Hasher { } hash(obj: any): number { - this._value = hash(obj, this._value); + this._value = doHash(obj, this._value); return this._value; } } + +const enum SHA1Constant { + BLOCK_SIZE = 64, // 512 / 8 + UNICODE_REPLACEMENT = 0xFFFD, +} + +function leftRotate(value: number, bits: number, totalBits: number = 32): number { + // delta + bits = totalBits + const delta = totalBits - bits; + + // All ones, expect `delta` zeros aligned to the right + const mask = ~((1 << delta) - 1); + + // Join (value left-shifted `bits` bits) with (masked value right-shifted `delta` bits) + return ((value << bits) | ((mask & value) >>> delta)) >>> 0; +} + +function fill(dest: Uint8Array, index: number = 0, count: number = dest.byteLength, value: number = 0): void { + for (let i = 0; i < count; i++) { + dest[index + i] = value; + } +} + +function leftPad(value: string, length: number, char: string = '0'): string { + while (value.length < length) { + value = char + value; + } + return value; +} + +function toHexString(value: number, bitsize: number = 32): string { + return leftPad((value >>> 0).toString(16), bitsize / 4); +} + +/** + * A SHA1 implementation that works with strings and does not allocate. + */ +export class StringSHA1 { + private static _bigBlock32 = new DataView(new ArrayBuffer(320)); // 80 * 4 = 320 + + private _h0 = 0x67452301; + private _h1 = 0xEFCDAB89; + private _h2 = 0x98BADCFE; + private _h3 = 0x10325476; + private _h4 = 0xC3D2E1F0; + + private readonly _buff: Uint8Array; + private readonly _buffDV: DataView; + private _buffLen: number; + private _totalLen: number; + private _leftoverHighSurrogate: number; + private _finished: boolean; + + constructor() { + this._buff = new Uint8Array(SHA1Constant.BLOCK_SIZE + 3 /* to fit any utf-8 */); + this._buffDV = new DataView(this._buff.buffer); + this._buffLen = 0; + this._totalLen = 0; + this._leftoverHighSurrogate = 0; + this._finished = false; + } + + public update(str: string): void { + const strLen = str.length; + if (strLen === 0) { + return; + } + + const buff = this._buff; + let buffLen = this._buffLen; + let leftoverHighSurrogate = this._leftoverHighSurrogate; + let charCode: number; + let offset: number; + + if (leftoverHighSurrogate !== 0) { + charCode = leftoverHighSurrogate; + offset = -1; + leftoverHighSurrogate = 0; + } else { + charCode = str.charCodeAt(0); + offset = 0; + } + + while (true) { + let codePoint = charCode; + if (strings.isHighSurrogate(charCode)) { + if (offset + 1 < strLen) { + const nextCharCode = str.charCodeAt(offset + 1); + if (strings.isLowSurrogate(nextCharCode)) { + offset++; + codePoint = strings.computeCodePoint(charCode, nextCharCode); + } else { + // illegal => unicode replacement character + codePoint = SHA1Constant.UNICODE_REPLACEMENT; + } + } else { + // last character is a surrogate pair + leftoverHighSurrogate = charCode; + break; + } + } else if (strings.isLowSurrogate(charCode)) { + // illegal => unicode replacement character + codePoint = SHA1Constant.UNICODE_REPLACEMENT; + } + + buffLen = this._push(buff, buffLen, codePoint); + offset++; + if (offset < strLen) { + charCode = str.charCodeAt(offset); + } else { + break; + } + } + + this._buffLen = buffLen; + this._leftoverHighSurrogate = leftoverHighSurrogate; + } + + private _push(buff: Uint8Array, buffLen: number, codePoint: number): number { + if (codePoint < 0x0080) { + buff[buffLen++] = codePoint; + } else if (codePoint < 0x0800) { + buff[buffLen++] = 0b11000000 | ((codePoint & 0b00000000000000000000011111000000) >>> 6); + buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0); + } else if (codePoint < 0x10000) { + buff[buffLen++] = 0b11100000 | ((codePoint & 0b00000000000000001111000000000000) >>> 12); + buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6); + buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0); + } else { + buff[buffLen++] = 0b11110000 | ((codePoint & 0b00000000000111000000000000000000) >>> 18); + buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000111111000000000000) >>> 12); + buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6); + buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0); + } + + if (buffLen >= SHA1Constant.BLOCK_SIZE) { + this._step(); + buffLen -= SHA1Constant.BLOCK_SIZE; + this._totalLen += SHA1Constant.BLOCK_SIZE; + // take last 3 in case of UTF8 overflow + buff[0] = buff[SHA1Constant.BLOCK_SIZE + 0]; + buff[1] = buff[SHA1Constant.BLOCK_SIZE + 1]; + buff[2] = buff[SHA1Constant.BLOCK_SIZE + 2]; + } + + return buffLen; + } + + public digest(): string { + if (!this._finished) { + this._finished = true; + if (this._leftoverHighSurrogate) { + // illegal => unicode replacement character + this._leftoverHighSurrogate = 0; + this._buffLen = this._push(this._buff, this._buffLen, SHA1Constant.UNICODE_REPLACEMENT); + } + this._totalLen += this._buffLen; + this._wrapUp(); + } + + return toHexString(this._h0) + toHexString(this._h1) + toHexString(this._h2) + toHexString(this._h3) + toHexString(this._h4); + } + + private _wrapUp(): void { + this._buff[this._buffLen++] = 0x80; + fill(this._buff, this._buffLen); + + if (this._buffLen > 56) { + this._step(); + fill(this._buff); + } + + // this will fit because the mantissa can cover up to 52 bits + const ml = 8 * this._totalLen; + + this._buffDV.setUint32(56, Math.floor(ml / 4294967296), false); + this._buffDV.setUint32(60, ml % 4294967296, false); + + this._step(); + } + + private _step(): void { + const bigBlock32 = StringSHA1._bigBlock32; + const data = this._buffDV; + + for (let j = 0; j < 64 /* 16*4 */; j += 4) { + bigBlock32.setUint32(j, data.getUint32(j, false), false); + } + + for (let j = 64; j < 320 /* 80*4 */; j += 4) { + bigBlock32.setUint32(j, leftRotate((bigBlock32.getUint32(j - 12, false) ^ bigBlock32.getUint32(j - 32, false) ^ bigBlock32.getUint32(j - 56, false) ^ bigBlock32.getUint32(j - 64, false)), 1), false); + } + + let a = this._h0; + let b = this._h1; + let c = this._h2; + let d = this._h3; + let e = this._h4; + + let f: number, k: number; + let temp: number; + + for (let j = 0; j < 80; j++) { + if (j < 20) { + f = (b & c) | ((~b) & d); + k = 0x5A827999; + } else if (j < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (j < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + + temp = (leftRotate(a, 5) + f + e + k + bigBlock32.getUint32(j * 4, false)) & 0xffffffff; + e = d; + d = c; + c = leftRotate(b, 30); + b = a; + a = temp; + } + + this._h0 = (this._h0 + a) & 0xffffffff; + this._h1 = (this._h1 + b) & 0xffffffff; + this._h2 = (this._h2 + c) & 0xffffffff; + this._h3 = (this._h3 + d) & 0xffffffff; + this._h4 = (this._h4 + e) & 0xffffffff; + } +} diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index fb146b969f5..018d498e5b8 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INavigator, ArrayNavigator } from 'vs/base/common/iterator'; +import { INavigator, ArrayNavigator } from 'vs/base/common/navigator'; export class HistoryNavigator implements INavigator { @@ -28,21 +28,23 @@ export class HistoryNavigator implements INavigator { } public next(): T | null { - return this._navigator.next(); + if (this._currentPosition() !== this._elements.length - 1) { + return this._navigator.next(); + } + return null; } public previous(): T | null { - return this._navigator.previous(); + if (this._currentPosition() !== 0) { + return this._navigator.previous(); + } + return null; } public current(): T | null { return this._navigator.current(); } - public parent(): null { - return null; - } - public first(): T | null { return this._navigator.first(); } @@ -73,6 +75,15 @@ export class HistoryNavigator implements INavigator { } } + private _currentPosition(): number { + const currentElement = this._navigator.current(); + if (!currentElement) { + return -1; + } + + return this._elements.indexOf(currentElement); + } + private _initialize(history: readonly T[]): void { this._history = new Set(); for (const entry of history) { @@ -86,3 +97,106 @@ export class HistoryNavigator implements INavigator { return elements; } } + +interface HistoryNode { + value: T; + previous: HistoryNode | undefined; + next: HistoryNode | undefined; +} + +export class HistoryNavigator2 { + + private head: HistoryNode; + private tail: HistoryNode; + private cursor: HistoryNode; + private size: number; + + constructor(history: readonly T[], private capacity: number = 10) { + if (history.length < 1) { + throw new Error('not supported'); + } + + this.size = 1; + this.head = this.tail = this.cursor = { + value: history[0], + previous: undefined, + next: undefined + }; + + for (let i = 1; i < history.length; i++) { + this.add(history[i]); + } + } + + add(value: T): void { + const node: HistoryNode = { + value, + previous: this.tail, + next: undefined + }; + + this.tail.next = node; + this.tail = node; + this.cursor = this.tail; + this.size++; + + while (this.size > this.capacity) { + this.head = this.head.next!; + this.head.previous = undefined; + this.size--; + } + } + + replaceLast(value: T): void { + this.tail.value = value; + } + + isAtEnd(): boolean { + return this.cursor === this.tail; + } + + current(): T { + return this.cursor.value; + } + + previous(): T { + if (this.cursor.previous) { + this.cursor = this.cursor.previous; + } + + return this.cursor.value; + } + + next(): T { + if (this.cursor.next) { + this.cursor = this.cursor.next; + } + + return this.cursor.value; + } + + has(t: T): boolean { + let temp: HistoryNode | undefined = this.head; + while (temp) { + if (temp.value === t) { + return true; + } + temp = temp.next; + } + return false; + } + + resetCursor(): T { + this.cursor = this.tail; + return this.cursor.value; + } + + *[Symbol.iterator](): Iterator { + let node: HistoryNode | undefined = this.head; + + while (node) { + yield node.value; + node = node.next; + } + } +} diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index be074865f2b..7272e813245 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -6,6 +6,7 @@ import { equals } from 'vs/base/common/arrays'; import { UriComponents } from 'vs/base/common/uri'; import { escapeCodicons } from 'vs/base/common/codicons'; +import { illegalArgument } from 'vs/base/common/errors'; export interface IMarkdownString { readonly value: string; @@ -14,50 +15,58 @@ export interface IMarkdownString { uris?: { [href: string]: UriComponents }; } +export const enum MarkdownStringTextNewlineStyle { + Paragraph = 0, + Break = 1, +} + export class MarkdownString implements IMarkdownString { - private readonly _isTrusted: boolean; - private readonly _supportThemeIcons: boolean; + + public value: string; + public isTrusted?: boolean; + public supportThemeIcons?: boolean; constructor( - private _value: string = '', + value: string = '', isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, ) { + this.value = value; + if (typeof this.value !== 'string') { + throw illegalArgument('value'); + } + if (typeof isTrustedOrOptions === 'boolean') { - this._isTrusted = isTrustedOrOptions; - this._supportThemeIcons = false; + this.isTrusted = isTrustedOrOptions; + this.supportThemeIcons = false; } else { - this._isTrusted = isTrustedOrOptions.isTrusted ?? false; - this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; + this.isTrusted = isTrustedOrOptions.isTrusted ?? undefined; + this.supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; } - } - get value() { return this._value; } - get isTrusted() { return this._isTrusted; } - get supportThemeIcons() { return this._supportThemeIcons; } - - appendText(value: string): MarkdownString { + appendText(value: string, newlineStyle: MarkdownStringTextNewlineStyle = MarkdownStringTextNewlineStyle.Paragraph): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this._value += (this._supportThemeIcons ? escapeCodicons(value) : value) + this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - .replace('\n', '\n\n'); + .replace(/^([ \t]+)(.+)$/gm, (_match, g1, g2) => ' '.repeat(g1.length) + g2) + .replace(/^>/gm, '\\>') + .replace(/\n/g, newlineStyle === MarkdownStringTextNewlineStyle.Break ? '\\\n' : '\n\n'); return this; } appendMarkdown(value: string): MarkdownString { - this._value += value; - + this.value += value; return this; } appendCodeblock(langId: string, code: string): MarkdownString { - this._value += '\n```'; - this._value += langId; - this._value += '\n'; - this._value += code; - this._value += '\n```\n'; + this.value += '\n```'; + this.value += langId; + this.value += '\n'; + this.value += code; + this.value += '\n```\n'; return this; } } diff --git a/src/vs/base/common/insane/insane.d.ts b/src/vs/base/common/insane/insane.d.ts index 9b5a77c3b8e..675c92a28d8 100644 --- a/src/vs/base/common/insane/insane.d.ts +++ b/src/vs/base/common/insane/insane.d.ts @@ -3,12 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +export interface InsaneOptions { + readonly allowedSchemes?: readonly string[], + readonly allowedTags?: readonly string[], + readonly allowedAttributes?: { readonly [key: string]: string[] }, + readonly filter?: (token: { tag: string, attrs: { readonly [key: string]: string } }) => boolean, +} + export function insane( html: string, - options?: { - readonly allowedSchemes?: readonly string[], - readonly allowedTags?: readonly string[], - readonly allowedAttributes?: { readonly [key: string]: string[] }, - }, + options?: InsaneOptions, strict?: boolean, ): string; diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 9287a6eaebf..d66882ac09f 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -3,297 +3,83 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export interface IteratorDefinedResult { - readonly done: false; - readonly value: T; -} -export interface IteratorUndefinedResult { - readonly done: true; - readonly value: undefined; -} -export const FIN: IteratorUndefinedResult = { done: true, value: undefined }; -export type IteratorResult = IteratorDefinedResult | IteratorUndefinedResult; +export namespace Iterable { -export interface Iterator { - next(): IteratorResult; -} + export function is(thing: any): thing is IterableIterator { + return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function'; + } -interface NativeIteratorYieldResult { - done?: false; - value: TYield; -} - -interface NativeIteratorReturnResult { - done: true; - value: TReturn; -} - -type NativeIteratorResult = NativeIteratorYieldResult | NativeIteratorReturnResult; - -export interface NativeIterator { - next(): NativeIteratorResult; -} - -export module Iterator { - const _empty: Iterator = { - next() { - return FIN; - } - }; - - export function empty(): Iterator { + const _empty: Iterable = Object.freeze([]); + export function empty(): Iterable { return _empty; } - export function single(value: T): Iterator { - let done = false; - - return { - next(): IteratorResult { - if (done) { - return FIN; - } - - done = true; - return { done: false, value }; - } - }; + export function* single(element: T): Iterable { + yield element; } - export function fromArray(array: ReadonlyArray, index = 0, length = array.length): Iterator { - return { - next(): IteratorResult { - if (index >= length) { - return FIN; - } - - return { done: false, value: array[index++] }; - } - }; + export function from(iterable: Iterable | undefined | null): Iterable { + return iterable || _empty; } - export function fromNativeIterator(it: NativeIterator): Iterator { - return { - next(): IteratorResult { - const result = it.next(); - - if (result.done) { - return FIN; - } - - return { done: false, value: result.value }; - } - }; + export function first(iterable: Iterable): T | undefined { + return iterable[Symbol.iterator]().next().value; } - export function from(elements: Iterator | T[] | undefined): Iterator { - if (!elements) { - return Iterator.empty(); - } else if (Array.isArray(elements)) { - return Iterator.fromArray(elements); - } else { - return elements; + export function some(iterable: Iterable, predicate: (t: T) => boolean): boolean { + for (const element of iterable) { + if (predicate(element)) { + return true; + } + } + return false; + } + + export function* filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable { + for (const element of iterable) { + if (predicate(element)) { + yield element; + } } } - export function map(iterator: Iterator, fn: (t: T) => R): Iterator { - return { - next() { - const element = iterator.next(); - if (element.done) { - return FIN; - } else { - return { done: false, value: fn(element.value) }; - } - } - }; - } - - export function filter(iterator: Iterator, fn: (t: T) => boolean): Iterator { - return { - next() { - while (true) { - const element = iterator.next(); - if (element.done) { - return FIN; - } - if (fn(element.value)) { - return { done: false, value: element.value }; - } - } - } - }; - } - - export function forEach(iterator: Iterator, fn: (t: T) => void): void { - for (let next = iterator.next(); !next.done; next = iterator.next()) { - fn(next.value); + export function* map(iterable: Iterable, fn: (t: T) => R): Iterable { + for (const element of iterable) { + yield fn(element); } } - export function collect(iterator: Iterator, atMost: number = Number.POSITIVE_INFINITY): T[] { - const result: T[] = []; + export function* concat(...iterables: Iterable[]): Iterable { + for (const iterable of iterables) { + for (const element of iterable) { + yield element; + } + } + } + + /** + * Consumes `atMost` elements from iterable and returns the consumed elements, + * and an iterable for the rest of the elements. + */ + export function consume(iterable: Iterable, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable] { + const consumed: T[] = []; if (atMost === 0) { - return result; + return [consumed, iterable]; } - let i = 0; + const iterator = iterable[Symbol.iterator](); - for (let next = iterator.next(); !next.done; next = iterator.next()) { - result.push(next.value); + for (let i = 0; i < atMost; i++) { + const next = iterator.next(); - if (++i >= atMost) { - break; + if (next.done) { + return [consumed, Iterable.empty()]; } + + consumed.push(next.value); } - return result; - } - - export function concat(...iterators: Iterator[]): Iterator { - let i = 0; - - return { - next() { - if (i >= iterators.length) { - return FIN; - } - - const iterator = iterators[i]; - const result = iterator.next(); - - if (result.done) { - i++; - return this.next(); - } - - return result; - } - }; - } - - export function chain(iterator: Iterator): ChainableIterator { - return new ChainableIterator(iterator); + return [consumed, { [Symbol.iterator]() { return iterator; } }]; } } - -export class ChainableIterator implements Iterator { - - constructor(private it: Iterator) { } - - next(): IteratorResult { return this.it.next(); } - map(fn: (t: T) => R): ChainableIterator { return new ChainableIterator(Iterator.map(this.it, fn)); } - filter(fn: (t: T) => boolean): ChainableIterator { return new ChainableIterator(Iterator.filter(this.it, fn)); } -} - -export type ISequence = Iterator | T[]; - -export function getSequenceIterator(arg: ISequence | undefined): Iterator { - if (Array.isArray(arg)) { - return Iterator.fromArray(arg); - } else if (!arg) { - return Iterator.empty(); - } else { - return arg; - } -} - -export interface INextIterator { - next(): T | null; -} - -export class ArrayIterator implements INextIterator { - - private readonly items: readonly T[]; - protected start: number; - protected end: number; - protected index: number; - - constructor(items: readonly T[], start: number = 0, end: number = items.length, index = start - 1) { - this.items = items; - this.start = start; - this.end = end; - this.index = index; - } - - public first(): T | null { - this.index = this.start; - return this.current(); - } - - public next(): T | null { - this.index = Math.min(this.index + 1, this.end); - return this.current(); - } - - protected current(): T | null { - if (this.index === this.start - 1 || this.index === this.end) { - return null; - } - - return this.items[this.index]; - } -} - -export class ArrayNavigator extends ArrayIterator implements INavigator { - - constructor(items: readonly T[], start: number = 0, end: number = items.length, index = start - 1) { - super(items, start, end, index); - } - - public current(): T | null { - return super.current(); - } - - public previous(): T | null { - this.index = Math.max(this.index - 1, this.start - 1); - return this.current(); - } - - public first(): T | null { - this.index = this.start; - return this.current(); - } - - public last(): T | null { - this.index = this.end - 1; - return this.current(); - } - - public parent(): T | null { - return null; - } -} - -export class MappedIterator implements INextIterator { - - constructor(protected iterator: INextIterator, protected fn: (item: T | null) => R) { - // noop - } - - next() { return this.fn(this.iterator.next()); } -} - -export interface INavigator extends INextIterator { - current(): T | null; - previous(): T | null; - parent(): T | null; - first(): T | null; - last(): T | null; - next(): T | null; -} - -export class MappedNavigator extends MappedIterator implements INavigator { - - constructor(protected navigator: INavigator, fn: (item: T | null) => R) { - super(navigator, fn); - } - - current() { return this.fn(this.navigator.current()); } - previous() { return this.fn(this.navigator.previous()); } - parent() { return this.fn(this.navigator.parent()); } - first() { return this.fn(this.navigator.first()); } - last() { return this.fn(this.navigator.last()); } - next() { return this.fn(this.navigator.next()); } -} diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index df362c06589..933e370f92c 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -72,6 +72,7 @@ export interface JSONScanner { } + export interface ParseError { error: ParseErrorCode; offset: number; diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 7fd05e5d2e1..a16f4457a1c 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -175,7 +175,3 @@ export function applyEdits(text: string, edits: Edit[]): string { } return text; } - -export function isWS(text: string, offset: number) { - return '\r\n \t'.indexOf(text.charAt(offset)) !== -1; -} diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index 3af5da70367..da614983689 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -53,17 +53,19 @@ export interface IJSONSchema { then?: IJSONSchema; else?: IJSONSchema; - // VSCode extensions - defaultSnippets?: IJSONSchemaSnippet[]; // VSCode extension - errorMessage?: string; // VSCode extension - patternErrorMessage?: string; // VSCode extension - deprecationMessage?: string; // VSCode extension - enumDescriptions?: string[]; // VSCode extension - markdownEnumDescriptions?: string[]; // VSCode extension - markdownDescription?: string; // VSCode extension - doNotSuggest?: boolean; // VSCode extension - allowComments?: boolean; // VSCode extension - allowTrailingCommas?: boolean; // VSCode extension + // VS Code extensions + defaultSnippets?: IJSONSchemaSnippet[]; + errorMessage?: string; + patternErrorMessage?: string; + deprecationMessage?: string; + markdownDeprecationMessage?: string; + enumDescriptions?: string[]; + markdownEnumDescriptions?: string[]; + markdownDescription?: string; + doNotSuggest?: boolean; + suggestSortText?: string; + allowComments?: boolean; + allowTrailingCommas?: boolean; } export interface IJSONSchemaMap { diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index 671816830bd..1cdbc8d8e9e 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -182,7 +182,9 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe } // the actual key - result.push(key); + if (key !== '') { + result.push(key); + } return result.join(labels.separator); } diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 86a05d80658..5f3fab86490 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { sep, posix, normalize, win32 } from 'vs/base/common/path'; -import { endsWith, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings'; +import { posix, normalize, win32, sep } from 'vs/base/common/path'; +import { startsWithIgnoreCase, rtrim } from 'vs/base/common/strings'; import { Schemas } from 'vs/base/common/network'; import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform'; import { isEqual, basename, relativePath } from 'vs/base/common/resources'; @@ -18,7 +18,7 @@ export interface IWorkspaceFolderProvider { } export interface IUserHomeProvider { - userHome: string; + userHome?: URI; } /** @@ -63,8 +63,8 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom // normalize and tildify (macOS, Linux only) let res = normalize(resource.fsPath); - if (!isWindows && userHomeProvider) { - res = tildify(res, userHomeProvider.userHome); + if (!isWindows && userHomeProvider?.userHome) { + res = tildify(res, userHomeProvider.userHome.fsPath); } return res; @@ -117,7 +117,7 @@ export function tildify(path: string, userHome: string): string { } // Linux: case sensitive, macOS: case insensitive - if (isLinux ? startsWith(path, normalizedUserHome) : startsWithIgnoreCase(path, normalizedUserHome)) { + if (isLinux ? path.startsWith(normalizedUserHome) : startsWithIgnoreCase(path, normalizedUserHome)) { path = `~/${path.substr(normalizedUserHome.length)}`; } @@ -160,7 +160,7 @@ export function untildify(path: string, userHome: string): string { const ellipsis = '\u2026'; const unc = '\\\\'; const home = '~'; -export function shorten(paths: string[]): string[] { +export function shorten(paths: string[], pathSeparator: string = sep): string[] { const shortenedPaths: string[] = new Array(paths.length); // for every path @@ -169,7 +169,7 @@ export function shorten(paths: string[]): string[] { let path = paths[pathIndex]; if (path === '') { - shortenedPaths[pathIndex] = `.${sep}`; + shortenedPaths[pathIndex] = `.${pathSeparator}`; continue; } @@ -185,20 +185,20 @@ export function shorten(paths: string[]): string[] { if (path.indexOf(unc) === 0) { prefix = path.substr(0, path.indexOf(unc) + unc.length); path = path.substr(path.indexOf(unc) + unc.length); - } else if (path.indexOf(sep) === 0) { - prefix = path.substr(0, path.indexOf(sep) + sep.length); - path = path.substr(path.indexOf(sep) + sep.length); + } else if (path.indexOf(pathSeparator) === 0) { + prefix = path.substr(0, path.indexOf(pathSeparator) + pathSeparator.length); + path = path.substr(path.indexOf(pathSeparator) + pathSeparator.length); } else if (path.indexOf(home) === 0) { prefix = path.substr(0, path.indexOf(home) + home.length); path = path.substr(path.indexOf(home) + home.length); } // pick the first shortest subpath found - const segments: string[] = path.split(sep); + const segments: string[] = path.split(pathSeparator); for (let subpathLength = 1; match && subpathLength <= segments.length; subpathLength++) { for (let start = segments.length - subpathLength; match && start >= 0; start--) { match = false; - let subpath = segments.slice(start, start + subpathLength).join(sep); + let subpath = segments.slice(start, start + subpathLength).join(pathSeparator); // that is unique to any other path for (let otherPathIndex = 0; !match && otherPathIndex < paths.length; otherPathIndex++) { @@ -209,8 +209,8 @@ export function shorten(paths: string[]): string[] { // Adding separator as prefix for subpath, such that 'endsWith(src, trgt)' considers subpath as directory name instead of plain string. // prefix is not added when either subpath is root directory or path[otherPathIndex] does not have multiple directories. - const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(sep) > -1) ? sep + subpath : subpath; - const isOtherPathEnding: boolean = endsWith(paths[otherPathIndex], subpathWithSep); + const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(pathSeparator) > -1) ? pathSeparator + subpath : subpath; + const isOtherPathEnding: boolean = paths[otherPathIndex].endsWith(subpathWithSep); match = !isSubpathEnding || isOtherPathEnding; } @@ -221,16 +221,16 @@ export function shorten(paths: string[]): string[] { let result = ''; // preserve disk drive or root prefix - if (endsWith(segments[0], ':') || prefix !== '') { + if (segments[0].endsWith(':') || prefix !== '') { if (start === 1) { // extend subpath to include disk drive prefix start = 0; subpathLength++; - subpath = segments[0] + sep + subpath; + subpath = segments[0] + pathSeparator + subpath; } if (start > 0) { - result = segments[0] + sep; + result = segments[0] + pathSeparator; } result = prefix + result; @@ -238,14 +238,14 @@ export function shorten(paths: string[]): string[] { // add ellipsis at the beginning if neeeded if (start > 0) { - result = result + ellipsis + sep; + result = result + ellipsis + pathSeparator; } result = result + subpath; // add ellipsis at the end if needed if (start + subpathLength < segments.length) { - result = result + sep + ellipsis; + result = result + pathSeparator + ellipsis; } shortenedPaths[pathIndex] = result; diff --git a/src/vs/base/common/lazy.ts b/src/vs/base/common/lazy.ts index 7f1bef48d65..ce6339106ef 100644 --- a/src/vs/base/common/lazy.ts +++ b/src/vs/base/common/lazy.ts @@ -21,7 +21,7 @@ export class Lazy { private _didRun: boolean = false; private _value?: T; - private _error: any; + private _error: Error | undefined; constructor( private readonly executor: () => T, diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 06e3746750c..79bbb910aac 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { once } from 'vs/base/common/functional'; +import { Iterable } from 'vs/base/common/iterator'; /** * Enables logging of potentially leaked disposables. @@ -44,37 +45,57 @@ function trackDisposable(x: T): T { return x; } +export class MultiDisposeError extends Error { + constructor( + public readonly errors: any[] + ) { + super(`Encounter errors while disposing of store. Errors: [${errors.join(', ')}]`); + } +} + export interface IDisposable { dispose(): void; } export function isDisposable(thing: E): thing is E & IDisposable { - return typeof (thing).dispose === 'function' - && (thing).dispose.length === 0; + return typeof (thing).dispose === 'function' && (thing).dispose.length === 0; } export function dispose(disposable: T): T; export function dispose(disposable: T | undefined): T | undefined; +export function dispose = IterableIterator>(disposables: IterableIterator): A; export function dispose(disposables: Array): Array; export function dispose(disposables: ReadonlyArray): ReadonlyArray; -export function dispose(disposables: T | T[] | undefined): T | T[] | undefined { - if (Array.isArray(disposables)) { - disposables.forEach(d => { +export function dispose(arg: T | IterableIterator | undefined): any { + if (Iterable.is(arg)) { + let errors: any[] = []; + + for (const d of arg) { if (d) { markTracked(d); - d.dispose(); + try { + d.dispose(); + } catch (e) { + errors.push(e); + } } - }); - return []; - } else if (disposables) { - markTracked(disposables); - disposables.dispose(); - return disposables; - } else { - return undefined; + } + + if (errors.length === 1) { + throw errors[0]; + } else if (errors.length > 1) { + throw new MultiDisposeError(errors); + } + + return Array.isArray(arg) ? [] : arg; + } else if (arg) { + markTracked(arg); + arg.dispose(); + return arg; } } + export function combinedDisposable(...disposables: IDisposable[]): IDisposable { disposables.forEach(markTracked); return trackDisposable({ dispose: () => dispose(disposables) }); @@ -91,6 +112,9 @@ export function toDisposable(fn: () => void): IDisposable { } export class DisposableStore implements IDisposable { + + static DISABLE_DISPOSED_WARNING = false; + private _toDispose = new Set(); private _isDisposed = false; @@ -113,21 +137,26 @@ export class DisposableStore implements IDisposable { * Dispose of all registered disposables but do not mark this object as disposed. */ public clear(): void { - this._toDispose.forEach(item => item.dispose()); - this._toDispose.clear(); + try { + dispose(this._toDispose.values()); + } finally { + this._toDispose.clear(); + } } public add(t: T): T { if (!t) { return t; } - if ((t as any as DisposableStore) === this) { + if ((t as unknown as DisposableStore) === this) { throw new Error('Cannot register a disposable on itself!'); } markTracked(t); if (this._isDisposed) { - console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack); + if (!DisposableStore.DISABLE_DISPOSED_WARNING) { + console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack); + } } else { this._toDispose.add(t); } @@ -153,7 +182,7 @@ export abstract class Disposable implements IDisposable { } protected _register(t: T): T { - if ((t as any as Disposable) === this) { + if ((t as unknown as Disposable) === this) { throw new Error('Cannot register a disposable on itself!'); } return this._store.add(t); @@ -214,11 +243,11 @@ export abstract class ReferenceCollection { private readonly references: Map = new Map(); - acquire(key: string): IReference { + acquire(key: string, ...args: any[]): IReference { let reference = this.references.get(key); if (!reference) { - reference = { counter: 0, object: this.createReferencedObject(key) }; + reference = { counter: 0, object: this.createReferencedObject(key, ...args) }; this.references.set(key, reference); } @@ -235,7 +264,7 @@ export abstract class ReferenceCollection { return { object, dispose }; } - protected abstract createReferencedObject(key: string): T; + protected abstract createReferencedObject(key: string, ...args: any[]): T; protected abstract destroyReferencedObject(key: string, object: T): void; } diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 54bb9253de6..8ca17bc73f9 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Iterator, IteratorResult, FIN } from 'vs/base/common/iterator'; - class Node { static readonly Undefined = new Node(undefined); @@ -126,24 +124,12 @@ export class LinkedList { this._size -= 1; } - iterator(): Iterator { - let element: { done: false; value: E; }; + *[Symbol.iterator](): Iterator { let node = this._first; - return { - next(): IteratorResult { - if (node === Node.Undefined) { - return FIN; - } - - if (!element) { - element = { done: false, value: node.element }; - } else { - element.value = node.element; - } - node = node.next; - return element; - } - }; + while (node !== Node.Undefined) { + yield node.element; + node = node.next; + } } toArray(): E[] { diff --git a/src/vs/base/common/linkedText.ts b/src/vs/base/common/linkedText.ts new file mode 100644 index 00000000000..5f6c6a05dcf --- /dev/null +++ b/src/vs/base/common/linkedText.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { memoize } from 'vs/base/common/decorators'; + +export interface ILink { + readonly label: string; + readonly href: string; + readonly title?: string; +} + +export type LinkedTextNode = string | ILink; + +export class LinkedText { + + constructor(readonly nodes: LinkedTextNode[]) { } + + @memoize + toString(): string { + return this.nodes.map(node => typeof node === 'string' ? node : node.label).join(''); + } +} + +const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: ("|')([^\3]+)(\3))?\)/gi; + +export function parseLinkedText(text: string): LinkedText { + const result: LinkedTextNode[] = []; + + let index = 0; + let match: RegExpExecArray | null; + + while (match = LINK_REGEX.exec(text)) { + if (match.index - index > 0) { + result.push(text.substring(index, match.index)); + } + + const [, label, href, , title] = match; + + if (title) { + result.push({ label, href, title }); + } else { + result.push({ label, href }); + } + + index = match.index + match[0].length; + } + + if (index < text.length) { + result.push(text.substring(index)); + } + + return new LinkedText(result); +} diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 4f6b55c3fe5..39b03f06fd6 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -5,23 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; -import { Iterator, IteratorResult, FIN } from './iterator'; - - -export function values(set: Set): V[]; -export function values(map: Map): V[]; -export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] { - const result: V[] = []; - forEachable.forEach(value => result.push(value)); - return result; -} - -export function keys(map: Map): K[] { - const result: K[] = []; - map.forEach((_value, key) => result.push(key)); - - return result; -} +import { compareSubstringIgnoreCase, compare, compareSubstring, compareIgnoreCase } from 'vs/base/common/strings'; export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); @@ -51,28 +35,8 @@ export function setToString(set: Set): string { return `Set(${set.size}) {${entries.join(', ')}}`; } -export function mapToSerializable(map: Map): [string, string][] { - const serializable: [string, string][] = []; - - map.forEach((value, key) => { - serializable.push([key, value]); - }); - - return serializable; -} - -export function serializableToMap(serializable: [string, string][]): Map { - const items = new Map(); - - for (const [key, value] of serializable) { - items.set(key, value); - } - - return items; -} - -export interface IKeyIterator { - reset(key: string): this; +export interface IKeyIterator { + reset(key: K): this; next(): this; hasNext(): boolean; @@ -80,7 +44,7 @@ export interface IKeyIterator { value(): string; } -export class StringIterator implements IKeyIterator { +export class StringIterator implements IKeyIterator { private _value: string = ''; private _pos: number = 0; @@ -111,13 +75,16 @@ export class StringIterator implements IKeyIterator { } } -export class PathIterator implements IKeyIterator { +export class PathIterator implements IKeyIterator { private _value!: string; private _from!: number; private _to!: number; - constructor(private _splitOnBackslash: boolean = true) { } + constructor( + private readonly _splitOnBackslash: boolean = true, + private readonly _caseSensitive: boolean = true + ) { } reset(key: string): this { this._value = key.replace(/\\$|\/$/, ''); @@ -150,27 +117,9 @@ export class PathIterator implements IKeyIterator { } cmp(a: string): number { - - let aPos = 0; - const aLen = a.length; - let thisPos = this._from; - - while (aPos < aLen && thisPos < this._to) { - const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); - if (cmp !== 0) { - return cmp; - } - aPos += 1; - thisPos += 1; - } - - if (aLen === this._to - this._from) { - return 0; - } else if (aPos < aLen) { - return -1; - } else { - return 1; - } + return this._caseSensitive + ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) + : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to); } value(): string { @@ -178,33 +127,121 @@ export class PathIterator implements IKeyIterator { } } -class TernarySearchTreeNode { +const enum UriIteratorState { + Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5 +} + +export class UriIterator implements IKeyIterator { + + private _pathIterator!: PathIterator; + private _value!: URI; + private _states: UriIteratorState[] = []; + private _stateIdx: number = 0; + + constructor(private readonly _ignorePathCasing: boolean) { } + + reset(key: URI): this { + this._value = key; + this._states = []; + if (this._value.scheme) { + this._states.push(UriIteratorState.Scheme); + } + if (this._value.authority) { + this._states.push(UriIteratorState.Authority); + } + if (this._value.path) { + this._pathIterator = new PathIterator(false, !this._ignorePathCasing); + this._pathIterator.reset(key.path); + if (this._pathIterator.value()) { + this._states.push(UriIteratorState.Path); + } + } + if (this._value.query) { + this._states.push(UriIteratorState.Query); + } + if (this._value.fragment) { + this._states.push(UriIteratorState.Fragment); + } + this._stateIdx = 0; + return this; + } + + next(): this { + if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) { + this._pathIterator.next(); + } else { + this._stateIdx += 1; + } + return this; + } + + hasNext(): boolean { + return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) + || this._stateIdx < this._states.length - 1; + } + + cmp(a: string): number { + if (this._states[this._stateIdx] === UriIteratorState.Scheme) { + return compareIgnoreCase(a, this._value.scheme); + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return compareIgnoreCase(a, this._value.authority); + } else if (this._states[this._stateIdx] === UriIteratorState.Path) { + return this._pathIterator.cmp(a); + } else if (this._states[this._stateIdx] === UriIteratorState.Query) { + return compare(a, this._value.query); + } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) { + return compare(a, this._value.fragment); + } + throw new Error(); + } + + value(): string { + if (this._states[this._stateIdx] === UriIteratorState.Scheme) { + return this._value.scheme; + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return this._value.authority; + } else if (this._states[this._stateIdx] === UriIteratorState.Path) { + return this._pathIterator.value(); + } else if (this._states[this._stateIdx] === UriIteratorState.Query) { + return this._value.query; + } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) { + return this._value.fragment; + } + throw new Error(); + } +} + +class TernarySearchTreeNode { segment!: string; - value: E | undefined; - key!: string; - left: TernarySearchTreeNode | undefined; - mid: TernarySearchTreeNode | undefined; - right: TernarySearchTreeNode | undefined; + value: V | undefined; + key!: K; + left: TernarySearchTreeNode | undefined; + mid: TernarySearchTreeNode | undefined; + right: TernarySearchTreeNode | undefined; isEmpty(): boolean { return !this.left && !this.mid && !this.right && !this.value; } } -export class TernarySearchTree { +export class TernarySearchTree { - static forPaths(): TernarySearchTree { - return new TernarySearchTree(new PathIterator()); + static forUris(ignorePathCasing: boolean = false): TernarySearchTree { + return new TernarySearchTree(new UriIterator(ignorePathCasing)); } - static forStrings(): TernarySearchTree { - return new TernarySearchTree(new StringIterator()); + static forPaths(): TernarySearchTree { + return new TernarySearchTree(new PathIterator()); } - private _iter: IKeyIterator; - private _root: TernarySearchTreeNode | undefined; + static forStrings(): TernarySearchTree { + return new TernarySearchTree(new StringIterator()); + } - constructor(segments: IKeyIterator) { + private _iter: IKeyIterator; + private _root: TernarySearchTreeNode | undefined; + + constructor(segments: IKeyIterator) { this._iter = segments; } @@ -212,12 +249,12 @@ export class TernarySearchTree { this._root = undefined; } - set(key: string, element: E): E | undefined { + set(key: K, element: V): V | undefined { const iter = this._iter.reset(key); - let node: TernarySearchTreeNode; + let node: TernarySearchTreeNode; if (!this._root) { - this._root = new TernarySearchTreeNode(); + this._root = new TernarySearchTreeNode(); this._root.segment = iter.value(); } @@ -227,7 +264,7 @@ export class TernarySearchTree { if (val > 0) { // left if (!node.left) { - node.left = new TernarySearchTreeNode(); + node.left = new TernarySearchTreeNode(); node.left.segment = iter.value(); } node = node.left; @@ -235,7 +272,7 @@ export class TernarySearchTree { } else if (val < 0) { // right if (!node.right) { - node.right = new TernarySearchTreeNode(); + node.right = new TernarySearchTreeNode(); node.right.segment = iter.value(); } node = node.right; @@ -244,7 +281,7 @@ export class TernarySearchTree { // mid iter.next(); if (!node.mid) { - node.mid = new TernarySearchTreeNode(); + node.mid = new TernarySearchTreeNode(); node.mid.segment = iter.value(); } node = node.mid; @@ -258,7 +295,7 @@ export class TernarySearchTree { return oldElement; } - get(key: string): E | undefined { + get(key: K): V | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -280,10 +317,17 @@ export class TernarySearchTree { return node ? node.value : undefined; } - delete(key: string): void { + delete(key: K): void { + return this._delete(key, false); + } + deleteSuperstr(key: K): void { + return this._delete(key, true); + } + + private _delete(key: K, superStr: boolean): void { const iter = this._iter.reset(key); - const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; + const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; let node = this._root; // find and unset node @@ -307,7 +351,7 @@ export class TernarySearchTree { node.value = undefined; // clean up empty nodes - while (stack.length > 0 && node.isEmpty()) { + while (stack.length > 0 && (node.isEmpty() || superStr)) { let [dir, parent] = stack.pop()!; switch (dir) { case 1: parent.left = undefined; break; @@ -321,10 +365,10 @@ export class TernarySearchTree { } } - findSubstr(key: string): E | undefined { + findSubstr(key: K): V | undefined { const iter = this._iter.reset(key); let node = this._root; - let candidate: E | undefined = undefined; + let candidate: V | undefined = undefined; while (node) { const val = iter.cmp(node.segment); if (val > 0) { @@ -345,7 +389,7 @@ export class TernarySearchTree { return node && node.value || candidate; } - findSuperstr(key: string): Iterator | undefined { + findSuperstr(key: K): Iterator | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -365,73 +409,87 @@ export class TernarySearchTree { if (!node.mid) { return undefined; } else { - return this._nodeIterator(node.mid); + return this._values(node.mid); } } } return undefined; } - private _nodeIterator(node: TernarySearchTreeNode): Iterator { - let res: { done: false; value: E; }; - let idx: number; - let data: E[]; - const next = (): IteratorResult => { - if (!data) { - // lazy till first invocation - data = []; - idx = 0; - this._forEach(node, value => data.push(value)); - } - if (idx >= data.length) { - return FIN; - } - - if (!res) { - res = { done: false, value: data[idx++] }; - } else { - res.value = data[idx++]; - } - return res; - }; - return { next }; + forEach(callback: (value: V, index: K) => any): void { + for (const [key, value] of this) { + callback(value, key); + } } - forEach(callback: (value: E, index: string) => any) { - this._forEach(this._root, callback); + *[Symbol.iterator](): IterableIterator<[K, V]> { + yield* this._entries(this._root); } - private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: E, index: string) => any) { + private *_values(node: TernarySearchTreeNode): IterableIterator { + for (const [, value] of this._entries(node)) { + yield value; + } + } + + private *_entries(node: TernarySearchTreeNode | undefined): IterableIterator<[K, V]> { if (node) { // left - this._forEach(node.left, callback); + yield* this._entries(node.left); // node if (node.value) { // callback(node.value, this._iter.join(parts)); - callback(node.value, node.key); + yield [node.key, node.value]; } // mid - this._forEach(node.mid, callback); + yield* this._entries(node.mid); // right - this._forEach(node.right, callback); + yield* this._entries(node.right); } } } -export class ResourceMap { +interface ResourceMapKeyFn { + (resource: URI): string; +} - protected readonly map: Map; - protected readonly ignoreCase?: boolean; +export class ResourceMap implements Map { - constructor() { - this.map = new Map(); - this.ignoreCase = false; // in the future this should be an uri-comparator + private static readonly defaultToKey = (resource: URI) => resource.toString(); + + readonly [Symbol.toStringTag] = 'ResourceMap'; + + private readonly map: Map; + private readonly toKey: ResourceMapKeyFn; + + /** + * + * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util + */ + constructor(toKey?: ResourceMapKeyFn); + + /** + * + * @param other Another resource which this maps is created from + * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util + */ + constructor(other?: ResourceMap, toKey?: ResourceMapKeyFn); + + constructor(mapOrKeyFn?: ResourceMap | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) { + if (mapOrKeyFn instanceof ResourceMap) { + this.map = new Map(mapOrKeyFn.map); + this.toKey = toKey ?? ResourceMap.defaultToKey; + } else { + this.map = new Map(); + this.toKey = mapOrKeyFn ?? ResourceMap.defaultToKey; + } } - set(resource: URI, value: T): void { + set(resource: URI, value: T): this { this.map.set(this.toKey(resource), value); + return this; } get(resource: URI): T | undefined { @@ -454,33 +512,35 @@ export class ResourceMap { return this.map.delete(this.toKey(resource)); } - forEach(clb: (value: T, key: URI) => void): void { - this.map.forEach((value, index) => clb(value, URI.parse(index))); - } - - values(): T[] { - return values(this.map); - } - - private toKey(resource: URI): string { - let key = resource.toString(); - if (this.ignoreCase) { - key = key.toLowerCase(); + forEach(clb: (value: T, key: URI, map: Map) => void, thisArg?: any): void { + if (typeof thisArg !== 'undefined') { + clb = clb.bind(thisArg); + } + for (let [index, value] of this.map) { + clb(value, URI.parse(index), this); } - - return key; } - keys(): URI[] { - return keys(this.map).map(k => URI.parse(k)); + values(): IterableIterator { + return this.map.values(); } - clone(): ResourceMap { - const resourceMap = new ResourceMap(); + *keys(): IterableIterator { + for (let key of this.map.keys()) { + yield URI.parse(key); + } + } - this.map.forEach((value, key) => resourceMap.map.set(key, value)); + *entries(): IterableIterator<[URI, T]> { + for (let tuple of this.map.entries()) { + yield [URI.parse(tuple[0]), tuple[1]]; + } + } - return resourceMap; + *[Symbol.iterator](): IterableIterator<[URI, T]> { + for (let item of this.map) { + yield [URI.parse(item[0]), item[1]]; + } } } @@ -497,18 +557,23 @@ export const enum Touch { AsNew = 2 } -export class LinkedMap { +export class LinkedMap implements Map { + + readonly [Symbol.toStringTag] = 'LinkedMap'; private _map: Map>; private _head: Item | undefined; private _tail: Item | undefined; private _size: number; + private _state: number; + constructor() { this._map = new Map>(); this._head = undefined; this._tail = undefined; this._size = 0; + this._state = 0; } clear(): void { @@ -516,6 +581,7 @@ export class LinkedMap { this._head = undefined; this._tail = undefined; this._size = 0; + this._state++; } isEmpty(): boolean { @@ -549,7 +615,7 @@ export class LinkedMap { return item.value; } - set(key: K, value: V, touch: Touch = Touch.None): void { + set(key: K, value: V, touch: Touch = Touch.None): this { let item = this._map.get(key); if (item) { item.value = value; @@ -575,6 +641,7 @@ export class LinkedMap { this._map.set(key, item); this._size++; } + return this; } delete(key: K): boolean { @@ -607,6 +674,7 @@ export class LinkedMap { } forEach(callbackfn: (value: V, key: K, map: LinkedMap) => void, thisArg?: any): void { + const state = this._state; let current = this._head; while (current) { if (thisArg) { @@ -614,38 +682,25 @@ export class LinkedMap { } else { callbackfn(current.value, current.key, this); } + if (this._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } current = current.next; } } - values(): V[] { - const result: V[] = []; - let current = this._head; - while (current) { - result.push(current.value); - current = current.next; - } - return result; - } - - keys(): K[] { - const result: K[] = []; - let current = this._head; - while (current) { - result.push(current.key); - current = current.next; - } - return result; - } - - /* VS Code / Monaco editor runs on es5 which has no Symbol.iterator keys(): IterableIterator { - const current = this._head; + const map = this; + const state = this._state; + let current = this._head; const iterator: IterableIterator = { [Symbol.iterator]() { return iterator; }, - next():IteratorResult { + next(): IteratorResult { + if (map._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } if (current) { const result = { value: current.key, done: false }; current = current.next; @@ -659,12 +714,17 @@ export class LinkedMap { } values(): IterableIterator { - const current = this._head; + const map = this; + const state = this._state; + let current = this._head; const iterator: IterableIterator = { [Symbol.iterator]() { return iterator; }, - next():IteratorResult { + next(): IteratorResult { + if (map._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } if (current) { const result = { value: current.value, done: false }; current = current.next; @@ -676,7 +736,34 @@ export class LinkedMap { }; return iterator; } - */ + + entries(): IterableIterator<[K, V]> { + const map = this; + const state = this._state; + let current = this._head; + const iterator: IterableIterator<[K, V]> = { + [Symbol.iterator]() { + return iterator; + }, + next(): IteratorResult<[K, V]> { + if (map._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } + if (current) { + const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false }; + current = current.next; + return result; + } else { + return { value: undefined, done: true }; + } + } + }; + return iterator; + } + + [Symbol.iterator](): IterableIterator<[K, V]> { + return this.entries(); + } protected trimOld(newSize: number) { if (newSize >= this.size) { @@ -698,6 +785,7 @@ export class LinkedMap { if (current) { current.previous = undefined; } + this._state++; } private addItemFirst(item: Item): void { @@ -711,6 +799,7 @@ export class LinkedMap { this._head.previous = item; } this._head = item; + this._state++; } private addItemLast(item: Item): void { @@ -724,6 +813,7 @@ export class LinkedMap { this._tail.next = item; } this._tail = item; + this._state++; } private removeItem(item: Item): void { @@ -760,6 +850,7 @@ export class LinkedMap { } item.next = undefined; item.previous = undefined; + this._state++; } private touch(item: Item, touch: Touch): void { @@ -796,6 +887,7 @@ export class LinkedMap { item.next = this._head; this._head.previous = item; this._head = item; + this._state++; } else if (touch === Touch.AsNew) { if (item === this._tail) { return; @@ -819,6 +911,7 @@ export class LinkedMap { item.previous = this._tail; this._tail.next = item; this._tail = item; + this._state++; } } @@ -870,17 +963,18 @@ export class LRUCache extends LinkedMap { this.checkTrim(); } - get(key: K): V | undefined { - return super.get(key, Touch.AsNew); + get(key: K, touch: Touch = Touch.AsNew): V | undefined { + return super.get(key, touch); } peek(key: K): V | undefined { return super.get(key, Touch.None); } - set(key: K, value: V): void { + set(key: K, value: V): this { super.set(key, value, Touch.AsNew); this.checkTrim(); + return this; } private checkTrim() { diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index 1288f459647..ce8d1641037 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -1,1714 +1,2700 @@ /** * marked - a markdown parser - * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) + * Copyright (c) 2011-2020, Christopher Jeffrey. (MIT Licensed) * https://github.com/markedjs/marked */ -// BEGIN MONACOCHANGE -var __marked_exports; -// END MONACOCHANGE - -;(function(root) { -'use strict'; - /** - * Block-Level Grammar + * DO NOT EDIT THIS FILE + * The code in this file is generated from files in ./src/ */ -var block = { - newline: /^\n+/, - code: /^( {4}[^\n]+\n*)+/, - fences: noop, - hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, - heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, - nptable: noop, - blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, - html: '^ {0,3}(?:' // optional indentation - + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) - + '|comment[^\\n]*(\\n+|$)' // (2) - + '|<\\?[\\s\\S]*?\\?>\\n*' // (3) - + '|\\n*' // (4) - + '|\\n*' // (5) - + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) - + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag - + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag - + ')', - def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - table: noop, - lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, - paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/, - text: /^[^\n]+/ -}; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.marked = factory()); +}(this, (function () { 'use strict'; -block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; -block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; -block.def = edit(block.def) - .replace('label', block._label) - .replace('title', block._title) - .getRegex(); - -block.bullet = /(?:[*+-]|\d{1,9}\.)/; -block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/; -block.item = edit(block.item, 'gm') - .replace(/bull/g, block.bullet) - .getRegex(); - -block.list = edit(block.list) - .replace(/bull/g, block.bullet) - .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') - .replace('def', '\\n+(?=' + block.def.source + ')') - .getRegex(); - -block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' - + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' - + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' - + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' - + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' - + '|track|ul'; -block._comment = //; -block.html = edit(block.html, 'i') - .replace('comment', block._comment) - .replace('tag', block._tag) - .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) - .getRegex(); - -block.paragraph = edit(block.paragraph) - .replace('hr', block.hr) - .replace('heading', block.heading) - .replace('lheading', block.lheading) - .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks - .getRegex(); - -block.blockquote = edit(block.blockquote) - .replace('paragraph', block.paragraph) - .getRegex(); - -/** - * Normal Block Grammar - */ - -block.normal = merge({}, block); - -/** - * GFM Block Grammar - */ - -block.gfm = merge({}, block.normal, { - fences: /^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, - paragraph: /^/, - heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ -}); - -block.gfm.paragraph = edit(block.paragraph) - .replace('(?!', '(?!' - + block.gfm.fences.source.replace('\\1', '\\2') + '|' - + block.list.source.replace('\\1', '\\3') + '|') - .getRegex(); - -/** - * GFM + Tables Block Grammar - */ - -block.tables = merge({}, block.gfm, { - nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/, - table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/ -}); - -/** - * Pedantic grammar - */ - -block.pedantic = merge({}, block.normal, { - html: edit( - '^ *(?:comment *(?:\\n|\\s*$)' - + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag - + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') - .replace('comment', block._comment) - .replace(/tag/g, '(?!(?:' - + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' - + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' - + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b') - .getRegex(), - def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/ -}); - -/** - * Block Lexer - */ - -function Lexer(options) { - this.tokens = []; - this.tokens.links = Object.create(null); - this.options = options || marked.defaults; - this.rules = block.normal; - - if (this.options.pedantic) { - this.rules = block.pedantic; - } else if (this.options.gfm) { - if (this.options.tables) { - this.rules = block.tables; - } else { - this.rules = block.gfm; + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); } } -} -/** - * Expose Block Rules - */ + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } -Lexer.rules = block; + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } -/** - * Static Lex Method - */ + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; -Lexer.lex = function(src, options) { - var lexer = new Lexer(options); - return lexer.lex(src); -}; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; -/** - * Preprocessing - */ + return arr2; + } -Lexer.prototype.lex = function(src) { - src = src - .replace(/\r\n|\r/g, '\n') - .replace(/\t/g, ' ') - .replace(/\u00a0/g, ' ') - .replace(/\u2424/g, '\n'); + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it; - return this.token(src, true); -}; + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } -/** - * Lexing - */ + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } -Lexer.prototype.token = function(src, top) { - src = src.replace(/^ +$/gm, ''); - var next, - loose, - cap, - bull, - b, - item, - listStart, - listItems, - t, - space, - i, - tag, - l, - isordered, - istask, - ischecked; + it = o[Symbol.iterator](); + return it.next.bind(it); + } - while (src) { - // newline - if (cap = this.rules.newline.exec(src)) { - src = src.substring(cap[0].length); - if (cap[0].length > 1) { - this.tokens.push({ - type: 'space' - }); + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var defaults = createCommonjsModule(function (module) { + function getDefaults() { + return { + baseUrl: null, + breaks: false, + gfm: true, + headerIds: true, + headerPrefix: '', + highlight: null, + langPrefix: 'language-', + mangle: true, + pedantic: false, + renderer: null, + sanitize: false, + sanitizer: null, + silent: false, + smartLists: false, + smartypants: false, + tokenizer: null, + walkTokens: null, + xhtml: false + }; + } + + function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; + } + + module.exports = { + defaults: getDefaults(), + getDefaults: getDefaults, + changeDefaults: changeDefaults + }; + }); + var defaults_1 = defaults.defaults; + var defaults_2 = defaults.getDefaults; + var defaults_3 = defaults.changeDefaults; + + /** + * Helpers + */ + var escapeTest = /[&<>"']/; + var escapeReplace = /[&<>"']/g; + var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; + var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; + var escapeReplacements = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + var getEscapeReplacement = function getEscapeReplacement(ch) { + return escapeReplacements[ch]; + }; + + function escape(html, encode) { + if (encode) { + if (escapeTest.test(html)) { + return html.replace(escapeReplace, getEscapeReplacement); + } + } else { + if (escapeTestNoEncode.test(html)) { + return html.replace(escapeReplaceNoEncode, getEscapeReplacement); } } - // code - if (cap = this.rules.code.exec(src)) { - src = src.substring(cap[0].length); - cap = cap[0].replace(/^ {4}/gm, ''); - this.tokens.push({ - type: 'code', - text: !this.options.pedantic - ? rtrim(cap, '\n') - : cap - }); - continue; - } + return html; + } - // fences (gfm) - if (cap = this.rules.fences.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'code', - lang: cap[2] ? cap[2].trim() : cap[2], - text: cap[3] || '' - }); - continue; - } + var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; - // heading - if (cap = this.rules.heading.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'heading', - depth: cap[1].length, - text: cap[2] - }); - continue; - } + function unescape(html) { + // explicitly match decimal, hex, and named HTML entities + return html.replace(unescapeTest, function (_, n) { + n = n.toLowerCase(); + if (n === 'colon') return ':'; - // table no leading pipe (gfm) - if (cap = this.rules.nptable.exec(src)) { - item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; + if (n.charAt(0) === '#') { + return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1)); + } - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); + return ''; + }); + } - for (i = 0; i < item.align.length; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } + var caret = /(^|[^\[])\^/g; - for (i = 0; i < item.cells.length; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); - } + function edit(regex, opt) { + regex = regex.source || regex; + opt = opt || ''; + var obj = { + replace: function replace(name, val) { + val = val.source || val; + val = val.replace(caret, '$1'); + regex = regex.replace(name, val); + return obj; + }, + getRegex: function getRegex() { + return new RegExp(regex, opt); + } + }; + return obj; + } - this.tokens.push(item); + var nonWordAndColonTest = /[^\w:]/g; + var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - continue; + function cleanUrl(sanitize, base, href) { + if (sanitize) { + var prot; + + try { + prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase(); + } catch (e) { + return null; + } + + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { + return null; } } - // hr - if (cap = this.rules.hr.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'hr' - }); - continue; + if (base && !originIndependentUrl.test(href)) { + href = resolveUrl(base, href); } - // blockquote - if (cap = this.rules.blockquote.exec(src)) { - src = src.substring(cap[0].length); - - this.tokens.push({ - type: 'blockquote_start' - }); - - cap = cap[0].replace(/^ *> ?/gm, ''); - - // Pass `top` to keep the current - // "toplevel" state. This is exactly - // how markdown.pl works. - this.token(cap, top); - - this.tokens.push({ - type: 'blockquote_end' - }); - - continue; + try { + href = encodeURI(href).replace(/%25/g, '%'); + } catch (e) { + return null; } - // list - if (cap = this.rules.list.exec(src)) { - src = src.substring(cap[0].length); - bull = cap[2]; - isordered = bull.length > 1; + return href; + } - listStart = { - type: 'list_start', - ordered: isordered, - start: isordered ? +bull : '', - loose: false + var baseUrls = {}; + var justDomain = /^[^:]+:\/*[^/]*$/; + var protocol = /^([^:]+:)[\s\S]*$/; + var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/; + + function resolveUrl(base, href) { + if (!baseUrls[' ' + base]) { + // we can ignore everything in base after the last slash of its path component, + // but we might need to add _that_ + // https://tools.ietf.org/html/rfc3986#section-3 + if (justDomain.test(base)) { + baseUrls[' ' + base] = base + '/'; + } else { + baseUrls[' ' + base] = rtrim(base, '/', true); + } + } + + base = baseUrls[' ' + base]; + var relativeBase = base.indexOf(':') === -1; + + if (href.substring(0, 2) === '//') { + if (relativeBase) { + return href; + } + + return base.replace(protocol, '$1') + href; + } else if (href.charAt(0) === '/') { + if (relativeBase) { + return href; + } + + return base.replace(domain, '$1') + href; + } else { + return base + href; + } + } + + var noopTest = { + exec: function noopTest() {} + }; + + function merge(obj) { + var i = 1, + target, + key; + + for (; i < arguments.length; i++) { + target = arguments[i]; + + for (key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + obj[key] = target[key]; + } + } + } + + return obj; + } + + function splitCells(tableRow, count) { + // ensure that every cell-delimiting pipe has a space + // before it to distinguish it from an escaped pipe + var row = tableRow.replace(/\|/g, function (match, offset, str) { + var escaped = false, + curr = offset; + + while (--curr >= 0 && str[curr] === '\\') { + escaped = !escaped; + } + + if (escaped) { + // odd number of slashes means | is escaped + // so we leave it alone + return '|'; + } else { + // add space before unescaped | + return ' |'; + } + }), + cells = row.split(/ \|/); + var i = 0; + + if (cells.length > count) { + cells.splice(count); + } else { + while (cells.length < count) { + cells.push(''); + } + } + + for (; i < cells.length; i++) { + // leading or trailing whitespace is ignored per the gfm spec + cells[i] = cells[i].trim().replace(/\\\|/g, '|'); + } + + return cells; + } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). + // /c*$/ is vulnerable to REDOS. + // invert: Remove suffix of non-c chars instead. Default falsey. + + + function rtrim(str, c, invert) { + var l = str.length; + + if (l === 0) { + return ''; + } // Length of suffix matching the invert condition. + + + var suffLen = 0; // Step left until we fail to match the invert condition. + + while (suffLen < l) { + var currChar = str.charAt(l - suffLen - 1); + + if (currChar === c && !invert) { + suffLen++; + } else if (currChar !== c && invert) { + suffLen++; + } else { + break; + } + } + + return str.substr(0, l - suffLen); + } + + function findClosingBracket(str, b) { + if (str.indexOf(b[1]) === -1) { + return -1; + } + + var l = str.length; + var level = 0, + i = 0; + + for (; i < l; i++) { + if (str[i] === '\\') { + i++; + } else if (str[i] === b[0]) { + level++; + } else if (str[i] === b[1]) { + level--; + + if (level < 0) { + return i; + } + } + } + + return -1; + } + + function checkSanitizeDeprecation(opt) { + if (opt && opt.sanitize && !opt.silent) { + console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); + } + } // copied from https://stackoverflow.com/a/5450113/806777 + + + function repeatString(pattern, count) { + if (count < 1) { + return ''; + } + + var result = ''; + + while (count > 1) { + if (count & 1) { + result += pattern; + } + + count >>= 1; + pattern += pattern; + } + + return result + pattern; + } + + var helpers = { + escape: escape, + unescape: unescape, + edit: edit, + cleanUrl: cleanUrl, + resolveUrl: resolveUrl, + noopTest: noopTest, + merge: merge, + splitCells: splitCells, + rtrim: rtrim, + findClosingBracket: findClosingBracket, + checkSanitizeDeprecation: checkSanitizeDeprecation, + repeatString: repeatString + }; + + var defaults$1 = defaults.defaults; + var rtrim$1 = helpers.rtrim, + splitCells$1 = helpers.splitCells, + _escape = helpers.escape, + findClosingBracket$1 = helpers.findClosingBracket; + + function outputLink(cap, link, raw) { + var href = link.href; + var title = link.title ? _escape(link.title) : null; + var text = cap[1].replace(/\\([\[\]])/g, '$1'); + + if (cap[0].charAt(0) !== '!') { + return { + type: 'link', + raw: raw, + href: href, + title: title, + text: text }; + } else { + return { + type: 'image', + raw: raw, + href: href, + title: title, + text: _escape(text) + }; + } + } - this.tokens.push(listStart); + function indentCodeCompensation(raw, text) { + var matchIndentToCode = raw.match(/^(\s+)(?:```)/); - // Get each top-level item. - cap = cap[0].match(this.rules.item); + if (matchIndentToCode === null) { + return text; + } - listItems = []; - next = false; - l = cap.length; - i = 0; + var indentToCode = matchIndentToCode[1]; + return text.split('\n').map(function (node) { + var matchIndentInNode = node.match(/^\s+/); - for (; i < l; i++) { - item = cap[i]; + if (matchIndentInNode === null) { + return node; + } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) */, ''); + var indentInNode = matchIndentInNode[0]; - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); + if (indentInNode.length >= indentToCode.length) { + return node.slice(indentToCode.length); + } + + return node; + }).join('\n'); + } + /** + * Tokenizer + */ + + + var Tokenizer_1 = /*#__PURE__*/function () { + function Tokenizer(options) { + this.options = options || defaults$1; + } + + var _proto = Tokenizer.prototype; + + _proto.space = function space(src) { + var cap = this.rules.block.newline.exec(src); + + if (cap) { + if (cap[0].length > 1) { + return { + type: 'space', + raw: cap[0] + }; } - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - b = block.bullet.exec(cap[i + 1])[0]; - if (bull.length > 1 ? b.length === 1 - : (b.length > 1 || (this.options.smartLists && b !== bull))) { - src = cap.slice(i + 1).join('\n') + src; - i = l - 1; - } + return { + raw: '\n' + }; + } + }; + + _proto.code = function code(src, tokens) { + var cap = this.rules.block.code.exec(src); + + if (cap) { + var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph. + + if (lastToken && lastToken.type === 'paragraph') { + return { + raw: cap[0], + text: cap[0].trimRight() + }; } - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(item); - if (i !== l - 1) { - next = item.charAt(item.length - 1) === '\n'; - if (!loose) loose = next; - } + var text = cap[0].replace(/^ {4}/gm, ''); + return { + type: 'code', + raw: cap[0], + codeBlockStyle: 'indented', + text: !this.options.pedantic ? rtrim$1(text, '\n') : text + }; + } + }; - if (loose) { - listStart.loose = true; - } + _proto.fences = function fences(src) { + var cap = this.rules.block.fences.exec(src); - // Check for task list items - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; - if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); - } + if (cap) { + var raw = cap[0]; + var text = indentCodeCompensation(raw, cap[3] || ''); + return { + type: 'code', + raw: raw, + lang: cap[2] ? cap[2].trim() : cap[2], + text: text + }; + } + }; - t = { - type: 'list_item_start', - task: istask, - checked: ischecked, - loose: loose + _proto.heading = function heading(src) { + var cap = this.rules.block.heading.exec(src); + + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[1].length, + text: cap[2] + }; + } + }; + + _proto.nptable = function nptable(src) { + var cap = this.rules.block.nptable.exec(src); + + if (cap) { + var item = { + type: 'table', + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], + raw: cap[0] }; - listItems.push(t); - this.tokens.push(t); + if (item.header.length === item.align.length) { + var l = item.align.length; + var i; - // Recurse. - this.token(item, false); + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } - this.tokens.push({ - type: 'list_item_end' - }); - } + l = item.cells.length; - if (listStart.loose) { - l = listItems.length; - i = 0; - for (; i < l; i++) { - listItems[i].loose = true; + for (i = 0; i < l; i++) { + item.cells[i] = splitCells$1(item.cells[i], item.header.length); + } + + return item; } } + }; - this.tokens.push({ - type: 'list_end' - }); + _proto.hr = function hr(src) { + var cap = this.rules.block.hr.exec(src); - continue; - } + if (cap) { + return { + type: 'hr', + raw: cap[0] + }; + } + }; - // html - if (cap = this.rules.html.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: this.options.sanitize - ? 'paragraph' - : 'html', - pre: !this.options.sanitizer - && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: cap[0] - }); - continue; - } + _proto.blockquote = function blockquote(src) { + var cap = this.rules.block.blockquote.exec(src); - // def - if (top && (cap = this.rules.def.exec(src))) { - src = src.substring(cap[0].length); - if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); - tag = cap[1].toLowerCase().replace(/\s+/g, ' '); - if (!this.tokens.links[tag]) { - this.tokens.links[tag] = { + if (cap) { + var text = cap[0].replace(/^ *> ?/gm, ''); + return { + type: 'blockquote', + raw: cap[0], + text: text + }; + } + }; + + _proto.list = function list(src) { + var cap = this.rules.block.list.exec(src); + + if (cap) { + var raw = cap[0]; + var bull = cap[2]; + var isordered = bull.length > 1; + var isparen = bull[bull.length - 1] === ')'; + var list = { + type: 'list', + raw: raw, + ordered: isordered, + start: isordered ? +bull.slice(0, -1) : '', + loose: false, + items: [] + }; // Get each top-level item. + + var itemMatch = cap[0].match(this.rules.block.item); + var next = false, + item, + space, + b, + addBack, + loose, + istask, + ischecked; + var l = itemMatch.length; + + for (var i = 0; i < l; i++) { + item = itemMatch[i]; + raw = item; // Remove the list item's bullet + // so it is seen as the next token. + + space = item.length; + item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the + // list item contains. Hacky. + + if (~item.indexOf('\n ')) { + space -= item.length; + item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, ''); + } // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + + + if (i !== l - 1) { + b = this.rules.block.bullet.exec(itemMatch[i + 1])[0]; + + if (isordered ? b.length === 1 || !isparen && b[b.length - 1] === ')' : b.length > 1 || this.options.smartLists && b !== bull) { + addBack = itemMatch.slice(i + 1).join('\n'); + list.raw = list.raw.substring(0, list.raw.length - addBack.length); + i = l - 1; + } + } // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + + + loose = next || /\n\n(?!\s*$)/.test(item); + + if (i !== l - 1) { + next = item.charAt(item.length - 1) === '\n'; + if (!loose) loose = next; + } + + if (loose) { + list.loose = true; + } // Check for task list items + + + istask = /^\[[ xX]\] /.test(item); + ischecked = undefined; + + if (istask) { + ischecked = item[1] !== ' '; + item = item.replace(/^\[[ xX]\] +/, ''); + } + + list.items.push({ + type: 'list_item', + raw: raw, + task: istask, + checked: ischecked, + loose: loose, + text: item + }); + } + + return list; + } + }; + + _proto.html = function html(src) { + var cap = this.rules.block.html.exec(src); + + if (cap) { + return { + type: this.options.sanitize ? 'paragraph' : 'html', + raw: cap[0], + pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), + text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + }; + } + }; + + _proto.def = function def(src) { + var cap = this.rules.block.def.exec(src); + + if (cap) { + if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); + var tag = cap[1].toLowerCase().replace(/\s+/g, ' '); + return { + tag: tag, + raw: cap[0], href: cap[2], title: cap[3] }; } - continue; - } + }; - // table (gfm) - if (cap = this.rules.table.exec(src)) { - item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; + _proto.table = function table(src) { + var cap = this.rules.block.table.exec(src); - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); + if (cap) { + var item = { + type: 'table', + header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] + }; - for (i = 0; i < item.align.length; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; + if (item.header.length === item.align.length) { + item.raw = cap[0]; + var l = item.align.length; + var i; + + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + l = item.cells.length; + + for (i = 0; i < l; i++) { + item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length); + } + + return item; + } + } + }; + + _proto.lheading = function lheading(src) { + var cap = this.rules.block.lheading.exec(src); + + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[2].charAt(0) === '=' ? 1 : 2, + text: cap[1] + }; + } + }; + + _proto.paragraph = function paragraph(src) { + var cap = this.rules.block.paragraph.exec(src); + + if (cap) { + return { + type: 'paragraph', + raw: cap[0], + text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1] + }; + } + }; + + _proto.text = function text(src, tokens) { + var cap = this.rules.block.text.exec(src); + + if (cap) { + var lastToken = tokens[tokens.length - 1]; + + if (lastToken && lastToken.type === 'text') { + return { + raw: cap[0], + text: cap[0] + }; + } + + return { + type: 'text', + raw: cap[0], + text: cap[0] + }; + } + }; + + _proto.escape = function escape(src) { + var cap = this.rules.inline.escape.exec(src); + + if (cap) { + return { + type: 'escape', + raw: cap[0], + text: _escape(cap[1]) + }; + } + }; + + _proto.tag = function tag(src, inLink, inRawBlock) { + var cap = this.rules.inline.tag.exec(src); + + if (cap) { + if (!inLink && /^
/i.test(cap[0])) { + inLink = false; + } + + if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + inRawBlock = true; + } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + inRawBlock = false; + } + + return { + type: this.options.sanitize ? 'text' : 'html', + raw: cap[0], + inLink: inLink, + inRawBlock: inRawBlock, + text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0] + }; + } + }; + + _proto.link = function link(src) { + var cap = this.rules.inline.link.exec(src); + + if (cap) { + var lastParenIndex = findClosingBracket$1(cap[2], '()'); + + if (lastParenIndex > -1) { + var start = cap[0].indexOf('!') === 0 ? 5 : 4; + var linkLen = start + cap[1].length + lastParenIndex; + cap[2] = cap[2].substring(0, lastParenIndex); + cap[0] = cap[0].substring(0, linkLen).trim(); + cap[3] = ''; + } + + var href = cap[2]; + var title = ''; + + if (this.options.pedantic) { + var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); + + if (link) { + href = link[1]; + title = link[3]; } else { - item.align[i] = null; + title = ''; + } + } else { + title = cap[3] ? cap[3].slice(1, -1) : ''; + } + + href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); + var token = outputLink(cap, { + href: href ? href.replace(this.rules.inline._escapes, '$1') : href, + title: title ? title.replace(this.rules.inline._escapes, '$1') : title + }, cap[0]); + return token; + } + }; + + _proto.reflink = function reflink(src, links) { + var cap; + + if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) { + var link = (cap[2] || cap[1]).replace(/\s+/g, ' '); + link = links[link.toLowerCase()]; + + if (!link || !link.href) { + var text = cap[0].charAt(0); + return { + type: 'text', + raw: text, + text: text + }; + } + + var token = outputLink(cap, link, cap[0]); + return token; + } + }; + + _proto.strong = function strong(src, maskedSrc, prevChar) { + if (prevChar === void 0) { + prevChar = ''; + } + + var match = this.rules.inline.strong.start.exec(src); + + if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) { + maskedSrc = maskedSrc.slice(-1 * src.length); + var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd; + endReg.lastIndex = 0; + var cap; + + while ((match = endReg.exec(maskedSrc)) != null) { + cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3)); + + if (cap) { + return { + type: 'strong', + raw: src.slice(0, cap[0].length), + text: src.slice(2, cap[0].length - 2) + }; + } + } + } + }; + + _proto.em = function em(src, maskedSrc, prevChar) { + if (prevChar === void 0) { + prevChar = ''; + } + + var match = this.rules.inline.em.start.exec(src); + + if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) { + maskedSrc = maskedSrc.slice(-1 * src.length); + var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd; + endReg.lastIndex = 0; + var cap; + + while ((match = endReg.exec(maskedSrc)) != null) { + cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2)); + + if (cap) { + return { + type: 'em', + raw: src.slice(0, cap[0].length), + text: src.slice(1, cap[0].length - 1) + }; + } + } + } + }; + + _proto.codespan = function codespan(src) { + var cap = this.rules.inline.code.exec(src); + + if (cap) { + var text = cap[2].replace(/\n/g, ' '); + var hasNonSpaceChars = /[^ ]/.test(text); + var hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' '); + + if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { + text = text.substring(1, text.length - 1); + } + + text = _escape(text, true); + return { + type: 'codespan', + raw: cap[0], + text: text + }; + } + }; + + _proto.br = function br(src) { + var cap = this.rules.inline.br.exec(src); + + if (cap) { + return { + type: 'br', + raw: cap[0] + }; + } + }; + + _proto.del = function del(src) { + var cap = this.rules.inline.del.exec(src); + + if (cap) { + return { + type: 'del', + raw: cap[0], + text: cap[1] + }; + } + }; + + _proto.autolink = function autolink(src, mangle) { + var cap = this.rules.inline.autolink.exec(src); + + if (cap) { + var text, href; + + if (cap[2] === '@') { + text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]); + href = 'mailto:' + text; + } else { + text = _escape(cap[1]); + href = text; + } + + return { + type: 'link', + raw: cap[0], + text: text, + href: href, + tokens: [{ + type: 'text', + raw: text, + text: text + }] + }; + } + }; + + _proto.url = function url(src, mangle) { + var cap; + + if (cap = this.rules.inline.url.exec(src)) { + var text, href; + + if (cap[2] === '@') { + text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]); + href = 'mailto:' + text; + } else { + // do extended autolink path validation + var prevCapZero; + + do { + prevCapZero = cap[0]; + cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; + } while (prevCapZero !== cap[0]); + + text = _escape(cap[0]); + + if (cap[1] === 'www.') { + href = 'http://' + text; + } else { + href = text; } } - for (i = 0; i < item.cells.length; i++) { - item.cells[i] = splitCells( - item.cells[i].replace(/^ *\| *| *\| *$/g, ''), - item.header.length); + return { + type: 'link', + raw: cap[0], + text: text, + href: href, + tokens: [{ + type: 'text', + raw: text, + text: text + }] + }; + } + }; + + _proto.inlineText = function inlineText(src, inRawBlock, smartypants) { + var cap = this.rules.inline.text.exec(src); + + if (cap) { + var text; + + if (inRawBlock) { + text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]; + } else { + text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]); } - this.tokens.push(item); - - continue; + return { + type: 'text', + raw: cap[0], + text: text + }; } - } + }; - // lheading - if (cap = this.rules.lheading.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'heading', - depth: cap[2] === '=' ? 1 : 2, - text: cap[1] - }); - continue; - } + return Tokenizer; + }(); - // top-level paragraph - if (top && (cap = this.rules.paragraph.exec(src))) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'paragraph', - text: cap[1].charAt(cap[1].length - 1) === '\n' - ? cap[1].slice(0, -1) - : cap[1] - }); - continue; - } + var noopTest$1 = helpers.noopTest, + edit$1 = helpers.edit, + merge$1 = helpers.merge; + /** + * Block-Level Grammar + */ - // text - if (cap = this.rules.text.exec(src)) { - // Top-level should never reach here. - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'text', - text: cap[0] - }); - continue; - } + var block = { + newline: /^\n+/, + code: /^( {4}[^\n]+\n*)+/, + fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, + hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, + heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/, + blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, + list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + html: '^ {0,3}(?:' // optional indentation + + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + + '|comment[^\\n]*(\\n+|$)' // (2) + + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3) + + '|\\n*|$)' // (4) + + '|\\n*|$)' // (5) + + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) + + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag + + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag + + ')', + def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, + nptable: noopTest$1, + table: noopTest$1, + lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, + // regex template, placeholders will be replaced according to different paragraph + // interruption rules of commonmark and the original markdown spec: + _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/, + text: /^[^\n]+/ + }; + block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; + block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; + block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex(); + block.bullet = /(?:[*+-]|\d{1,9}[.)])/; + block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/; + block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex(); + block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex(); + block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul'; + block._comment = /|$)/; + block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); + block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs + .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks + .getRegex(); + block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex(); + /** + * Normal Block Grammar + */ - if (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); - } - } + block.normal = merge$1({}, block); + /** + * GFM Block Grammar + */ - return this.tokens; -}; + block.gfm = merge$1({}, block.normal, { + nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', + // Cells + table: '^ *\\|(.+)\\n' // Header + + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align + + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells -/** - * Inline-Level Grammar - */ + }); + block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks + .getRegex(); + block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt + .replace('html', ')|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks + .getRegex(); + /** + * Pedantic grammar (original John Gruber's loose markdown specification) + */ -var inline = { - escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, - autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noop, - tag: '^comment' - + '|^' // self-closing tag + block.pedantic = merge$1({}, block.normal, { + html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag + + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(), + def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, + heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, + fences: noopTest$1, + // fences not supported + paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex() + }); + /** + * Inline-Level Grammar + */ + + var inline = { + escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, + autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, + url: noopTest$1, + tag: '^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. + '|^' // declaration, e.g. - + '|^', // CDATA section - link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/, - reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, - nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, - strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/, - em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/, - code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, - br: /^( {2,}|\\)\n(?!\s*$)/, - del: noop, - text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\', + // CDATA section + link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/, + reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, + nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, + reflinkSearch: 'reflink|nolink(?!\\()', + strong: { + start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/, + // (1) returns if starts w/ punctuation + middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/, + endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/, + // last char can't be punct, or final * must also be followed by punct (or endline) + endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) -// list of punctuation marks from common mark spec -// without ` and ] to workaround Rule 17 (inline code blocks/links) -inline._punctuation = '!"#$%&\'()*+,\\-./:;<=>?@\\[^_{|}~'; -inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex(); + }, + em: { + start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/, + // (1) returns if starts w/ punctuation + middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/, + endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/, + // last char can't be punct, or final * must also be followed by punct (or endline) + endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) -inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + }, + code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, + br: /^( {2,}|\\)\n(?!\s*$)/, + del: noopTest$1, + text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; + inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, -inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>'; + inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*'; + inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex(); + inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex(); + inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex(); + inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex(); + inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex(); + inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex(); + inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex(); + inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex(); + inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; + inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; + inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/; + inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex(); + inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex(); + inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; + inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/; + inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex(); + inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex(); + inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex(); + /** + * Normal Inline Grammar + */ -inline.tag = edit(inline.tag) - .replace('comment', block._comment) - .replace('attribute', inline._attribute) - .getRegex(); + inline.normal = merge$1({}, inline); + /** + * Pedantic Inline Grammar + */ -inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/; -inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/; -inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + inline.pedantic = merge$1({}, inline.normal, { + strong: { + start: /^__|\*\*/, + middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + endAst: /\*\*(?!\*)/g, + endUnd: /__(?!_)/g + }, + em: { + start: /^_|\*/, + middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/, + endAst: /\*(?!\*)/g, + endUnd: /_(?!_)/g + }, + link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(), + reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex() + }); + /** + * GFM Inline Grammar + */ -inline.link = edit(inline.link) - .replace('label', inline._label) - .replace('href', inline._href) - .replace('title', inline._title) - .getRegex(); + inline.gfm = merge$1({}, inline.normal, { + escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), + _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, + url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, + _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, + del: /^~+(?=\S)([\s\S]*?\S)~+/, + text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\ 0.5) { + ch = 'x' + ch.toString(16); + } + + out += '&#' + ch + ';'; } + + return out; } -} + /** + * Block Lexer + */ -/** - * Expose Inline Rules - */ -InlineLexer.rules = inline; + var Lexer_1 = /*#__PURE__*/function () { + function Lexer(options) { + this.tokens = []; + this.tokens.links = Object.create(null); + this.options = options || defaults$2; + this.options.tokenizer = this.options.tokenizer || new Tokenizer_1(); + this.tokenizer = this.options.tokenizer; + this.tokenizer.options = this.options; + var rules = { + block: block$1.normal, + inline: inline$1.normal + }; -/** - * Static Lexing/Compiling Method - */ - -InlineLexer.output = function(src, links, options) { - var inline = new InlineLexer(links, options); - return inline.output(src); -}; - -/** - * Lexing/Compiling - */ - -InlineLexer.prototype.output = function(src) { - var out = '', - link, - text, - href, - title, - cap, - prevCapZero; - - while (src) { - // escape - if (cap = this.rules.escape.exec(src)) { - src = src.substring(cap[0].length); - out += escape(cap[1]); - continue; - } - - // tag - if (cap = this.rules.tag.exec(src)) { - if (!this.inLink && /^/i.test(cap[0])) { - this.inLink = false; - } - if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - this.inRawBlock = true; - } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - this.inRawBlock = false; - } - - src = src.substring(cap[0].length); - out += this.options.sanitize - ? this.options.sanitizer - ? this.options.sanitizer(cap[0]) - : escape(cap[0]) - : cap[0]; - continue; - } - - // link - if (cap = this.rules.link.exec(src)) { - var lastParenIndex = findClosingBracket(cap[2], '()'); - if (lastParenIndex > -1) { - var linkLen = cap[0].length - (cap[2].length - lastParenIndex) - (cap[3] || '').length; - cap[2] = cap[2].substring(0, lastParenIndex); - cap[0] = cap[0].substring(0, linkLen).trim(); - cap[3] = ''; - } - src = src.substring(cap[0].length); - this.inLink = true; - href = cap[2]; if (this.options.pedantic) { - link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); + rules.block = block$1.pedantic; + rules.inline = inline$1.pedantic; + } else if (this.options.gfm) { + rules.block = block$1.gfm; - if (link) { - href = link[1]; - title = link[3]; + if (this.options.breaks) { + rules.inline = inline$1.breaks; } else { - title = ''; - } - } else { - title = cap[3] ? cap[3].slice(1, -1) : ''; - } - href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); - out += this.outputLink(cap, { - href: InlineLexer.escapes(href), - title: InlineLexer.escapes(title) - }); - this.inLink = false; - continue; - } - - // reflink, nolink - if ((cap = this.rules.reflink.exec(src)) - || (cap = this.rules.nolink.exec(src))) { - src = src.substring(cap[0].length); - link = (cap[2] || cap[1]).replace(/\s+/g, ' '); - link = this.links[link.toLowerCase()]; - if (!link || !link.href) { - out += cap[0].charAt(0); - src = cap[0].substring(1) + src; - continue; - } - this.inLink = true; - out += this.outputLink(cap, link); - this.inLink = false; - continue; - } - - // strong - if (cap = this.rules.strong.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1])); - continue; - } - - // em - if (cap = this.rules.em.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1])); - continue; - } - - // code - if (cap = this.rules.code.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.codespan(escape(cap[2].trim(), true)); - continue; - } - - // br - if (cap = this.rules.br.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.br(); - continue; - } - - // del (gfm) - if (cap = this.rules.del.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.del(this.output(cap[1])); - continue; - } - - // autolink - if (cap = this.rules.autolink.exec(src)) { - src = src.substring(cap[0].length); - if (cap[2] === '@') { - text = escape(this.mangle(cap[1])); - href = 'mailto:' + text; - } else { - text = escape(cap[1]); - href = text; - } - out += this.renderer.link(href, null, text); - continue; - } - - // url (gfm) - if (!this.inLink && (cap = this.rules.url.exec(src))) { - if (cap[2] === '@') { - text = escape(cap[0]); - href = 'mailto:' + text; - } else { - // do extended autolink path validation - do { - prevCapZero = cap[0]; - cap[0] = this.rules._backpedal.exec(cap[0])[0]; - } while (prevCapZero !== cap[0]); - text = escape(cap[0]); - if (cap[1] === 'www.') { - href = 'http://' + text; - } else { - href = text; + rules.inline = inline$1.gfm; } } - src = src.substring(cap[0].length); - out += this.renderer.link(href, null, text); - continue; - } - // text - if (cap = this.rules.text.exec(src)) { - src = src.substring(cap[0].length); - if (this.inRawBlock) { - out += this.renderer.text(cap[0]); - } else { - out += this.renderer.text(escape(this.smartypants(cap[0]))); + this.tokenizer.rules = rules; + } + /** + * Expose Rules + */ + + + /** + * Static Lex Method + */ + Lexer.lex = function lex(src, options) { + var lexer = new Lexer(options); + return lexer.lex(src); + } + /** + * Static Lex Inline Method + */ + ; + + Lexer.lexInline = function lexInline(src, options) { + var lexer = new Lexer(options); + return lexer.inlineTokens(src); + } + /** + * Preprocessing + */ + ; + + var _proto = Lexer.prototype; + + _proto.lex = function lex(src) { + src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' '); + this.blockTokens(src, this.tokens, true); + this.inline(this.tokens); + return this.tokens; + } + /** + * Lexing + */ + ; + + _proto.blockTokens = function blockTokens(src, tokens, top) { + if (tokens === void 0) { + tokens = []; } - continue; + + if (top === void 0) { + top = true; + } + + src = src.replace(/^ +$/gm, ''); + var token, i, l, lastToken; + + while (src) { + // newline + if (token = this.tokenizer.space(src)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } + + continue; + } // code + + + if (token = this.tokenizer.code(src, tokens)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } else { + lastToken = tokens[tokens.length - 1]; + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.text; + } + + continue; + } // fences + + + if (token = this.tokenizer.fences(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // heading + + + if (token = this.tokenizer.heading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // table no leading pipe (gfm) + + + if (token = this.tokenizer.nptable(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // hr + + + if (token = this.tokenizer.hr(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // blockquote + + + if (token = this.tokenizer.blockquote(src)) { + src = src.substring(token.raw.length); + token.tokens = this.blockTokens(token.text, [], top); + tokens.push(token); + continue; + } // list + + + if (token = this.tokenizer.list(src)) { + src = src.substring(token.raw.length); + l = token.items.length; + + for (i = 0; i < l; i++) { + token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); + } + + tokens.push(token); + continue; + } // html + + + if (token = this.tokenizer.html(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // def + + + if (top && (token = this.tokenizer.def(src))) { + src = src.substring(token.raw.length); + + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } + + continue; + } // table (gfm) + + + if (token = this.tokenizer.table(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // lheading + + + if (token = this.tokenizer.lheading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // top-level paragraph + + + if (top && (token = this.tokenizer.paragraph(src))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // text + + + if (token = this.tokenizer.text(src, tokens)) { + src = src.substring(token.raw.length); + + if (token.type) { + tokens.push(token); + } else { + lastToken = tokens[tokens.length - 1]; + lastToken.raw += '\n' + token.raw; + lastToken.text += '\n' + token.text; + } + + continue; + } + + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); + + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + + return tokens; + }; + + _proto.inline = function inline(tokens) { + var i, j, k, l2, row, token; + var l = tokens.length; + + for (i = 0; i < l; i++) { + token = tokens[i]; + + switch (token.type) { + case 'paragraph': + case 'text': + case 'heading': + { + token.tokens = []; + this.inlineTokens(token.text, token.tokens); + break; + } + + case 'table': + { + token.tokens = { + header: [], + cells: [] + }; // header + + l2 = token.header.length; + + for (j = 0; j < l2; j++) { + token.tokens.header[j] = []; + this.inlineTokens(token.header[j], token.tokens.header[j]); + } // cells + + + l2 = token.cells.length; + + for (j = 0; j < l2; j++) { + row = token.cells[j]; + token.tokens.cells[j] = []; + + for (k = 0; k < row.length; k++) { + token.tokens.cells[j][k] = []; + this.inlineTokens(row[k], token.tokens.cells[j][k]); + } + } + + break; + } + + case 'blockquote': + { + this.inline(token.tokens); + break; + } + + case 'list': + { + l2 = token.items.length; + + for (j = 0; j < l2; j++) { + this.inline(token.items[j].tokens); + } + + break; + } + } + } + + return tokens; + } + /** + * Lexing/Compiling + */ + ; + + _proto.inlineTokens = function inlineTokens(src, tokens, inLink, inRawBlock, prevChar) { + if (tokens === void 0) { + tokens = []; + } + + if (inLink === void 0) { + inLink = false; + } + + if (inRawBlock === void 0) { + inRawBlock = false; + } + + if (prevChar === void 0) { + prevChar = ''; + } + + var token; // String with links masked to avoid interference with em and strong + + var maskedSrc = src; + var match; // Mask out reflinks + + if (this.tokens.links) { + var links = Object.keys(this.tokens.links); + + if (links.length > 0) { + while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { + if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + } + } + } + } // Mask out other blocks + + + while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + } + + while (src) { + // escape + if (token = this.tokenizer.escape(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // tag + + + if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { + src = src.substring(token.raw.length); + inLink = token.inLink; + inRawBlock = token.inRawBlock; + tokens.push(token); + continue; + } // link + + + if (token = this.tokenizer.link(src)) { + src = src.substring(token.raw.length); + + if (token.type === 'link') { + token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); + } + + tokens.push(token); + continue; + } // reflink, nolink + + + if (token = this.tokenizer.reflink(src, this.tokens.links)) { + src = src.substring(token.raw.length); + + if (token.type === 'link') { + token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); + } + + tokens.push(token); + continue; + } // strong + + + if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // em + + + if (token = this.tokenizer.em(src, maskedSrc, prevChar)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // code + + + if (token = this.tokenizer.codespan(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // br + + + if (token = this.tokenizer.br(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // del (gfm) + + + if (token = this.tokenizer.del(src)) { + src = src.substring(token.raw.length); + token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); + tokens.push(token); + continue; + } // autolink + + + if (token = this.tokenizer.autolink(src, mangle)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // url (gfm) + + + if (!inLink && (token = this.tokenizer.url(src, mangle))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } // text + + + if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) { + src = src.substring(token.raw.length); + prevChar = token.raw.slice(-1); + tokens.push(token); + continue; + } + + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); + + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + + return tokens; + }; + + _createClass(Lexer, null, [{ + key: "rules", + get: function get() { + return { + block: block$1, + inline: inline$1 + }; + } + }]); + + return Lexer; + }(); + + var defaults$3 = defaults.defaults; + var cleanUrl$1 = helpers.cleanUrl, + escape$1 = helpers.escape; + /** + * Renderer + */ + + var Renderer_1 = /*#__PURE__*/function () { + function Renderer(options) { + this.options = options || defaults$3; } - if (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); - } - } + var _proto = Renderer.prototype; - return out; -}; + _proto.code = function code(_code, infostring, escaped) { + var lang = (infostring || '').match(/\S*/)[0]; -InlineLexer.escapes = function(text) { - return text ? text.replace(InlineLexer.rules._escapes, '$1') : text; -}; + if (this.options.highlight) { + var out = this.options.highlight(_code, lang); -/** - * Compile Link - */ + if (out != null && out !== _code) { + escaped = true; + _code = out; + } + } -InlineLexer.prototype.outputLink = function(cap, link) { - var href = link.href, - title = link.title ? escape(link.title) : null; + if (!lang) { + return '
' + (escaped ? _code : escape$1(_code, true)) + '
\n'; + } - return cap[0].charAt(0) !== '!' - ? this.renderer.link(href, title, this.output(cap[1])) - : this.renderer.image(href, title, escape(cap[1])); -}; + return '
' + (escaped ? _code : escape$1(_code, true)) + '
\n'; + }; -/** - * Smartypants Transformations - */ + _proto.blockquote = function blockquote(quote) { + return '
\n' + quote + '
\n'; + }; -InlineLexer.prototype.smartypants = function(text) { - if (!this.options.smartypants) return text; - return text - // em-dashes - .replace(/---/g, '\u2014') - // en-dashes - .replace(/--/g, '\u2013') - // opening singles - .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') - // closing singles & apostrophes - .replace(/'/g, '\u2019') - // opening doubles - .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') - // closing doubles - .replace(/"/g, '\u201d') - // ellipses - .replace(/\.{3}/g, '\u2026'); -}; + _proto.html = function html(_html) { + return _html; + }; -/** - * Mangle Links - */ + _proto.heading = function heading(text, level, raw, slugger) { + if (this.options.headerIds) { + return '' + text + '\n'; + } // ignore IDs -InlineLexer.prototype.mangle = function(text) { - if (!this.options.mangle) return text; - var out = '', - l = text.length, - i = 0, - ch; - for (; i < l; i++) { - ch = text.charCodeAt(i); - if (Math.random() > 0.5) { - ch = 'x' + ch.toString(16); - } - out += '&#' + ch + ';'; - } + return '' + text + '\n'; + }; - return out; -}; + _proto.hr = function hr() { + return this.options.xhtml ? '
\n' : '
\n'; + }; -/** - * Renderer - */ + _proto.list = function list(body, ordered, start) { + var type = ordered ? 'ol' : 'ul', + startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; + return '<' + type + startatt + '>\n' + body + '\n'; + }; -function Renderer(options) { - this.options = options || marked.defaults; -} + _proto.listitem = function listitem(text) { + return '
  • ' + text + '
  • \n'; + }; -Renderer.prototype.code = function(code, infostring, escaped) { - var lang = (infostring || '').match(/\S*/)[0]; - if (this.options.highlight) { - var out = this.options.highlight(code, lang); - if (out != null && out !== code) { - escaped = true; - code = out; - } - } + _proto.checkbox = function checkbox(checked) { + return ' '; + }; - if (!lang) { - return '
    '
    -      + (escaped ? code : escape(code, true))
    -      + '
    '; - } + _proto.paragraph = function paragraph(text) { + return '

    ' + text + '

    \n'; + }; - return '
    '
    -    + (escaped ? code : escape(code, true))
    -    + '
    \n'; -}; + _proto.table = function table(header, body) { + if (body) body = '' + body + ''; + return '\n' + '\n' + header + '\n' + body + '
    \n'; + }; -Renderer.prototype.blockquote = function(quote) { - return '
    \n' + quote + '
    \n'; -}; + _proto.tablerow = function tablerow(content) { + return '\n' + content + '\n'; + }; -Renderer.prototype.html = function(html) { - return html; -}; + _proto.tablecell = function tablecell(content, flags) { + var type = flags.header ? 'th' : 'td'; + var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>'; + return tag + content + '\n'; + } // span level renderer + ; -Renderer.prototype.heading = function(text, level, raw, slugger) { - if (this.options.headerIds) { - return '' - + text - + '\n'; - } - // ignore IDs - return '' + text + '\n'; -}; + _proto.strong = function strong(text) { + return '' + text + ''; + }; -Renderer.prototype.hr = function() { - return this.options.xhtml ? '
    \n' : '
    \n'; -}; + _proto.em = function em(text) { + return '' + text + ''; + }; -Renderer.prototype.list = function(body, ordered, start) { - var type = ordered ? 'ol' : 'ul', - startatt = (ordered && start !== 1) ? (' start="' + start + '"') : ''; - return '<' + type + startatt + '>\n' + body + '\n'; -}; + _proto.codespan = function codespan(text) { + return '' + text + ''; + }; -Renderer.prototype.listitem = function(text) { - return '
  • ' + text + '
  • \n'; -}; + _proto.br = function br() { + return this.options.xhtml ? '
    ' : '
    '; + }; -Renderer.prototype.checkbox = function(checked) { - return ' '; -}; + _proto.del = function del(text) { + return '' + text + ''; + }; -Renderer.prototype.paragraph = function(text) { - return '

    ' + text + '

    \n'; -}; + _proto.link = function link(href, title, text) { + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); -Renderer.prototype.table = function(header, body) { - if (body) body = '' + body + ''; + if (href === null) { + return text; + } - return '\n' - + '\n' - + header - + '\n' - + body - + '
    \n'; -}; + var out = '
    \n' + content + '\n'; -}; + if (title) { + out += ' title="' + title + '"'; + } -Renderer.prototype.tablecell = function(content, flags) { - var type = flags.header ? 'th' : 'td'; - var tag = flags.align - ? '<' + type + ' align="' + flags.align + '">' - : '<' + type + '>'; - return tag + content + '\n'; -}; + out += '>' + text + ''; + return out; + }; -// span level renderer -Renderer.prototype.strong = function(text) { - return '' + text + ''; -}; + _proto.image = function image(href, title, text) { + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); -Renderer.prototype.em = function(text) { - return '' + text + ''; -}; + if (href === null) { + return text; + } -Renderer.prototype.codespan = function(text) { - return '' + text + ''; -}; + var out = '' + text + '' : '
    '; -}; + if (title) { + out += ' title="' + title + '"'; + } -Renderer.prototype.del = function(text) { - return '' + text + ''; -}; + out += this.options.xhtml ? '/>' : '>'; + return out; + }; -Renderer.prototype.link = function(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } - var out = ''; - return out; -}; + _proto.text = function text(_text) { + return _text; + }; -Renderer.prototype.image = function(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } + return Renderer; + }(); - var out = '' + text + '' : '>'; - return out; -}; + /** + * TextRenderer + * returns only the textual part of the token + */ + var TextRenderer_1 = /*#__PURE__*/function () { + function TextRenderer() {} -Renderer.prototype.text = function(text) { - return text; -}; + var _proto = TextRenderer.prototype; -/** - * TextRenderer - * returns only the textual part of the token - */ + // no need for block level renderers + _proto.strong = function strong(text) { + return text; + }; -function TextRenderer() {} + _proto.em = function em(text) { + return text; + }; -// no need for block level renderers + _proto.codespan = function codespan(text) { + return text; + }; -TextRenderer.prototype.strong = -TextRenderer.prototype.em = -TextRenderer.prototype.codespan = -TextRenderer.prototype.del = -TextRenderer.prototype.text = function (text) { - return text; -}; + _proto.del = function del(text) { + return text; + }; -TextRenderer.prototype.link = -TextRenderer.prototype.image = function(href, title, text) { - return '' + text; -}; + _proto.html = function html(text) { + return text; + }; -TextRenderer.prototype.br = function() { - return ''; -}; + _proto.text = function text(_text) { + return _text; + }; -/** - * Parsing & Compiling - */ + _proto.link = function link(href, title, text) { + return '' + text; + }; -function Parser(options) { - this.tokens = []; - this.token = null; - this.options = options || marked.defaults; - this.options.renderer = this.options.renderer || new Renderer(); - this.renderer = this.options.renderer; - this.renderer.options = this.options; - this.slugger = new Slugger(); -} + _proto.image = function image(href, title, text) { + return '' + text; + }; -/** - * Static Parse Method - */ - -Parser.parse = function(src, options) { - var parser = new Parser(options); - return parser.parse(src); -}; - -/** - * Parse Loop - */ - -Parser.prototype.parse = function(src) { - this.inline = new InlineLexer(src.links, this.options); - // use an InlineLexer with a TextRenderer to extract pure text - this.inlineText = new InlineLexer( - src.links, - merge({}, this.options, {renderer: new TextRenderer()}) - ); - this.tokens = src.reverse(); - - var out = ''; - while (this.next()) { - out += this.tok(); - } - - return out; -}; - -/** - * Next Token - */ - -Parser.prototype.next = function() { - return this.token = this.tokens.pop(); -}; - -/** - * Preview Next Token - */ - -Parser.prototype.peek = function() { - return this.tokens[this.tokens.length - 1] || 0; -}; - -/** - * Parse Text Tokens - */ - -Parser.prototype.parseText = function() { - var body = this.token.text; - - while (this.peek().type === 'text') { - body += '\n' + this.next().text; - } - - return this.inline.output(body); -}; - -/** - * Parse Current Token - */ - -Parser.prototype.tok = function() { - switch (this.token.type) { - case 'space': { + _proto.br = function br() { return ''; + }; + + return TextRenderer; + }(); + + /** + * Slugger generates header id + */ + var Slugger_1 = /*#__PURE__*/function () { + function Slugger() { + this.seen = {}; } - case 'hr': { - return this.renderer.hr(); + + var _proto = Slugger.prototype; + + _proto.serialize = function serialize(value) { + return value.toLowerCase().trim() // remove html tags + .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars + .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-'); } - case 'heading': { - return this.renderer.heading( - this.inline.output(this.token.text), - this.token.depth, - unescape(this.inlineText.output(this.token.text)), - this.slugger); + /** + * Finds the next safe (unique) slug to use + */ + ; + + _proto.getNextSafeSlug = function getNextSafeSlug(originalSlug, isDryRun) { + var slug = originalSlug; + var occurenceAccumulator = 0; + + if (this.seen.hasOwnProperty(slug)) { + occurenceAccumulator = this.seen[originalSlug]; + + do { + occurenceAccumulator++; + slug = originalSlug + '-' + occurenceAccumulator; + } while (this.seen.hasOwnProperty(slug)); + } + + if (!isDryRun) { + this.seen[originalSlug] = occurenceAccumulator; + this.seen[slug] = 0; + } + + return slug; } - case 'code': { - return this.renderer.code(this.token.text, - this.token.lang, - this.token.escaped); + /** + * Convert string to unique id + * @param {object} options + * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator. + */ + ; + + _proto.slug = function slug(value, options) { + if (options === void 0) { + options = {}; + } + + var slug = this.serialize(value); + return this.getNextSafeSlug(slug, options.dryrun); + }; + + return Slugger; + }(); + + var defaults$4 = defaults.defaults; + var unescape$1 = helpers.unescape; + /** + * Parsing & Compiling + */ + + var Parser_1 = /*#__PURE__*/function () { + function Parser(options) { + this.options = options || defaults$4; + this.options.renderer = this.options.renderer || new Renderer_1(); + this.renderer = this.options.renderer; + this.renderer.options = this.options; + this.textRenderer = new TextRenderer_1(); + this.slugger = new Slugger_1(); } - case 'table': { - var header = '', - body = '', + /** + * Static Parse Method + */ + + + Parser.parse = function parse(tokens, options) { + var parser = new Parser(options); + return parser.parse(tokens); + } + /** + * Static Parse Inline Method + */ + ; + + Parser.parseInline = function parseInline(tokens, options) { + var parser = new Parser(options); + return parser.parseInline(tokens); + } + /** + * Parse Loop + */ + ; + + var _proto = Parser.prototype; + + _proto.parse = function parse(tokens, top) { + if (top === void 0) { + top = true; + } + + var out = '', i, + j, + k, + l2, + l3, row, cell, - j; + header, + body, + token, + ordered, + start, + loose, + itemBody, + item, + checked, + task, + checkbox; + var l = tokens.length; - // header - cell = ''; - for (i = 0; i < this.token.header.length; i++) { - cell += this.renderer.tablecell( - this.inline.output(this.token.header[i]), - { header: true, align: this.token.align[i] } - ); - } - header += this.renderer.tablerow(cell); + for (i = 0; i < l; i++) { + token = tokens[i]; - for (i = 0; i < this.token.cells.length; i++) { - row = this.token.cells[i]; + switch (token.type) { + case 'space': + { + continue; + } - cell = ''; - for (j = 0; j < row.length; j++) { - cell += this.renderer.tablecell( - this.inline.output(row[j]), - { header: false, align: this.token.align[j] } - ); + case 'hr': + { + out += this.renderer.hr(); + continue; + } + + case 'heading': + { + out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger); + continue; + } + + case 'code': + { + out += this.renderer.code(token.text, token.lang, token.escaped); + continue; + } + + case 'table': + { + header = ''; // header + + cell = ''; + l2 = token.header.length; + + for (j = 0; j < l2; j++) { + cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), { + header: true, + align: token.align[j] + }); + } + + header += this.renderer.tablerow(cell); + body = ''; + l2 = token.cells.length; + + for (j = 0; j < l2; j++) { + row = token.tokens.cells[j]; + cell = ''; + l3 = row.length; + + for (k = 0; k < l3; k++) { + cell += this.renderer.tablecell(this.parseInline(row[k]), { + header: false, + align: token.align[k] + }); + } + + body += this.renderer.tablerow(cell); + } + + out += this.renderer.table(header, body); + continue; + } + + case 'blockquote': + { + body = this.parse(token.tokens); + out += this.renderer.blockquote(body); + continue; + } + + case 'list': + { + ordered = token.ordered; + start = token.start; + loose = token.loose; + l2 = token.items.length; + body = ''; + + for (j = 0; j < l2; j++) { + item = token.items[j]; + checked = item.checked; + task = item.task; + itemBody = ''; + + if (item.task) { + checkbox = this.renderer.checkbox(checked); + + if (loose) { + if (item.tokens.length > 0 && item.tokens[0].type === 'text') { + item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; + + if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { + item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; + } + } else { + item.tokens.unshift({ + type: 'text', + text: checkbox + }); + } + } else { + itemBody += checkbox; + } + } + + itemBody += this.parse(item.tokens, loose); + body += this.renderer.listitem(itemBody, task, checked); + } + + out += this.renderer.list(body, ordered, start); + continue; + } + + case 'html': + { + // TODO parse inline content if parameter markdown=1 + out += this.renderer.html(token.text); + continue; + } + + case 'paragraph': + { + out += this.renderer.paragraph(this.parseInline(token.tokens)); + continue; + } + + case 'text': + { + body = token.tokens ? this.parseInline(token.tokens) : token.text; + + while (i + 1 < l && tokens[i + 1].type === 'text') { + token = tokens[++i]; + body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text); + } + + out += top ? this.renderer.paragraph(body) : body; + continue; + } + + default: + { + var errMsg = 'Token with "' + token.type + '" type was not found.'; + + if (this.options.silent) { + console.error(errMsg); + return; + } else { + throw new Error(errMsg); + } + } } - - body += this.renderer.tablerow(cell); - } - return this.renderer.table(header, body); - } - case 'blockquote_start': { - body = ''; - - while (this.next().type !== 'blockquote_end') { - body += this.tok(); } - return this.renderer.blockquote(body); + return out; } - case 'list_start': { - body = ''; - var ordered = this.token.ordered, - start = this.token.start; + /** + * Parse Inline Tokens + */ + ; - while (this.next().type !== 'list_end') { - body += this.tok(); - } + _proto.parseInline = function parseInline(tokens, renderer) { + renderer = renderer || this.renderer; + var out = '', + i, + token; + var l = tokens.length; - return this.renderer.list(body, ordered, start); - } - case 'list_item_start': { - body = ''; - var loose = this.token.loose; - var checked = this.token.checked; - var task = this.token.task; + for (i = 0; i < l; i++) { + token = tokens[i]; - if (this.token.task) { - body += this.renderer.checkbox(checked); - } + switch (token.type) { + case 'escape': + { + out += renderer.text(token.text); + break; + } - while (this.next().type !== 'list_item_end') { - body += !loose && this.token.type === 'text' - ? this.parseText() - : this.tok(); - } - return this.renderer.listitem(body, task, checked); - } - case 'html': { - // TODO parse inline content if parameter markdown=1 - return this.renderer.html(this.token.text); - } - case 'paragraph': { - return this.renderer.paragraph(this.inline.output(this.token.text)); - } - case 'text': { - return this.renderer.paragraph(this.parseText()); - } - default: { - var errMsg = 'Token with "' + this.token.type + '" type was not found.'; - if (this.options.silent) { - console.log(errMsg); - } else { - throw new Error(errMsg); - } - } - } -}; + case 'html': + { + out += renderer.html(token.text); + break; + } -/** - * Slugger generates header id - */ + case 'link': + { + out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer)); + break; + } -function Slugger () { - this.seen = {}; -} + case 'image': + { + out += renderer.image(token.href, token.title, token.text); + break; + } -/** - * Convert string to unique id - */ + case 'strong': + { + out += renderer.strong(this.parseInline(token.tokens, renderer)); + break; + } -Slugger.prototype.slug = function (value) { - var slug = value - .toLowerCase() - .trim() - .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') - .replace(/\s/g, '-'); + case 'em': + { + out += renderer.em(this.parseInline(token.tokens, renderer)); + break; + } - if (this.seen.hasOwnProperty(slug)) { - var originalSlug = slug; - do { - this.seen[originalSlug]++; - slug = originalSlug + '-' + this.seen[originalSlug]; - } while (this.seen.hasOwnProperty(slug)); - } - this.seen[slug] = 0; + case 'codespan': + { + out += renderer.codespan(token.text); + break; + } - return slug; -}; + case 'br': + { + out += renderer.br(); + break; + } -/** - * Helpers - */ + case 'del': + { + out += renderer.del(this.parseInline(token.tokens, renderer)); + break; + } -function escape(html, encode) { - if (encode) { - if (escape.escapeTest.test(html)) { - return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; }); - } - } else { - if (escape.escapeTestNoEncode.test(html)) { - return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; }); - } - } + case 'text': + { + out += renderer.text(token.text); + break; + } - return html; -} + default: + { + var errMsg = 'Token with "' + token.type + '" type was not found.'; -escape.escapeTest = /[&<>"']/; -escape.escapeReplace = /[&<>"']/g; -escape.replacements = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' -}; - -escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; -escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; - -function unescape(html) { - // explicitly match decimal, hex, and named HTML entities - return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) { - n = n.toLowerCase(); - if (n === 'colon') return ':'; - if (n.charAt(0) === '#') { - return n.charAt(1) === 'x' - ? String.fromCharCode(parseInt(n.substring(2), 16)) - : String.fromCharCode(+n.substring(1)); - } - return ''; - }); -} - -function edit(regex, opt) { - regex = regex.source || regex; - opt = opt || ''; - return { - replace: function(name, val) { - val = val.source || val; - val = val.replace(/(^|[^\[])\^/g, '$1'); - regex = regex.replace(name, val); - return this; - }, - getRegex: function() { - return new RegExp(regex, opt); - } - }; -} - -function cleanUrl(sanitize, base, href) { - if (sanitize) { - try { - var prot = decodeURIComponent(unescape(href)) - .replace(/[^\w:]/g, '') - .toLowerCase(); - } catch (e) { - return null; - } - if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { - return null; - } - } - if (base && !originIndependentUrl.test(href)) { - href = resolveUrl(base, href); - } - try { - href = encodeURI(href).replace(/%25/g, '%'); - } catch (e) { - return null; - } - return href; -} - -function resolveUrl(base, href) { - if (!baseUrls[' ' + base]) { - // we can ignore everything in base after the last slash of its path component, - // but we might need to add _that_ - // https://tools.ietf.org/html/rfc3986#section-3 - if (/^[^:]+:\/*[^/]*$/.test(base)) { - baseUrls[' ' + base] = base + '/'; - } else { - baseUrls[' ' + base] = rtrim(base, '/', true); - } - } - base = baseUrls[' ' + base]; - - if (href.slice(0, 2) === '//') { - return base.replace(/:[\s\S]*/, ':') + href; - } else if (href.charAt(0) === '/') { - return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href; - } else { - return base + href; - } -} -var baseUrls = {}; -var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - -function noop() {} -noop.exec = noop; - -function merge(obj) { - var i = 1, - target, - key; - - for (; i < arguments.length; i++) { - target = arguments[i]; - for (key in target) { - if (Object.prototype.hasOwnProperty.call(target, key)) { - obj[key] = target[key]; - } - } - } - - return obj; -} - -function splitCells(tableRow, count) { - // ensure that every cell-delimiting pipe has a space - // before it to distinguish it from an escaped pipe - var row = tableRow.replace(/\|/g, function (match, offset, str) { - var escaped = false, - curr = offset; - while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; - if (escaped) { - // odd number of slashes means | is escaped - // so we leave it alone - return '|'; - } else { - // add space before unescaped | - return ' |'; + if (this.options.silent) { + console.error(errMsg); + return; + } else { + throw new Error(errMsg); + } + } } - }), - cells = row.split(/ \|/), - i = 0; - - if (cells.length > count) { - cells.splice(count); - } else { - while (cells.length < count) cells.push(''); - } - - for (; i < cells.length; i++) { - // leading or trailing whitespace is ignored per the gfm spec - cells[i] = cells[i].trim().replace(/\\\|/g, '|'); - } - return cells; -} - -// Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). -// /c*$/ is vulnerable to REDOS. -// invert: Remove suffix of non-c chars instead. Default falsey. -function rtrim(str, c, invert) { - if (str.length === 0) { - return ''; - } - - // Length of suffix matching the invert condition. - var suffLen = 0; - - // Step left until we fail to match the invert condition. - while (suffLen < str.length) { - var currChar = str.charAt(str.length - suffLen - 1); - if (currChar === c && !invert) { - suffLen++; - } else if (currChar !== c && invert) { - suffLen++; - } else { - break; - } - } - - return str.substr(0, str.length - suffLen); -} - -function findClosingBracket(str, b) { - if (str.indexOf(b[1]) === -1) { - return -1; - } - var level = 0; - for (var i = 0; i < str.length; i++) { - if (str[i] === '\\') { - i++; - } else if (str[i] === b[0]) { - level++; - } else if (str[i] === b[1]) { - level--; - if (level < 0) { - return i; } + + return out; + }; + + return Parser; + }(); + + var merge$2 = helpers.merge, + checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation, + escape$2 = helpers.escape; + var getDefaults = defaults.getDefaults, + changeDefaults = defaults.changeDefaults, + defaults$5 = defaults.defaults; + /** + * Marked + */ + + function marked(src, opt, callback) { + // throw error in case of non string input + if (typeof src === 'undefined' || src === null) { + throw new Error('marked(): input parameter is undefined or null'); } - } - return -1; -} -/** - * Marked - */ + if (typeof src !== 'string') { + throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); + } -function marked(src, opt, callback) { - // throw error in case of non string input - if (typeof src === 'undefined' || src === null) { - throw new Error('marked(): input parameter is undefined or null'); - } - if (typeof src !== 'string') { - throw new Error('marked(): input parameter is of type ' - + Object.prototype.toString.call(src) + ', string expected'); - } - - if (callback || typeof opt === 'function') { - if (!callback) { + if (typeof opt === 'function') { callback = opt; opt = null; } - opt = merge({}, marked.defaults, opt || {}); + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); - var highlight = opt.highlight, - tokens, - pending, - i = 0; - - try { - tokens = Lexer.lex(src, opt); - } catch (e) { - return callback(e); - } - - pending = tokens.length; - - var done = function(err) { - if (err) { - opt.highlight = highlight; - return callback(err); - } - - var out; + if (callback) { + var highlight = opt.highlight; + var tokens; try { - out = Parser.parse(tokens, opt); + tokens = Lexer_1.lex(src, opt); } catch (e) { - err = e; + return callback(e); } - opt.highlight = highlight; + var done = function done(err) { + var out; - return err - ? callback(err) - : callback(null, out); - }; - - if (!highlight || highlight.length < 3) { - return done(); - } - - delete opt.highlight; - - if (!pending) return done(); - - for (; i < tokens.length; i++) { - (function(token) { - if (token.type !== 'code') { - return --pending || done(); - } - return highlight(token.text, token.lang, function(err, code) { - if (err) return done(err); - if (code == null || code === token.text) { - return --pending || done(); + if (!err) { + try { + out = Parser_1.parse(tokens, opt); + } catch (e) { + err = e; } - token.text = code; - token.escaped = true; - --pending || done(); - }); - })(tokens[i]); + } + + opt.highlight = highlight; + return err ? callback(err) : callback(null, out); + }; + + if (!highlight || highlight.length < 3) { + return done(); + } + + delete opt.highlight; + if (!tokens.length) return done(); + var pending = 0; + marked.walkTokens(tokens, function (token) { + if (token.type === 'code') { + pending++; + setTimeout(function () { + highlight(token.text, token.lang, function (err, code) { + if (err) { + return done(err); + } + + if (code != null && code !== token.text) { + token.text = code; + token.escaped = true; + } + + pending--; + + if (pending === 0) { + done(); + } + }); + }, 0); + } + }); + + if (pending === 0) { + done(); + } + + return; } - return; - } - try { - if (opt) opt = merge({}, marked.defaults, opt); - return Parser.parse(Lexer.lex(src, opt), opt); - } catch (e) { - e.message += '\nPlease report this to https://github.com/markedjs/marked.'; - if ((opt || marked.defaults).silent) { - return '

    An error occurred:

    '
    -        + escape(e.message + '', true)
    -        + '
    '; + try { + var _tokens = Lexer_1.lex(src, opt); + + if (opt.walkTokens) { + marked.walkTokens(_tokens, opt.walkTokens); + } + + return Parser_1.parse(_tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; + + if (opt.silent) { + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; + } + + throw e; } - throw e; } -} + /** + * Options + */ -/** - * Options - */ -marked.options = -marked.setOptions = function(opt) { - merge(marked.defaults, opt); - return marked; -}; - -marked.getDefaults = function () { - return { - baseUrl: null, - breaks: false, - gfm: true, - headerIds: true, - headerPrefix: '', - highlight: null, - langPrefix: 'language-', - mangle: true, - pedantic: false, - renderer: new Renderer(), - sanitize: false, - sanitizer: null, - silent: false, - smartLists: false, - smartypants: false, - tables: true, - xhtml: false + marked.options = marked.setOptions = function (opt) { + merge$2(marked.defaults, opt); + changeDefaults(marked.defaults); + return marked; }; -}; -marked.defaults = marked.getDefaults(); + marked.getDefaults = getDefaults; + marked.defaults = defaults$5; + /** + * Use Extension + */ -/** - * Expose - */ + marked.use = function (extension) { + var opts = merge$2({}, extension); -marked.Parser = Parser; -marked.parser = Parser.parse; + if (extension.renderer) { + (function () { + var renderer = marked.defaults.renderer || new Renderer_1(); -marked.Renderer = Renderer; -marked.TextRenderer = TextRenderer; + var _loop = function _loop(prop) { + var prevRenderer = renderer[prop]; -marked.Lexer = Lexer; -marked.lexer = Lexer.lex; + renderer[prop] = function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } -marked.InlineLexer = InlineLexer; -marked.inlineLexer = InlineLexer.output; + var ret = extension.renderer[prop].apply(renderer, args); -marked.Slugger = Slugger; + if (ret === false) { + ret = prevRenderer.apply(renderer, args); + } -marked.parse = marked; + return ret; + }; + }; -// BEGIN MONACOCHANGE -// if (typeof module !== 'undefined' && typeof exports === 'object') { -// module.exports = marked; -// } else if (typeof define === 'function' && define.amd) { -// define(function() { return marked; }); -// } else { -// root.marked = marked; -// } -// })(this || (typeof window !== 'undefined' ? window : global)); -__marked_exports = marked; -}).call(this); + for (var prop in extension.renderer) { + _loop(prop); + } -// ESM-comment-begin -define(function() { return __marked_exports; }); -// ESM-comment-end + opts.renderer = renderer; + })(); + } -// ESM-uncomment-begin -// export var marked = __marked_exports; -// export var Parser = __marked_exports.Parser; -// export var parser = __marked_exports.parser; -// export var Renderer = __marked_exports.Renderer; -// export var TextRenderer = __marked_exports.TextRenderer; -// export var Lexer = __marked_exports.Lexer; -// export var lexer = __marked_exports.lexer; -// export var InlineLexer = __marked_exports.InlineLexer; -// export var inlineLexer = __marked_exports.inlineLexer; -// export var parse = __marked_exports.parse; -// ESM-uncomment-end -// END MONACOCHANGE + if (extension.tokenizer) { + (function () { + var tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); + + var _loop2 = function _loop2(prop) { + var prevTokenizer = tokenizer[prop]; + + tokenizer[prop] = function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + var ret = extension.tokenizer[prop].apply(tokenizer, args); + + if (ret === false) { + ret = prevTokenizer.apply(tokenizer, args); + } + + return ret; + }; + }; + + for (var prop in extension.tokenizer) { + _loop2(prop); + } + + opts.tokenizer = tokenizer; + })(); + } + + if (extension.walkTokens) { + var walkTokens = marked.defaults.walkTokens; + + opts.walkTokens = function (token) { + extension.walkTokens(token); + + if (walkTokens) { + walkTokens(token); + } + }; + } + + marked.setOptions(opts); + }; + /** + * Run callback for every token + */ + + + marked.walkTokens = function (tokens, callback) { + for (var _iterator = _createForOfIteratorHelperLoose(tokens), _step; !(_step = _iterator()).done;) { + var token = _step.value; + callback(token); + + switch (token.type) { + case 'table': + { + for (var _iterator2 = _createForOfIteratorHelperLoose(token.tokens.header), _step2; !(_step2 = _iterator2()).done;) { + var cell = _step2.value; + marked.walkTokens(cell, callback); + } + + for (var _iterator3 = _createForOfIteratorHelperLoose(token.tokens.cells), _step3; !(_step3 = _iterator3()).done;) { + var row = _step3.value; + + for (var _iterator4 = _createForOfIteratorHelperLoose(row), _step4; !(_step4 = _iterator4()).done;) { + var _cell = _step4.value; + marked.walkTokens(_cell, callback); + } + } + + break; + } + + case 'list': + { + marked.walkTokens(token.items, callback); + break; + } + + default: + { + if (token.tokens) { + marked.walkTokens(token.tokens, callback); + } + } + } + } + }; + /** + * Parse Inline + */ + + + marked.parseInline = function (src, opt) { + // throw error in case of non string input + if (typeof src === 'undefined' || src === null) { + throw new Error('marked.parseInline(): input parameter is undefined or null'); + } + + if (typeof src !== 'string') { + throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); + } + + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); + + try { + var tokens = Lexer_1.lexInline(src, opt); + + if (opt.walkTokens) { + marked.walkTokens(tokens, opt.walkTokens); + } + + return Parser_1.parseInline(tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; + + if (opt.silent) { + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; + } + + throw e; + } + }; + /** + * Expose + */ + + + marked.Parser = Parser_1; + marked.parser = Parser_1.parse; + marked.Renderer = Renderer_1; + marked.TextRenderer = TextRenderer_1; + marked.Lexer = Lexer_1; + marked.lexer = Lexer_1.lex; + marked.Tokenizer = Tokenizer_1; + marked.Slugger = Slugger_1; + marked.parse = marked; + var marked_1 = marked; + + return marked_1; + +}))); diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 335ee5691e0..4af920357fd 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; +import { VSBuffer } from 'vs/base/common/buffer'; import { regExpFlags } from 'vs/base/common/strings'; +import { URI, UriComponents } from 'vs/base/common/uri'; export function stringify(obj: any): string { return JSON.stringify(obj, replacer); @@ -32,7 +33,15 @@ function replacer(key: string, value: any): any { return value; } -export function revive(obj: any, depth = 0): any { + +type Deserialize = T extends UriComponents ? URI + : T extends object + ? Revived + : T; + +export type Revived = { [K in keyof T]: Deserialize }; + +export function revive(obj: any, depth = 0): Revived { if (!obj || depth > 200) { return obj; } @@ -40,14 +49,27 @@ export function revive(obj: any, depth = 0): any { if (typeof obj === 'object') { switch ((obj).$mid) { - case 1: return URI.revive(obj); - case 2: return new RegExp(obj.source, obj.flags); + case 1: return URI.revive(obj); + case 2: return new RegExp(obj.source, obj.flags); } - // walk object (or array) - for (let key in obj) { - if (Object.hasOwnProperty.call(obj, key)) { - obj[key] = revive(obj[key], depth + 1); + if ( + obj instanceof VSBuffer + || obj instanceof Uint8Array + ) { + return obj; + } + + if (Array.isArray(obj)) { + for (let i = 0; i < obj.length; ++i) { + obj[i] = revive(obj[i], depth + 1); + } + } else { + // walk object + for (const key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + obj[key] = revive(obj[key], depth + 1); + } } } } diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 0a3a692a307..dcaca6245b6 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { basename, posix, extname } from 'vs/base/common/path'; -import { endsWith, startsWithUTF8BOM, startsWith } from 'vs/base/common/strings'; -import { coalesce } from 'vs/base/common/arrays'; +import { startsWithUTF8BOM } from 'vs/base/common/strings'; import { match } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -162,7 +161,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText let extensionMatch: ITextMimeAssociationItem | null = null; // We want to prioritize associations based on the order they are registered so that the last registered - // association wins over all other. This is for https://github.com/Microsoft/vscode/issues/20074 + // association wins over all other. This is for https://github.com/microsoft/vscode/issues/20074 for (let i = associations.length - 1; i >= 0; i--) { const association = associations[i]; @@ -185,7 +184,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText // Longest extension match if (association.extension) { if (!extensionMatch || association.extension.length > extensionMatch.extension!.length) { - if (endsWith(filename, association.extensionLowercase!)) { + if (filename.endsWith(association.extensionLowercase!)) { extensionMatch = association; } } @@ -218,7 +217,7 @@ function guessMimeTypeByFirstline(firstLine: string): string | null { if (firstLine.length > 0) { // We want to prioritize associations based on the order they are registered so that the last registered - // association wins over all other. This is for https://github.com/Microsoft/vscode/issues/20074 + // association wins over all other. This is for https://github.com/microsoft/vscode/issues/20074 for (let i = registeredAssociations.length - 1; i >= 0; i--) { const association = registeredAssociations[i]; if (!association.firstline) { @@ -247,34 +246,6 @@ export function isUnspecific(mime: string[] | string): boolean { return mime.length === 1 && isUnspecific(mime[0]); } -/** - * Returns a suggestion for the filename by the following logic: - * 1. If a relevant extension exists and is an actual filename extension (starting with a dot), suggest the prefix appended by the first one. - * 2. Otherwise, if there are other extensions, suggest the first one. - * 3. Otherwise, suggest the prefix. - */ -export function suggestFilename(mode: string | undefined, prefix: string): string { - const extensions = registeredAssociations - .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === mode) - .map(assoc => assoc.extension); - - const extensionsWithDotFirst = coalesce(extensions) - .filter(assoc => startsWith(assoc, '.')); - - if (extensionsWithDotFirst.length > 0) { - const candidateExtension = extensionsWithDotFirst[0]; - if (endsWith(prefix, candidateExtension)) { - // do not add the prefix if it already exists - // https://github.com/microsoft/vscode/issues/83603 - return prefix; - } - - return prefix + candidateExtension; - } - - return extensions[0] || prefix; -} - interface MapExtToMediaMimes { [index: string]: string; } @@ -335,3 +306,13 @@ export function getMediaMime(path: string): string | undefined { const ext = extname(path); return mapExtToMediaMimes[ext.toLowerCase()]; } + +export function getExtensionForMimeType(mimeType: string): string | undefined { + for (const extension in mapExtToMediaMimes) { + if (mapExtToMediaMimes[extension] === mimeType) { + return extension; + } + } + + return undefined; +} diff --git a/src/vs/base/common/navigator.ts b/src/vs/base/common/navigator.ts new file mode 100644 index 00000000000..ba7feffef57 --- /dev/null +++ b/src/vs/base/common/navigator.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface INavigator { + current(): T | null; + previous(): T | null; + first(): T | null; + last(): T | null; + next(): T | null; +} + +export class ArrayNavigator implements INavigator { + + constructor( + private readonly items: readonly T[], + protected start: number = 0, + protected end: number = items.length, + protected index = start - 1 + ) { } + + current(): T | null { + if (this.index === this.start - 1 || this.index === this.end) { + return null; + } + + return this.items[this.index]; + } + + next(): T | null { + this.index = Math.min(this.index + 1, this.end); + return this.current(); + } + + previous(): T | null { + this.index = Math.max(this.index - 1, this.start - 1); + return this.current(); + } + + first(): T | null { + this.index = this.start; + return this.current(); + } + + last(): T | null { + this.index = this.end - 1; + return this.current(); + } +} diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index a68e020f9f1..f475b10e5e8 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -53,6 +53,31 @@ export namespace Schemas { export const vscodeRemoteResource = 'vscode-remote-resource'; export const userData = 'vscode-userdata'; + + export const vscodeCustomEditor = 'vscode-custom-editor'; + + export const vscodeNotebook = 'vscode-notebook'; + + export const vscodeNotebookCell = 'vscode-notebook-cell'; + + export const vscodeSettings = 'vscode-settings'; + + export const webviewPanel = 'webview-panel'; + + /** + * Scheme used for loading the wrapper html and script in webviews. + */ + export const vscodeWebview = 'vscode-webview'; + + /** + * Scheme used for loading resources inside of webviews. + */ + export const vscodeWebviewResource = 'vscode-webview-resource'; + + /** + * Scheme used for extension pages + */ + export const extension = 'extension'; } class RemoteAuthoritiesImpl { @@ -104,3 +129,46 @@ class RemoteAuthoritiesImpl { } export const RemoteAuthorities = new RemoteAuthoritiesImpl(); + +class FileAccessImpl { + + /** + * Returns a URI to use in contexts where the browser is responsible + * for loading (e.g. fetch()) or when used within the DOM. + * + * **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context. + */ + asBrowserUri(uri: URI): URI; + asBrowserUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; + asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + const uri = this.toUri(uriOrModule, moduleIdToUrl); + + if (uri.scheme === Schemas.vscodeRemote) { + return RemoteAuthorities.rewrite(uri); + } + + return uri; + } + + /** + * Returns the `file` URI to use in contexts where node.js + * is responsible for loading. + */ + asFileUri(uri: URI): URI; + asFileUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; + asFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + const uri = this.toUri(uriOrModule, moduleIdToUrl); + + return uri; + } + + private toUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + if (URI.isUri(uriOrModule)) { + return uriOrModule; + } + + return URI.parse(moduleIdToUrl!.toUrl(uriOrModule)); + } +} + +export const FileAccess = new FileAccessImpl(); diff --git a/src/vs/base/common/normalization.ts b/src/vs/base/common/normalization.ts index b6304df31a0..3a94fb716ec 100644 --- a/src/vs/base/common/normalization.ts +++ b/src/vs/base/common/normalization.ts @@ -11,7 +11,7 @@ import { LRUCache } from 'vs/base/common/map'; * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize} */ -export const canNormalize = typeof (('').normalize) === 'function'; +export const canNormalize = typeof (String.prototype as any /* standalone editor compilation */).normalize === 'function'; const nfcCache = new LRUCache(10000); // bounded to 10000 elements export function normalizeNFC(str: string): string { @@ -46,3 +46,17 @@ function normalize(str: string, form: string, normalizedCache: LRUCache string = (function () { + if (!canNormalize) { + // no ES6 features... + return function (str: string) { return str; }; + } else { + // transform into NFD form and remove accents + // see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463 + const regex = /[\u0300-\u036f]/g; + return function (str: string) { + return normalizeNFD(str).replace(regex, ''); + }; + } +})(); diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index 1c694e1d84a..9edfddfe533 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -18,3 +18,19 @@ export class Counter { return this._next++; } } + +export class MovingAverage { + + private _n = 1; + private _val = 0; + + update(value: number): this { + this._val = this._val + (value - this._val) / this._n; + this._n += 1; + return this; + } + + get value(): number { + return this._val; + } +} diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 1475bf4a550..2abc22e51e1 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -10,7 +10,7 @@ export function deepClone(obj: T): T { return obj; } if (obj instanceof RegExp) { - // See https://github.com/Microsoft/TypeScript/issues/10990 + // See https://github.com/microsoft/TypeScript/issues/10990 return obj as any; } const result: any = Array.isArray(obj) ? [] : {}; @@ -113,15 +113,6 @@ export function mixin(destination: any, source: any, overwrite: boolean = true): return destination; } -export function assign(destination: T): T; -export function assign(destination: T, u: U): T & U; -export function assign(destination: T, u: U, v: V): T & U & V; -export function assign(destination: T, u: U, v: V, w: W): T & U & V & W; -export function assign(destination: any, ...sources: any[]): any { - sources.forEach(source => Object.keys(source).forEach(key => destination[key] = source[key])); - return destination; -} - export function equals(one: any, other: any): boolean { if (one === other) { return true; @@ -176,18 +167,18 @@ export function equals(one: any, other: any): boolean { } /** - * Calls JSON.Stringify with a replacer to break apart any circular references. - * This prevents JSON.stringify from throwing the exception + * Calls `JSON.Stringify` with a replacer to break apart any circular references. + * This prevents `JSON`.stringify` from throwing the exception * "Uncaught TypeError: Converting circular structure to JSON" */ export function safeStringify(obj: any): string { - const seen: any[] = []; + const seen = new Set(); return JSON.stringify(obj, (key, value) => { if (isObject(value) || Array.isArray(value)) { - if (seen.indexOf(value) !== -1) { + if (seen.has(value)) { return '[Circular]'; } else { - seen.push(value); + seen.add(value); } } return value; @@ -229,3 +220,9 @@ export function distinct(base: obj, target: obj): obj { return result; } + +export function getCaseInsensitive(target: obj, key: string): any { + const lowercaseKey = key.toLowerCase(); + const equivalentKey = Object.keys(target).find(k => k.toLowerCase() === lowercaseKey); + return equivalentKey ? target[equivalentKey] : target[key]; +} diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 5f1739053bb..d12384b6f16 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace -// Copied from: https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158 +// Copied from: https://github.com/nodejs/node/blob/v12.8.1/lib/path.js /** * Copyright Joyent, Inc. and other Node contributors. @@ -88,7 +88,7 @@ function normalizeString(path: string, allowAboveRoot: boolean, separator: strin let lastSegmentLength = 0; let lastSlash = -1; let dots = 0; - let code; + let code = 0; for (let i = 0; i <= path.length; ++i) { if (i < path.length) { code = path.charCodeAt(i); @@ -103,7 +103,7 @@ function normalizeString(path: string, allowAboveRoot: boolean, separator: strin if (isPathSeparator(code)) { if (lastSlash === i - 1 || dots === 1) { // NOOP - } else if (lastSlash !== i - 1 && dots === 2) { + } else if (dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT || res.charCodeAt(res.length - 2) !== CHAR_DOT) { @@ -119,7 +119,7 @@ function normalizeString(path: string, allowAboveRoot: boolean, separator: strin lastSlash = i; dots = 0; continue; - } else if (res.length === 2 || res.length === 1) { + } else if (res.length !== 0) { res = ''; lastSegmentLength = 0; lastSlash = i; @@ -128,17 +128,12 @@ function normalizeString(path: string, allowAboveRoot: boolean, separator: strin } } if (allowAboveRoot) { - if (res.length > 0) { - res += `${separator}..`; - } - else { - res = '..'; - } + res += res.length > 0 ? `${separator}..` : '..'; lastSegmentLength = 2; } } else { if (res.length > 0) { - res += separator + path.slice(lastSlash + 1, i); + res += `${separator}${path.slice(lastSlash + 1, i)}`; } else { res = path.slice(lastSlash + 1, i); @@ -157,16 +152,16 @@ function normalizeString(path: string, allowAboveRoot: boolean, separator: strin } function _format(sep: string, pathObject: ParsedPath) { + if (pathObject === null || typeof pathObject !== 'object') { + throw new ErrorInvalidArgType('pathObject', 'Object', pathObject); + } const dir = pathObject.dir || pathObject.root; const base = pathObject.base || - ((pathObject.name || '') + (pathObject.ext || '')); + `${pathObject.name || ''}${pathObject.ext || ''}`; if (!dir) { return base; } - if (dir === pathObject.root) { - return dir + base; - } - return dir + sep + base; + return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; } export interface ParsedPath { @@ -206,7 +201,13 @@ export const win32: IPath = { let path; if (i >= 0) { path = pathSegments[i]; - } else if (!resolvedDevice) { + validateString(path, 'path'); + + // Skip empty entries + if (path.length === 0) { + continue; + } + } else if (resolvedDevice.length === 0) { path = process.cwd(); } else { // Windows has the concept of drive-specific current working @@ -214,24 +215,17 @@ export const win32: IPath = { // absolute path, get cwd for that drive, or the process cwd if // the drive cwd is not available. We're sure the device is not // a UNC path at this points, because UNC paths are always absolute. - path = (process.env as any)['=' + resolvedDevice] || process.cwd(); + path = (process.env as any)[`=${resolvedDevice}`] || process.cwd(); // Verify that a cwd was found and that it actually points // to our drive. If not, default to the drive's root. if (path === undefined || - path.slice(0, 3).toLowerCase() !== - resolvedDevice.toLowerCase() + '\\') { - path = resolvedDevice + '\\'; + path.slice(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() && + path.charCodeAt(2) === CHAR_BACKWARD_SLASH) { + path = `${resolvedDevice}\\`; } } - validateString(path, 'path'); - - // Skip empty entries - if (path.length === 0) { - continue; - } - const len = path.length; let rootEnd = 0; let device = ''; @@ -239,98 +233,86 @@ export const win32: IPath = { const code = path.charCodeAt(0); // Try to match a root - if (len > 1) { + if (len === 1) { if (isPathSeparator(code)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an - // absolute path of some kind (UNC or otherwise) + // `path` contains just a path separator + rootEnd = 1; isAbsolute = true; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } - } - if (j < len && j !== last) { - const firstPart = path.slice(last, j); - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) { - break; - } - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } - } - if (j === len) { - // We matched a UNC root only - - device = '\\\\' + firstPart + '\\' + path.slice(last); - rootEnd = j; - } else if (j !== last) { - // We matched a UNC root with leftovers - - device = '\\\\' + firstPart + '\\' + path.slice(last, j); - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - device = path.slice(0, 2); - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - // Treat separator following drive name as an absolute path - // indicator - isAbsolute = true; - rootEnd = 3; - } - } - } } } else if (isPathSeparator(code)) { - // `path` contains just a path separator - rootEnd = 1; + // Possible UNC root + + // If we started with a separator, we know we at least have an + // absolute path of some kind (UNC or otherwise) isAbsolute = true; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; + } + if (j < len && j !== last) { + const firstPart = path.slice(last, j); + // Matched! + last = j; + // Match 1 or more path separators + while (j < len && isPathSeparator(path.charCodeAt(j))) { + j++; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; + } + if (j === len || j !== last) { + // We matched a UNC root + device = `\\\\${firstPart}\\${path.slice(last, j)}`; + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRoot(code) && + path.charCodeAt(1) === CHAR_COLON) { + // Possible device root + device = path.slice(0, 2); + rootEnd = 2; + if (len > 2 && isPathSeparator(path.charCodeAt(2))) { + // Treat separator following drive name as an absolute path + // indicator + isAbsolute = true; + rootEnd = 3; + } } - if (device.length > 0 && - resolvedDevice.length > 0 && - device.toLowerCase() !== resolvedDevice.toLowerCase()) { - // This path points to another device so it is not applicable - continue; + if (device.length > 0) { + if (resolvedDevice.length > 0) { + if (device.toLowerCase() !== resolvedDevice.toLowerCase()) { + // This path points to another device so it is not applicable + continue; + } + } else { + resolvedDevice = device; + } } - if (resolvedDevice.length === 0 && device.length > 0) { - resolvedDevice = device; - } - if (!resolvedAbsolute) { - resolvedTail = path.slice(rootEnd) + '\\' + resolvedTail; + if (resolvedAbsolute) { + if (resolvedDevice.length > 0) { + break; + } + } else { + resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`; resolvedAbsolute = isAbsolute; - } - - if (resolvedDevice.length > 0 && resolvedAbsolute) { - break; + if (isAbsolute && resolvedDevice.length > 0) { + break; + } } } @@ -342,8 +324,9 @@ export const win32: IPath = { resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isPathSeparator); - return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || - '.'; + return resolvedAbsolute ? + `${resolvedDevice}\\${resolvedTail}` : + `${resolvedDevice}${resolvedTail}` || '.'; }, normalize(path: string): string { @@ -358,89 +341,72 @@ export const win32: IPath = { const code = path.charCodeAt(0); // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root + if (len === 1) { + // `path` contains just a single char, exit early to avoid + // unnecessary work + return isPosixPathSeparator(code) ? '\\' : path; + } + if (isPathSeparator(code)) { + // Possible UNC root - // If we started with a separator, we know we at least have an absolute - // path of some kind (UNC or otherwise) - isAbsolute = true; + // If we started with a separator, we know we at least have an absolute + // path of some kind (UNC or otherwise) + isAbsolute = true; - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; + } + if (j < len && j !== last) { + const firstPart = path.slice(last, j); + // Matched! + last = j; + // Match 1 or more path separators + while (j < len && isPathSeparator(path.charCodeAt(j))) { + j++; } if (j < len && j !== last) { - const firstPart = path.slice(last, j); // Matched! last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) { - break; - } + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } - } - if (j === len) { - // We matched a UNC root only - // Return the normalized version of the UNC root since there - // is nothing left to process - - return '\\\\' + firstPart + '\\' + path.slice(last) + '\\'; - } else if (j !== last) { - // We matched a UNC root with leftovers - - device = '\\\\' + firstPart + '\\' + path.slice(last, j); - rootEnd = j; - } + if (j === len) { + // We matched a UNC root only + // Return the normalized version of the UNC root since there + // is nothing left to process + return `\\\\${firstPart}\\${path.slice(last)}\\`; } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - device = path.slice(0, 2); - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - // Treat separator following drive name as an absolute path - // indicator - isAbsolute = true; - rootEnd = 3; + if (j !== last) { + // We matched a UNC root with leftovers + device = `\\\\${firstPart}\\${path.slice(last, j)}`; + rootEnd = j; } } } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { + // Possible device root + device = path.slice(0, 2); + rootEnd = 2; + if (len > 2 && isPathSeparator(path.charCodeAt(2))) { + // Treat separator following drive name as an absolute path + // indicator + isAbsolute = true; + rootEnd = 3; } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid unnecessary - // work - return '\\'; } - let tail; - if (rootEnd < len) { - tail = normalizeString(path.slice(rootEnd), !isAbsolute, '\\', - isPathSeparator); - } else { - tail = ''; - } + let tail = rootEnd < len ? + normalizeString(path.slice(rootEnd), !isAbsolute, '\\', isPathSeparator) : + ''; if (tail.length === 0 && !isAbsolute) { tail = '.'; } @@ -448,30 +414,9 @@ export const win32: IPath = { tail += '\\'; } if (device === undefined) { - if (isAbsolute) { - if (tail.length > 0) { - return '\\' + tail; - } - else { - return '\\'; - } - } else if (tail.length > 0) { - return tail; - } else { - return ''; - } - } else if (isAbsolute) { - if (tail.length > 0) { - return device + '\\' + tail; - } - else { - return device + '\\'; - } - } else if (tail.length > 0) { - return device + tail; - } else { - return device; + return isAbsolute ? `\\${tail}` : tail; } + return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; }, isAbsolute(path: string): boolean { @@ -482,18 +427,12 @@ export const win32: IPath = { } const code = path.charCodeAt(0); - if (isPathSeparator(code)) { - return true; - } else if (isWindowsDeviceRoot(code)) { + return isPathSeparator(code) || // Possible device root - - if (len > 2 && path.charCodeAt(1) === CHAR_COLON) { - if (isPathSeparator(path.charCodeAt(2))) { - return true; - } - } - } - return false; + len > 2 && + isWindowsDeviceRoot(code) && + path.charCodeAt(1) === CHAR_COLON && + isPathSeparator(path.charCodeAt(2)); }, join(...paths: string[]): string { @@ -511,7 +450,7 @@ export const win32: IPath = { joined = firstPart = arg; } else { - joined += '\\' + arg; + joined += `\\${arg}`; } } } @@ -538,32 +477,28 @@ export const win32: IPath = { if (typeof firstPart === 'string' && isPathSeparator(firstPart.charCodeAt(0))) { ++slashCount; const firstLen = firstPart.length; - if (firstLen > 1) { - if (isPathSeparator(firstPart.charCodeAt(1))) { - ++slashCount; - if (firstLen > 2) { - if (isPathSeparator(firstPart.charCodeAt(2))) { - ++slashCount; - } - else { - // We matched a UNC path in the first part - needsReplace = false; - } + if (firstLen > 1 && isPathSeparator(firstPart.charCodeAt(1))) { + ++slashCount; + if (firstLen > 2) { + if (isPathSeparator(firstPart.charCodeAt(2))) { + ++slashCount; + } else { + // We matched a UNC path in the first part + needsReplace = false; } } } } if (needsReplace) { // Find any more consecutive slashes we need to replace - for (; slashCount < joined.length; ++slashCount) { - if (!isPathSeparator(joined.charCodeAt(slashCount))) { - break; - } + while (slashCount < joined.length && + isPathSeparator(joined.charCodeAt(slashCount))) { + slashCount++; } // Replace the slashes if needed if (slashCount >= 2) { - joined = '\\' + joined.slice(slashCount); + joined = `\\${joined.slice(slashCount)}`; } } @@ -599,111 +534,102 @@ export const win32: IPath = { // Trim any leading backslashes let fromStart = 0; - for (; fromStart < from.length; ++fromStart) { - if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) { - break; - } + while (fromStart < from.length && + from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) { + fromStart++; } // Trim trailing backslashes (applicable to UNC paths only) let fromEnd = from.length; - for (; fromEnd - 1 > fromStart; --fromEnd) { - if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) { - break; - } + while (fromEnd - 1 > fromStart && + from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) { + fromEnd--; } - const fromLen = (fromEnd - fromStart); + const fromLen = fromEnd - fromStart; // Trim any leading backslashes let toStart = 0; - for (; toStart < to.length; ++toStart) { - if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) { - break; - } + while (toStart < to.length && + to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { + toStart++; } // Trim trailing backslashes (applicable to UNC paths only) let toEnd = to.length; - for (; toEnd - 1 > toStart; --toEnd) { - if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) { - break; - } + while (toEnd - 1 > toStart && + to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) { + toEnd--; } - const toLen = (toEnd - toStart); + const toLen = toEnd - toStart; // Compare paths to find the longest common path from root - const length = (fromLen < toLen ? fromLen : toLen); + const length = fromLen < toLen ? fromLen : toLen; let lastCommonSep = -1; let i = 0; - for (; i <= length; ++i) { - if (i === length) { - if (toLen > length) { - if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' - return toOrig.slice(toStart + i + 1); - } else if (i === 2) { - // We get here if `from` is the device root. - // For example: from='C:\\'; to='C:\\foo' - return toOrig.slice(toStart + i); - } - } - if (fromLen > length) { - if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='C:\\foo\\bar'; to='C:\\foo' - lastCommonSep = i; - } else if (i === 2) { - // We get here if `to` is the device root. - // For example: from='C:\\foo\\bar'; to='C:\\' - lastCommonSep = 3; - } - } - break; - } + for (; i < length; i++) { const fromCode = from.charCodeAt(fromStart + i); - const toCode = to.charCodeAt(toStart + i); - if (fromCode !== toCode) { + if (fromCode !== to.charCodeAt(toStart + i)) { break; - } - else if (fromCode === CHAR_BACKWARD_SLASH) { + } else if (fromCode === CHAR_BACKWARD_SLASH) { lastCommonSep = i; } } // We found a mismatch before the first common path separator was seen, so // return the original `to`. - if (i !== length && lastCommonSep === -1) { - return toOrig; + if (i !== length) { + if (lastCommonSep === -1) { + return toOrig; + } + } else { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' + return toOrig.slice(toStart + i + 1); + } + if (i === 2) { + // We get here if `from` is the device root. + // For example: from='C:\\'; to='C:\\foo' + return toOrig.slice(toStart + i); + } + } + if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='C:\\foo\\bar'; to='C:\\foo' + lastCommonSep = i; + } else if (i === 2) { + // We get here if `to` is the device root. + // For example: from='C:\\foo\\bar'; to='C:\\' + lastCommonSep = 3; + } + } + if (lastCommonSep === -1) { + lastCommonSep = 0; + } } let out = ''; - if (lastCommonSep === -1) { - lastCommonSep = 0; - } // Generate the relative path based on the path difference between `to` and // `from` for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { - if (out.length === 0) { - out += '..'; - } - else { - out += '\\..'; - } + out += out.length === 0 ? '..' : '\\..'; } } + toStart += lastCommonSep; + // Lastly, append the rest of the destination (`to`) path that comes after // the common path parts if (out.length > 0) { - return out + toOrig.slice(toStart + lastCommonSep, toEnd); + return `${out}${toOrig.slice(toStart, toEnd)}`; } - else { - toStart += lastCommonSep; - if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { - ++toStart; - } - return toOrig.slice(toStart, toEnd); + + if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { + ++toStart; } + + return toOrig.slice(toStart, toEnd); }, toNamespacedPath(path: string): string { @@ -718,26 +644,24 @@ export const win32: IPath = { const resolvedPath = win32.resolve(path); - if (resolvedPath.length >= 3) { - if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { - // Possible UNC root + if (resolvedPath.length <= 2) { + return path; + } - if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { - const code = resolvedPath.charCodeAt(2); - if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { - // Matched non-long UNC root, convert the path to a long UNC path - return '\\\\?\\UNC\\' + resolvedPath.slice(2); - } - } - } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { - // Possible device root - - if (resolvedPath.charCodeAt(1) === CHAR_COLON && - resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) { - // Matched device root, convert the path to a long UNC path - return '\\\\?\\' + resolvedPath; + if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { + // Possible UNC root + if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { + const code = resolvedPath.charCodeAt(2); + if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { + // Matched non-long UNC root, convert the path to a long UNC path + return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; } } + } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) && + resolvedPath.charCodeAt(1) === CHAR_COLON && + resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) { + // Matched device root, convert the path to a long UNC path + return `\\\\?\\${resolvedPath}`; } return path; @@ -750,78 +674,65 @@ export const win32: IPath = { return '.'; } let rootEnd = -1; - let end = -1; - let matchedSlash = true; let offset = 0; const code = path.charCodeAt(0); + if (len === 1) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work or a dot. + return isPathSeparator(code) ? path : '.'; + } + // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root + if (isPathSeparator(code)) { + // Possible UNC root - rootEnd = offset = 1; + rootEnd = offset = 1; - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + while (j < len && isPathSeparator(path.charCodeAt(j))) { + j++; } if (j < len && j !== last) { // Matched! last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) { - break; - } + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } - } - if (j === len) { - // We matched a UNC root only - return path; - } - if (j !== last) { - // We matched a UNC root with leftovers - - // Offset by 1 to include the separator after the UNC root to - // treat it as a "normal root" on top of a (UNC) root - rootEnd = offset = j + 1; - } + if (j === len) { + // We matched a UNC root only + return path; } - } - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root + if (j !== last) { + // We matched a UNC root with leftovers - if (path.charCodeAt(1) === CHAR_COLON) { - rootEnd = offset = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - rootEnd = offset = 3; + // Offset by 1 to include the separator after the UNC root to + // treat it as a "normal root" on top of a (UNC) root + rootEnd = offset = j + 1; } } } } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - return path; + // Possible device root + } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { + rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2; + offset = rootEnd; } + let end = -1; + let matchedSlash = true; for (let i = len - 1; i >= offset; --i) { if (isPathSeparator(path.charCodeAt(i))) { if (!matchedSlash) { @@ -838,9 +749,8 @@ export const win32: IPath = { if (rootEnd === -1) { return '.'; } - else { - end = rootEnd; - } + + end = rootEnd; } return path.slice(0, end); }, @@ -858,17 +768,14 @@ export const win32: IPath = { // Check for a drive letter prefix so as not to mistake the following // path separator as an extra separator at the end of the path that can be // disregarded - if (path.length >= 2) { - const drive = path.charCodeAt(0); - if (isWindowsDeviceRoot(drive)) { - if (path.charCodeAt(1) === CHAR_COLON) { - start = 2; - } - } + if (path.length >= 2 && + isWindowsDeviceRoot(path.charCodeAt(0)) && + path.charCodeAt(1) === CHAR_COLON) { + start = 2; } if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext.length === path.length && ext === path) { + if (ext === path) { return ''; } let extIdx = ext.length - 1; @@ -909,33 +816,31 @@ export const win32: IPath = { if (start === end) { end = firstNonSlashEnd; - } - else if (end === -1) { + } else if (end === -1) { end = path.length; } return path.slice(start, end); - } else { - for (i = path.length - 1; i >= start; --i) { - if (isPathSeparator(path.charCodeAt(i))) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; - } - } - - if (end === -1) { - return ''; - } - return path.slice(start, end); } + for (i = path.length - 1; i >= start; --i) { + if (isPathSeparator(path.charCodeAt(i))) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) { + return ''; + } + return path.slice(start, end); }, extname(path: string): string { @@ -1004,14 +909,7 @@ export const win32: IPath = { return path.slice(startDot, end); }, - format(pathObject): string { - if (pathObject === null || typeof pathObject !== 'object') { - throw new ErrorInvalidArgType('pathObject', 'Object', pathObject); - } - - return _format('\\', pathObject); - }, - + format: _format.bind(null, '\\'), parse(path) { validateString(path, 'path'); @@ -1025,82 +923,72 @@ export const win32: IPath = { let rootEnd = 0; let code = path.charCodeAt(0); - // Try to match a root - if (len > 1) { + if (len === 1) { if (isPathSeparator(code)) { - // Possible UNC root + // `path` contains just a path separator, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + ret.base = ret.name = path; + return ret; + } + // Try to match a root + if (isPathSeparator(code)) { + // Possible UNC root - rootEnd = 1; - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } + rootEnd = 1; + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + while (j < len && isPathSeparator(path.charCodeAt(j))) { + j++; } if (j < len && j !== last) { // Matched! last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) { - break; - } + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + j++; } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) { - break; - } - } - if (j === len) { - // We matched a UNC root only - - rootEnd = j; - } else if (j !== last) { - // We matched a UNC root with leftovers - - rootEnd = j + 1; - } + if (j === len) { + // We matched a UNC root only + rootEnd = j; + } else if (j !== last) { + // We matched a UNC root with leftovers + rootEnd = j + 1; } } } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - if (len === 3) { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - rootEnd = 3; - } - } else { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - } } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; + } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { + // Possible device root + if (len <= 2) { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + rootEnd = 2; + if (isPathSeparator(path.charCodeAt(2))) { + if (len === 3) { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + rootEnd = 3; + } } - if (rootEnd > 0) { ret.root = path.slice(0, rootEnd); } @@ -1137,8 +1025,7 @@ export const win32: IPath = { // If this is our first dot, mark it as the start of our extension if (startDot === -1) { startDot = i; - } - else if (preDotState !== 1) { + } else if (preDotState !== 1) { preDotState = 1; } } else if (startDot !== -1) { @@ -1148,21 +1035,20 @@ export const win32: IPath = { } } - if (startDot === -1 || - end === -1 || - // We saw a non-dot character immediately before the dot - preDotState === 0 || - // The (right-most) trimmed path component is exactly '..' - (preDotState === 1 && - startDot === end - 1 && - startDot === startPart + 1)) { - if (end !== -1) { + if (end !== -1) { + if (startDot === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { ret.base = ret.name = path.slice(startPart, end); + } else { + ret.name = path.slice(startPart, startDot); + ret.base = path.slice(startPart, end); + ret.ext = path.slice(startDot, end); } - } else { - ret.name = path.slice(startPart, startDot); - ret.base = path.slice(startPart, end); - ret.ext = path.slice(startDot, end); } // If the directory is the root, use the entire root as the `dir` including @@ -1170,8 +1056,7 @@ export const win32: IPath = { // trailing slash (`C:\abc\def` -> `C:\abc`). if (startPart > 0 && startPart !== rootEnd) { ret.dir = path.slice(0, startPart - 1); - } - else { + } else { ret.dir = ret.root; } @@ -1191,13 +1076,7 @@ export const posix: IPath = { let resolvedAbsolute = false; for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - let path; - if (i >= 0) { - path = pathSegments[i]; - } - else { - path = process.cwd(); - } + const path = i >= 0 ? pathSegments[i] : process.cwd(); validateString(path, 'path'); @@ -1206,7 +1085,7 @@ export const posix: IPath = { continue; } - resolvedPath = path + '/' + resolvedPath; + resolvedPath = `${path}/${resolvedPath}`; resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; } @@ -1218,17 +1097,9 @@ export const posix: IPath = { isPosixPathSeparator); if (resolvedAbsolute) { - if (resolvedPath.length > 0) { - return '/' + resolvedPath; - } - else { - return '/'; - } - } else if (resolvedPath.length > 0) { - return resolvedPath; - } else { - return '.'; + return `/${resolvedPath}`; } + return resolvedPath.length > 0 ? resolvedPath : '.'; }, normalize(path: string): string { @@ -1245,17 +1116,17 @@ export const posix: IPath = { // Normalize the path path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator); - if (path.length === 0 && !isAbsolute) { - path = '.'; + if (path.length === 0) { + if (isAbsolute) { + return '/'; + } + return trailingSeparator ? './' : '.'; } - if (path.length > 0 && trailingSeparator) { + if (trailingSeparator) { path += '/'; } - if (isAbsolute) { - return '/' + path; - } - return path; + return isAbsolute ? `/${path}` : path; }, isAbsolute(path: string): boolean { @@ -1269,14 +1140,13 @@ export const posix: IPath = { } let joined; for (let i = 0; i < paths.length; ++i) { - const arg = arguments[i]; + const arg = paths[i]; validateString(arg, 'path'); if (arg.length > 0) { if (joined === undefined) { joined = arg; - } - else { - joined += '/' + arg; + } else { + joined += `/${arg}`; } } } @@ -1294,6 +1164,7 @@ export const posix: IPath = { return ''; } + // Trim leading forward slashes. from = posix.resolve(from); to = posix.resolve(to); @@ -1301,91 +1172,61 @@ export const posix: IPath = { return ''; } - // Trim any leading backslashes - let fromStart = 1; - for (; fromStart < from.length; ++fromStart) { - if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) { - break; - } - } + const fromStart = 1; const fromEnd = from.length; - const fromLen = (fromEnd - fromStart); - - // Trim any leading backslashes - let toStart = 1; - for (; toStart < to.length; ++toStart) { - if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) { - break; - } - } - const toEnd = to.length; - const toLen = (toEnd - toStart); + const fromLen = fromEnd - fromStart; + const toStart = 1; + const toLen = to.length - toStart; // Compare paths to find the longest common path from root const length = (fromLen < toLen ? fromLen : toLen); let lastCommonSep = -1; let i = 0; - for (; i <= length; ++i) { - if (i === length) { - if (toLen > length) { - if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='/foo/bar'; to='/foo/bar/baz' - return to.slice(toStart + i + 1); - } else if (i === 0) { - // We get here if `from` is the root - // For example: from='/'; to='/foo' - return to.slice(toStart + i); - } - } else if (fromLen > length) { - if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='/foo/bar/baz'; to='/foo/bar' - lastCommonSep = i; - } else if (i === 0) { - // We get here if `to` is the root. - // For example: from='/foo'; to='/' - lastCommonSep = 0; - } - } - break; - } + for (; i < length; i++) { const fromCode = from.charCodeAt(fromStart + i); - const toCode = to.charCodeAt(toStart + i); - if (fromCode !== toCode) { + if (fromCode !== to.charCodeAt(toStart + i)) { break; - } - else if (fromCode === CHAR_FORWARD_SLASH) { + } else if (fromCode === CHAR_FORWARD_SLASH) { lastCommonSep = i; } } + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='/foo/bar'; to='/foo/bar/baz' + return to.slice(toStart + i + 1); + } + if (i === 0) { + // We get here if `from` is the root + // For example: from='/'; to='/foo' + return to.slice(toStart + i); + } + } else if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='/foo/bar/baz'; to='/foo/bar' + lastCommonSep = i; + } else if (i === 0) { + // We get here if `to` is the root. + // For example: from='/foo/bar'; to='/' + lastCommonSep = 0; + } + } + } let out = ''; // Generate the relative path based on the path difference between `to` - // and `from` + // and `from`. for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { - if (out.length === 0) { - out += '..'; - } - else { - out += '/..'; - } + out += out.length === 0 ? '..' : '/..'; } } // Lastly, append the rest of the destination (`to`) path that comes after - // the common path parts - if (out.length > 0) { - return out + to.slice(toStart + lastCommonSep); - } - else { - toStart += lastCommonSep; - if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) { - ++toStart; - } - return to.slice(toStart); - } + // the common path parts. + return `${out}${to.slice(toStart + lastCommonSep)}`; }, toNamespacedPath(path: string): string { @@ -1434,7 +1275,7 @@ export const posix: IPath = { let i; if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext.length === path.length && ext === path) { + if (ext === path) { return ''; } let extIdx = ext.length - 1; @@ -1475,33 +1316,31 @@ export const posix: IPath = { if (start === end) { end = firstNonSlashEnd; - } - else if (end === -1) { + } else if (end === -1) { end = path.length; } return path.slice(start, end); - } else { - for (i = path.length - 1; i >= 0; --i) { - if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; - } - } - - if (end === -1) { - return ''; - } - return path.slice(start, end); } + for (i = path.length - 1; i >= 0; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) { + return ''; + } + return path.slice(start, end); }, extname(path: string): string { @@ -1558,13 +1397,7 @@ export const posix: IPath = { return path.slice(startDot, end); }, - format(pathObject): string { - if (pathObject === null || typeof pathObject !== 'object') { - throw new ErrorInvalidArgType('pathObject', 'Object', pathObject); - } - - return _format('/', pathObject); - }, + format: _format.bind(null, '/'), parse(path: string): ParsedPath { validateString(path, 'path'); @@ -1613,8 +1446,7 @@ export const posix: IPath = { // If this is our first dot, mark it as the start of our extension if (startDot === -1) { startDot = i; - } - else if (preDotState !== 1) { + } else if (preDotState !== 1) { preDotState = 1; } } else if (startDot !== -1) { @@ -1624,37 +1456,26 @@ export const posix: IPath = { } } - if (startDot === -1 || - end === -1 || - // We saw a non-dot character immediately before the dot - preDotState === 0 || - // The (right-most) trimmed path component is exactly '..' - (preDotState === 1 && - startDot === end - 1 && - startDot === startPart + 1)) { - if (end !== -1) { - if (startPart === 0 && isAbsolute) { - ret.base = ret.name = path.slice(1, end); - } - else { - ret.base = ret.name = path.slice(startPart, end); - } - } - } else { - if (startPart === 0 && isAbsolute) { - ret.name = path.slice(1, startDot); - ret.base = path.slice(1, end); + if (end !== -1) { + const start = startPart === 0 && isAbsolute ? 1 : startPart; + if (startDot === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + ret.base = ret.name = path.slice(start, end); } else { - ret.name = path.slice(startPart, startDot); - ret.base = path.slice(startPart, end); + ret.name = path.slice(start, startDot); + ret.base = path.slice(start, end); + ret.ext = path.slice(startDot, end); } - ret.ext = path.slice(startDot, end); } if (startPart > 0) { ret.dir = path.slice(0, startPart - 1); - } - else if (isAbsolute) { + } else if (isAbsolute) { ret.dir = '/'; } diff --git a/src/vs/base/common/performance.d.ts b/src/vs/base/common/performance.d.ts index 657f250d8b3..a26c1ebd731 100644 --- a/src/vs/base/common/performance.d.ts +++ b/src/vs/base/common/performance.d.ts @@ -5,7 +5,7 @@ export interface PerformanceEntry { readonly name: string; - readonly timestamp: number; + readonly startTime: number; } export function mark(name: string): void; @@ -15,8 +15,6 @@ export function mark(name: string): void; */ export function getEntries(): PerformanceEntry[]; -export function getEntry(name: string): PerformanceEntry; - export function getDuration(from: string, to: string): number; type ExportData = any[]; diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index 16960cea069..5893db44495 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -9,45 +9,33 @@ function _factory(sharedObj) { - sharedObj._performanceEntries = sharedObj._performanceEntries || []; + sharedObj.MonacoPerformanceMarks = sharedObj.MonacoPerformanceMarks || []; const _dataLen = 2; const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; function importEntries(entries) { - sharedObj._performanceEntries.splice(0, 0, ...entries); + sharedObj.MonacoPerformanceMarks.splice(0, 0, ...entries); } function exportEntries() { - return sharedObj._performanceEntries.slice(0); + return sharedObj.MonacoPerformanceMarks.slice(0); } function getEntries() { const result = []; - const entries = sharedObj._performanceEntries; + const entries = sharedObj.MonacoPerformanceMarks; for (let i = 0; i < entries.length; i += _dataLen) { result.push({ name: entries[i], - timestamp: entries[i + 1], + startTime: entries[i + 1], }); } return result; } - function getEntry(name) { - const entries = sharedObj._performanceEntries; - for (let i = 0; i < entries.length; i += _dataLen) { - if (entries[i] === name) { - return { - name: entries[i], - timestamp: entries[i + 1], - }; - } - } - } - function getDuration(from, to) { - const entries = sharedObj._performanceEntries; + const entries = sharedObj.MonacoPerformanceMarks; let target = to; let endIndex = 0; for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) { @@ -66,14 +54,13 @@ function _factory(sharedObj) { } function mark(name) { - sharedObj._performanceEntries.push(name, Date.now()); + sharedObj.MonacoPerformanceMarks.push(name, Date.now()); _timeStamp(name); } const exports = { mark: mark, getEntries: getEntries, - getEntry: getEntry, getDuration: getDuration, importEntries: importEntries, exportEntries: exportEntries @@ -86,7 +73,8 @@ function _factory(sharedObj) { // Because we want both instances to use the same perf-data // we store them globally -let sharedObj; +// eslint-disable-next-line no-var +var sharedObj; if (typeof global === 'object') { // nodejs sharedObj = global; @@ -104,5 +92,5 @@ if (typeof define === 'function') { // commonjs module.exports = _factory(sharedObj); } else { - // invalid context... + sharedObj.perf = _factory(sharedObj); } diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 5a631e0b395..3361d83be5b 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -26,15 +26,16 @@ export interface IProcessEnvironment { [key: string]: string; } -interface INodeProcess { - platform: string; +export interface INodeProcess { + platform: 'win32' | 'linux' | 'darwin'; env: IProcessEnvironment; - getuid(): number; nextTick: Function; versions?: { electron?: string; }; type?: string; + getuid(): number; + cwd(): string; } declare const process: INodeProcess; declare const global: any; @@ -47,25 +48,39 @@ interface INavigator { declare const navigator: INavigator; declare const self: any; -const isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer'); +const _globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {} as any); -// OS detection +let nodeProcess: INodeProcess | undefined = undefined; +if (typeof process !== 'undefined') { + // Native environment (non-sandboxed) + nodeProcess = process; +} else if (typeof _globals.vscode !== 'undefined') { + // Native envionment (sandboxed) + nodeProcess = _globals.vscode.process; +} + +const isElectronRenderer = typeof nodeProcess?.versions?.electron === 'string' && nodeProcess.type === 'renderer'; + +// Web environment if (typeof navigator === 'object' && !isElectronRenderer) { _userAgent = navigator.userAgent; _isWindows = _userAgent.indexOf('Windows') >= 0; _isMacintosh = _userAgent.indexOf('Macintosh') >= 0; - _isIOS = _userAgent.indexOf('Macintosh') >= 0 && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0; + _isIOS = (_userAgent.indexOf('Macintosh') >= 0 || _userAgent.indexOf('iPad') >= 0 || _userAgent.indexOf('iPhone') >= 0) && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0; _isLinux = _userAgent.indexOf('Linux') >= 0; _isWeb = true; _locale = navigator.language; _language = _locale; -} else if (typeof process === 'object') { - _isWindows = (process.platform === 'win32'); - _isMacintosh = (process.platform === 'darwin'); - _isLinux = (process.platform === 'linux'); +} + +// Native environment +else if (typeof nodeProcess === 'object') { + _isWindows = (nodeProcess.platform === 'win32'); + _isMacintosh = (nodeProcess.platform === 'darwin'); + _isLinux = (nodeProcess.platform === 'linux'); _locale = LANGUAGE_DEFAULT; _language = LANGUAGE_DEFAULT; - const rawNlsConfig = process.env['VSCODE_NLS_CONFIG']; + const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG']; if (rawNlsConfig) { try { const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); @@ -80,6 +95,11 @@ if (typeof navigator === 'object' && !isElectronRenderer) { _isNative = true; } +// Unknown environment +else { + console.error('Unable to resolve platform.'); +} + export const enum Platform { Web, Mac, @@ -114,7 +134,7 @@ export const platform = _platform; export const userAgent = _userAgent; export function isRootUser(): boolean { - return _isNative && !_isWindows && (process.getuid() === 0); + return !!nodeProcess && !_isWindows && (nodeProcess.getuid() === 0); } /** @@ -157,7 +177,6 @@ export const locale = _locale; */ export const translationsConfigFile = _translationsConfigFile; -const _globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {} as any); export const globals: any = _globals; interface ISetImmediate { @@ -196,8 +215,8 @@ export const setImmediate: ISetImmediate = (function defineSetImmediate() { globals.postMessage({ vscodeSetImmediateId: myId }, '*'); }; } - if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - return process.nextTick.bind(process); + if (nodeProcess) { + return nodeProcess.nextTick.bind(nodeProcess); } const _promise = Promise.resolve(); return (callback: (...args: any[]) => void) => _promise.then(callback); @@ -208,4 +227,18 @@ export const enum OperatingSystem { Macintosh = 2, Linux = 3 } -export const OS = (_isMacintosh ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux)); +export const OS = (_isMacintosh || _isIOS ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux)); + +let _isLittleEndian = true; +let _isLittleEndianComputed = false; +export function isLittleEndian(): boolean { + if (!_isLittleEndianComputed) { + _isLittleEndianComputed = true; + const test = new Uint8Array(2); + test[0] = 1; + test[1] = 2; + const view = new Uint16Array(test.buffer); + _isLittleEndian = (view[0] === (2 << 8) + 1); + } + return _isLittleEndian; +} diff --git a/src/vs/base/common/process.ts b/src/vs/base/common/process.ts index bd23d743d3e..5e85d672116 100644 --- a/src/vs/base/common/process.ts +++ b/src/vs/base/common/process.ts @@ -3,23 +3,36 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isWindows, isMacintosh, setImmediate, IProcessEnvironment } from 'vs/base/common/platform'; +import { isWindows, isMacintosh, setImmediate, globals, INodeProcess } from 'vs/base/common/platform'; -interface IProcess { - platform: string; - env: IProcessEnvironment; +declare const process: INodeProcess; - cwd(): string; - nextTick(callback: (...args: any[]) => void): void; +let safeProcess: INodeProcess; + +// Native node.js environment +if (typeof process !== 'undefined') { + safeProcess = process; } -declare const process: IProcess; -const safeProcess: IProcess = (typeof process === 'undefined') ? { - cwd(): string { return '/'; }, - env: Object.create(null), - get platform(): string { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; }, - nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); } -} : process; +// Native sandbox environment +else if (typeof globals.vscode !== 'undefined') { + safeProcess = globals.vscode.process; +} + +// Web environment +else { + safeProcess = { + + // Supported + get platform(): 'win32' | 'linux' | 'darwin' { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; }, + nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); }, + + // Unsupported + get env() { return Object.create(null); }, + cwd(): string { return '/'; }, + getuid(): number { return -1; } + }; +} export const cwd = safeProcess.cwd; export const env = safeProcess.env; diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index c52f7b3774f..17895a8510b 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -110,7 +110,8 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve /^ELECTRON_.+$/, /^GOOGLE_API_KEY$/, /^VSCODE_.+$/, - /^SNAP(|_.*)$/ + /^SNAP(|_.*)$/, + /^GDK_PIXBUF_.+$/, ]; const envKeys = Object.keys(env); envKeys diff --git a/src/vs/base/common/resourceTree.ts b/src/vs/base/common/resourceTree.ts index 4adca4ca4f2..23f661e1b80 100644 --- a/src/vs/base/common/resourceTree.ts +++ b/src/vs/base/common/resourceTree.ts @@ -5,10 +5,8 @@ import { memoize } from 'vs/base/common/decorators'; import * as paths from 'vs/base/common/path'; -import { Iterator } from 'vs/base/common/iterator'; import { relativePath, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { mapValues } from 'vs/base/common/collections'; import { PathIterator } from 'vs/base/common/map'; export interface IResourceNode { @@ -16,7 +14,7 @@ export interface IResourceNode { readonly relativePath: string; readonly name: string; readonly element: T | undefined; - readonly children: Iterator>; + readonly children: Iterable>; readonly childrenCount: number; readonly parent: IResourceNode | undefined; readonly context: C; @@ -31,8 +29,8 @@ class Node implements IResourceNode { return this._children.size; } - get children(): Iterator> { - return Iterator.fromArray(mapValues(this._children)); + get children(): Iterable> { + return this._children.values(); } @memoize @@ -70,7 +68,9 @@ function collect(node: IResourceNode, result: T[]): T[] { result.push(node.element); } - Iterator.forEach(node.children, child => collect(child, result)); + for (const child of node.children) { + collect(child, result); + } return result; } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index bea40bbdc62..621c97368c7 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -5,269 +5,374 @@ import * as extpath from 'vs/base/common/extpath'; import * as paths from 'vs/base/common/path'; -import { URI } from 'vs/base/common/uri'; -import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { URI, uriToFsPath } from 'vs/base/common/uri'; +import { equalsIgnoreCase, compare as strCompare } from 'vs/base/common/strings'; import { Schemas } from 'vs/base/common/network'; -import { isLinux, isWindows } from 'vs/base/common/platform'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; import { TernarySearchTree } from 'vs/base/common/map'; -export function getComparisonKey(resource: URI): string { - return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString(); -} - -export function hasToIgnoreCase(resource: URI | undefined): boolean { - // A file scheme resource is in the same platform as code, so ignore case for non linux platforms - // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider - return resource && resource.scheme === Schemas.file ? !isLinux : true; -} - -export function basenameOrAuthority(resource: URI): string { - return basename(resource) || resource.authority; -} - -/** - * Tests whether a `candidate` URI is a parent or equal of a given `base` URI. - * @param base A uri which is "longer" - * @param parentCandidate A uri which is "shorter" then `base` - */ -export function isEqualOrParent(base: URI, parentCandidate: URI, ignoreCase = hasToIgnoreCase(base)): boolean { - if (base.scheme === parentCandidate.scheme) { - if (base.scheme === Schemas.file) { - return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), ignoreCase); - } - if (isEqualAuthority(base.authority, parentCandidate.authority)) { - return extpath.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/'); - } - } - return false; -} - -/** - * Tests wheter the two authorities are the same - */ -export function isEqualAuthority(a1: string, a2: string) { - return a1 === a2 || equalsIgnoreCase(a1, a2); -} - -export function isEqual(first: URI | undefined, second: URI | undefined, ignoreCase = hasToIgnoreCase(first)): boolean { - if (first === second) { - return true; - } - - if (!first || !second) { - return false; - } - - if (first.scheme !== second.scheme || !isEqualAuthority(first.authority, second.authority)) { - return false; - } - - const p1 = first.path || '/', p2 = second.path || '/'; - return p1 === p2 || ignoreCase && equalsIgnoreCase(p1 || '/', p2 || '/'); -} - -export function basename(resource: URI): string { - return paths.posix.basename(resource.path); -} - -export function extname(resource: URI): string { - return paths.posix.extname(resource.path); -} - -/** - * Return a URI representing the directory of a URI path. - * - * @param resource The input URI. - * @returns The URI representing the directory of the input URI. - */ -export function dirname(resource: URI): URI { - if (resource.path.length === 0) { - return resource; - } - if (resource.scheme === Schemas.file) { - return URI.file(paths.dirname(originalFSPath(resource))); - } - let dirname = paths.posix.dirname(resource.path); - if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { - console.error(`dirname("${resource.toString})) resulted in a relative path`); - dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character - } - return resource.with({ - path: dirname - }); -} - -/** - * Join a URI path with path fragments and normalizes the resulting path. - * - * @param resource The input URI. - * @param pathFragment The path fragment to add to the URI path. - * @returns The resulting URI. - */ -export function joinPath(resource: URI, ...pathFragment: string[]): URI { - let joinedPath: string; - if (resource.scheme === Schemas.file) { - joinedPath = URI.file(paths.join(originalFSPath(resource), ...pathFragment)).path; - } else { - joinedPath = paths.posix.join(resource.path || '/', ...pathFragment); - } - return resource.with({ - path: joinedPath - }); -} - -/** - * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names. - * - * @param resource The URI to normalize the path. - * @returns The URI with the normalized path. - */ -export function normalizePath(resource: URI): URI { - if (!resource.path.length) { - return resource; - } - let normalizedPath: string; - if (resource.scheme === Schemas.file) { - normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; - } else { - normalizedPath = paths.posix.normalize(resource.path); - } - return resource.with({ - path: normalizedPath - }); -} - -/** - * Returns the fsPath of an URI where the drive letter is not normalized. - * See #56403. - */ export function originalFSPath(uri: URI): string { - let value: string; - const uriPath = uri.path; - if (uri.authority && uriPath.length > 1 && uri.scheme === Schemas.file) { - // unc path: file://shares/c$/far/boo - value = `//${uri.authority}${uriPath}`; - } else if ( - isWindows - && uriPath.charCodeAt(0) === CharCode.Slash - && extpath.isWindowsDriveLetter(uriPath.charCodeAt(1)) - && uriPath.charCodeAt(2) === CharCode.Colon - ) { - value = uriPath.substr(1); - } else { - // other path - value = uriPath; - } - if (isWindows) { - value = value.replace(/\//g, '\\'); - } - return value; + return uriToFsPath(uri, true); } -/** - * Returns true if the URI path is absolute. - */ -export function isAbsolutePath(resource: URI): boolean { - return !!resource.path && resource.path[0] === '/'; +//#region IExtUri + +export interface IExtUri { + + // --- identity + + /** + * Compares two uris. + * + * @param uri1 Uri + * @param uri2 Uri + * @param ignoreFragment Ignore the fragment (defaults to `false`) + */ + compare(uri1: URI, uri2: URI, ignoreFragment?: boolean): number; + + /** + * Tests whether two uris are equal + * + * @param uri1 Uri + * @param uri2 Uri + * @param ignoreFragment Ignore the fragment (defaults to `false`) + */ + isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment?: boolean): boolean; + + /** + * Tests whether a `candidate` URI is a parent or equal of a given `base` URI. + * + * @param base A uri which is "longer" + * @param parentCandidate A uri which is "shorter" then `base` + * @param ignoreFragment Ignore the fragment (defaults to `false`) + */ + isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment?: boolean): boolean; + + /** + * Creates a key from a resource URI to be used to resource comparison and for resource maps. + * @see ResourceMap + * @param uri Uri + * @param ignoreFragment Ignore the fragment (defaults to `false`) + */ + getComparisonKey(uri: URI, ignoreFragment?: boolean): string; + + // --- path math + + basenameOrAuthority(resource: URI): string; + + /** + * Returns the basename of the path component of an uri. + * @param resource + */ + basename(resource: URI): string; + + /** + * Returns the extension of the path component of an uri. + * @param resource + */ + extname(resource: URI): string; + /** + * Return a URI representing the directory of a URI path. + * + * @param resource The input URI. + * @returns The URI representing the directory of the input URI. + */ + dirname(resource: URI): URI; + /** + * Join a URI path with path fragments and normalizes the resulting path. + * + * @param resource The input URI. + * @param pathFragment The path fragment to add to the URI path. + * @returns The resulting URI. + */ + joinPath(resource: URI, ...pathFragment: string[]): URI + /** + * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names. + * + * @param resource The URI to normalize the path. + * @returns The URI with the normalized path. + */ + normalizePath(resource: URI): URI; + /** + * + * @param from + * @param to + */ + relativePath(from: URI, to: URI): string | undefined; + /** + * Resolves an absolute or relative path against a base URI. + * The path can be relative or absolute posix or a Windows path + */ + resolvePath(base: URI, path: string): URI; + + // --- misc + + /** + * Returns true if the URI path is absolute. + */ + isAbsolutePath(resource: URI): boolean; + /** + * Tests whether the two authorities are the same + */ + isEqualAuthority(a1: string, a2: string): boolean; + /** + * Returns true if the URI path has a trailing path separator + */ + hasTrailingPathSeparator(resource: URI, sep?: string): boolean; + /** + * Removes a trailing path separator, if there's one. + * Important: Doesn't remove the first slash, it would make the URI invalid + */ + removeTrailingPathSeparator(resource: URI, sep?: string): URI; + /** + * Adds a trailing path separator to the URI if there isn't one already. + * For example, c:\ would be unchanged, but c:\users would become c:\users\ + */ + addTrailingPathSeparator(resource: URI, sep?: string): URI; } -/** - * Returns true if the URI path has a trailing path separator - */ -export function hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean { - if (resource.scheme === Schemas.file) { - const fsp = originalFSPath(resource); - return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep; - } else { - const p = resource.path; - return p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; // ignore the slash at offset 0 - } -} +export class ExtUri implements IExtUri { -/** - * Removes a trailing path separator, if there's one. - * Important: Doesn't remove the first slash, it would make the URI invalid - */ -export function removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { - if (hasTrailingPathSeparator(resource, sep)) { - return resource.with({ path: resource.path.substr(0, resource.path.length - 1) }); - } - return resource; -} + constructor(private _ignorePathCasing: (uri: URI) => boolean) { } -/** - * Adds a trailing path separator to the URI if there isn't one already. - * For example, c:\ would be unchanged, but c:\users would become c:\users\ - */ -export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { - let isRootSep: boolean = false; - if (resource.scheme === Schemas.file) { - const fsp = originalFSPath(resource); - isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep)); - } else { - sep = '/'; - const p = resource.path; - isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; + compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number { + if (uri1 === uri2) { + return 0; + } + return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment)); } - if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { - return resource.with({ path: resource.path + '/' }); - } - return resource; -} -/** - * Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned. - * The returned relative path always uses forward slashes. - */ -export function relativePath(from: URI, to: URI, ignoreCase = hasToIgnoreCase(from)): string | undefined { - if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { - return undefined; + isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean { + if (uri1 === uri2) { + return true; + } + if (!uri1 || !uri2) { + return false; + } + return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment); } - if (from.scheme === Schemas.file) { - const relativePath = paths.relative(from.path, to.path); - return isWindows ? extpath.toSlashes(relativePath) : relativePath; + + getComparisonKey(uri: URI, ignoreFragment: boolean = false): string { + return uri.with({ + path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined, + fragment: ignoreFragment ? null : undefined + }).toString(); } - let fromPath = from.path || '/', toPath = to.path || '/'; - if (ignoreCase) { - // make casing of fromPath match toPath - let i = 0; - for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) { - if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) { - if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) { - break; - } + + isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean { + if (base.scheme === parentCandidate.scheme) { + if (base.scheme === Schemas.file) { + return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), this._ignorePathCasing(base)) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment); + } + if (isEqualAuthority(base.authority, parentCandidate.authority)) { + return extpath.isEqualOrParent(base.path, parentCandidate.path, this._ignorePathCasing(base), '/') && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment); } } - fromPath = toPath.substr(0, i) + fromPath.substr(i); + return false; } - return paths.posix.relative(fromPath, toPath); -} -/** - * Resolves an absolute or relative path against a base URI. - * The path can be relative or absolute posix or a Windows path - */ -export function resolvePath(base: URI, path: string): URI { - if (base.scheme === Schemas.file) { - const newURI = URI.file(paths.resolve(originalFSPath(base), path)); - return base.with({ - authority: newURI.authority, - path: newURI.path + // --- path math + + joinPath(resource: URI, ...pathFragment: string[]): URI { + return URI.joinPath(resource, ...pathFragment); + } + + basenameOrAuthority(resource: URI): string { + return basename(resource) || resource.authority; + } + + basename(resource: URI): string { + return paths.posix.basename(resource.path); + } + + extname(resource: URI): string { + return paths.posix.extname(resource.path); + } + + dirname(resource: URI): URI { + if (resource.path.length === 0) { + return resource; + } + let dirname; + if (resource.scheme === Schemas.file) { + dirname = URI.file(paths.dirname(originalFSPath(resource))).path; + } else { + dirname = paths.posix.dirname(resource.path); + if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { + console.error(`dirname("${resource.toString})) resulted in a relative path`); + dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character + } + } + return resource.with({ + path: dirname }); } - if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path - path = extpath.toSlashes(path); - if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter - path = '/' + path; + + normalizePath(resource: URI): URI { + if (!resource.path.length) { + return resource; + } + let normalizedPath: string; + if (resource.scheme === Schemas.file) { + normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; + } else { + normalizedPath = paths.posix.normalize(resource.path); + } + return resource.with({ + path: normalizedPath + }); + } + + relativePath(from: URI, to: URI): string | undefined { + if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { + return undefined; + } + if (from.scheme === Schemas.file) { + const relativePath = paths.relative(originalFSPath(from), originalFSPath(to)); + return isWindows ? extpath.toSlashes(relativePath) : relativePath; + } + let fromPath = from.path || '/', toPath = to.path || '/'; + if (this._ignorePathCasing(from)) { + // make casing of fromPath match toPath + let i = 0; + for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) { + if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) { + if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) { + break; + } + } + } + fromPath = toPath.substr(0, i) + fromPath.substr(i); + } + return paths.posix.relative(fromPath, toPath); + } + + resolvePath(base: URI, path: string): URI { + if (base.scheme === Schemas.file) { + const newURI = URI.file(paths.resolve(originalFSPath(base), path)); + return base.with({ + authority: newURI.authority, + path: newURI.path + }); + } + if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path + path = extpath.toSlashes(path); + if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter + path = '/' + path; + } + } + return base.with({ + path: paths.posix.resolve(base.path, path) + }); + } + + // --- misc + + isAbsolutePath(resource: URI): boolean { + return !!resource.path && resource.path[0] === '/'; + } + + isEqualAuthority(a1: string, a2: string) { + return a1 === a2 || equalsIgnoreCase(a1, a2); + } + + hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean { + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep; + } else { + const p = resource.path; + return (p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0 } } - return base.with({ - path: paths.posix.resolve(base.path, path) - }); + + removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + // Make sure that the path isn't a drive letter. A trailing separator there is not removable. + if (hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path.substr(0, resource.path.length - 1) }); + } + return resource; + } + + addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + let isRootSep: boolean = false; + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep)); + } else { + sep = '/'; + const p = resource.path; + isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; + } + if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path + '/' }); + } + return resource; + } } + +/** + * Unbiased utility that takes uris "as they are". This means it can be interchanged with + * uri#toString() usages. The following is true + * ``` + * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri)) + * ``` + */ +export const extUri = new ExtUri(() => false); + +/** + * BIASED utility that _mostly_ ignored the case of urs paths. ONLY use this util if you + * understand what you are doing. + * + * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged. + * + * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient + * because those uris come from a "trustworthy source". When creating unknown uris it's always + * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path + * casing matters. + */ +export const extUriBiasedIgnorePathCase = new ExtUri(uri => { + // A file scheme resource is in the same platform as code, so ignore case for non linux platforms + // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider + return uri.scheme === Schemas.file ? !isLinux : true; +}); + + +/** + * BIASED utility that always ignores the casing of uris paths. ONLY use this util if you + * understand what you are doing. + * + * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged. + * + * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient + * because those uris come from a "trustworthy source". When creating unknown uris it's always + * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path + * casing matters. + */ +export const extUriIgnorePathCase = new ExtUri(_ => true); + +export const isEqual = extUri.isEqual.bind(extUri); +export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri); +export const getComparisonKey = extUri.getComparisonKey.bind(extUri); +export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri); +export const basename = extUri.basename.bind(extUri); +export const extname = extUri.extname.bind(extUri); +export const dirname = extUri.dirname.bind(extUri); +export const joinPath = extUri.joinPath.bind(extUri); +export const normalizePath = extUri.normalizePath.bind(extUri); +export const relativePath = extUri.relativePath.bind(extUri); +export const resolvePath = extUri.resolvePath.bind(extUri); +export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri); +export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri); +export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri); +export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri); +export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri); + +//#endregion + export function distinctParents(items: T[], resourceAccessor: (item: T) => URI): T[] { const distinctParents: T[] = []; for (let i = 0; i < items.length; i++) { @@ -325,7 +430,7 @@ export namespace DataUri { export class ResourceGlobMatcher { private readonly globalExpression: ParsedExpression; - private readonly expressionsByRoot: TernarySearchTree<{ root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); + private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>(); constructor( globalExpression: IExpression, @@ -333,12 +438,12 @@ export class ResourceGlobMatcher { ) { this.globalExpression = parse(globalExpression); for (const expression of rootExpressions) { - this.expressionsByRoot.set(expression.root.toString(), { root: expression.root, expression: parse(expression.expression) }); + this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) }); } } matches(resource: URI): boolean { - const rootExpression = this.expressionsByRoot.findSubstr(resource.toString()); + const rootExpression = this.expressionsByRoot.findSubstr(resource); if (rootExpression) { const path = relativePath(rootExpression.root, resource); if (path && !!rootExpression.expression(path)) { @@ -349,15 +454,15 @@ export class ResourceGlobMatcher { } } -export function toLocalResource(resource: URI, authority: string | undefined): URI { +export function toLocalResource(resource: URI, authority: string | undefined, localScheme: string): URI { if (authority) { let path = resource.path; if (path && path[0] !== paths.posix.sep) { path = paths.posix.sep + path; } - return resource.with({ scheme: Schemas.vscodeRemote, authority, path }); + return resource.with({ scheme: localScheme, authority, path }); } - return resource.with({ scheme: Schemas.file }); + return resource.with({ scheme: localScheme }); } diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index faee10bed56..cb3fe20dc79 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -13,10 +13,18 @@ export const enum ScrollbarVisibility { } export interface ScrollEvent { + oldWidth: number; + oldScrollWidth: number; + oldScrollLeft: number; + width: number; scrollWidth: number; scrollLeft: number; + oldHeight: number; + oldScrollHeight: number; + oldScrollTop: number; + height: number; scrollHeight: number; scrollTop: number; @@ -33,6 +41,9 @@ export interface ScrollEvent { export class ScrollState implements IScrollDimensions, IScrollPosition { _scrollStateBrand: void; + public readonly rawScrollLeft: number; + public readonly rawScrollTop: number; + public readonly width: number; public readonly scrollWidth: number; public readonly scrollLeft: number; @@ -55,6 +66,9 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { scrollHeight = scrollHeight | 0; scrollTop = scrollTop | 0; + this.rawScrollLeft = scrollLeft; // before validation + this.rawScrollTop = scrollTop; // before validation + if (width < 0) { width = 0; } @@ -85,7 +99,9 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { public equals(other: ScrollState): boolean { return ( - this.width === other.width + this.rawScrollLeft === other.rawScrollLeft + && this.rawScrollTop === other.rawScrollTop + && this.width === other.width && this.scrollWidth === other.scrollWidth && this.scrollLeft === other.scrollLeft && this.height === other.height @@ -94,14 +110,14 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { ); } - public withScrollDimensions(update: INewScrollDimensions): ScrollState { + public withScrollDimensions(update: INewScrollDimensions, useRawScrollPositions: boolean): ScrollState { return new ScrollState( (typeof update.width !== 'undefined' ? update.width : this.width), (typeof update.scrollWidth !== 'undefined' ? update.scrollWidth : this.scrollWidth), - this.scrollLeft, + useRawScrollPositions ? this.rawScrollLeft : this.scrollLeft, (typeof update.height !== 'undefined' ? update.height : this.height), (typeof update.scrollHeight !== 'undefined' ? update.scrollHeight : this.scrollHeight), - this.scrollTop + useRawScrollPositions ? this.rawScrollTop : this.scrollTop ); } @@ -109,10 +125,10 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { return new ScrollState( this.width, this.scrollWidth, - (typeof update.scrollLeft !== 'undefined' ? update.scrollLeft : this.scrollLeft), + (typeof update.scrollLeft !== 'undefined' ? update.scrollLeft : this.rawScrollLeft), this.height, this.scrollHeight, - (typeof update.scrollTop !== 'undefined' ? update.scrollTop : this.scrollTop) + (typeof update.scrollTop !== 'undefined' ? update.scrollTop : this.rawScrollTop) ); } @@ -126,10 +142,18 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { const scrollTopChanged = (this.scrollTop !== previous.scrollTop); return { + oldWidth: previous.width, + oldScrollWidth: previous.scrollWidth, + oldScrollLeft: previous.scrollLeft, + width: this.width, scrollWidth: this.scrollWidth, scrollLeft: this.scrollLeft, + oldHeight: previous.height, + oldScrollHeight: previous.scrollHeight, + oldScrollTop: previous.scrollTop, + height: this.height, scrollHeight: this.scrollHeight, scrollTop: this.scrollTop, @@ -216,8 +240,8 @@ export class Scrollable extends Disposable { return this._state; } - public setScrollDimensions(dimensions: INewScrollDimensions): void { - const newState = this._state.withScrollDimensions(dimensions); + public setScrollDimensions(dimensions: INewScrollDimensions, useRawScrollPositions: boolean): void { + const newState = this._state.withScrollDimensions(dimensions, useRawScrollPositions); this._setState(newState); // Validate outstanding animated scroll position target @@ -308,6 +332,12 @@ export class Scrollable extends Disposable { this._setState(newState); + if (!this._smoothScrolling) { + // Looks like someone canceled the smooth scrolling + // from the scroll event handler + return; + } + if (update.isDone) { this._smoothScrolling.dispose(); this._smoothScrolling = null; diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts index 7b57f1e5a66..2eec434e9a7 100644 --- a/src/vs/base/common/search.ts +++ b/src/vs/base/common/search.ts @@ -18,7 +18,7 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa return pattern.toUpperCase(); } else if (matches[0].toLowerCase() === matches[0]) { return pattern.toLowerCase(); - } else if (strings.containsUppercaseCharacter(matches[0][0])) { + } else if (strings.containsUppercaseCharacter(matches[0][0]) && pattern.length > 0) { return pattern[0].toUpperCase() + pattern.substr(1); } else { // we don't understand its pattern yet. diff --git a/src/vs/base/common/semver/cgmanifest.json b/src/vs/base/common/semver/cgmanifest.json new file mode 100644 index 00000000000..bbb20d6a05f --- /dev/null +++ b/src/vs/base/common/semver/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "semver", + "repositoryUrl": "https://github.com/npm/node-semver", + "commitHash": "44cbc8482ac4f0f8d2de0abb7f8808056d2d55f9" + } + }, + "license": "The ISC License", + "version": "5.5.0" + } + ], + "version": 1 +} diff --git a/src/vs/base/common/semver/semver.d.ts b/src/vs/base/common/semver/semver.d.ts new file mode 100644 index 00000000000..61a55bac94f --- /dev/null +++ b/src/vs/base/common/semver/semver.d.ts @@ -0,0 +1,312 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export as namespace semver; + +export = semver; + +declare namespace semver { + + // Type definitions for semver 6.2 + // Project: https://github.com/npm/node-semver + // Definitions by: Bart van der Schoor + // BendingBender + // Lucian Buzzo + // Klaus Meinhardt + // ExE Boss + // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/semver + + export const SEMVER_SPEC_VERSION: "2.0.0"; + + export type ReleaseType = "major" | "premajor" | "minor" | "preminor" | "patch" | "prepatch" | "prerelease"; + + export interface Options { + loose?: boolean; + includePrerelease?: boolean; + } + + export interface CoerceOptions extends Options { + /** + * Used by `coerce()` to coerce from right to left. + * + * @default false + * + * @example + * coerce('1.2.3.4', { rtl: true }); + * // => SemVer { version: '2.3.4', ... } + * + * @since 6.2.0 + */ + rtl?: boolean; + } + + /** + * Return the parsed version as a SemVer object, or null if it's not valid. + */ + export function parse(version: string | SemVer | null | undefined, optionsOrLoose?: boolean | Options): SemVer | null; + + /** + * Return the parsed version as a string, or null if it's not valid. + */ + export function valid(version: string | SemVer | null | undefined, optionsOrLoose?: boolean | Options): string | null; + + /** + * Coerces a string to SemVer if possible + */ + export function coerce(version: string | number | SemVer | null | undefined, options?: CoerceOptions): SemVer | null; + + /** + * Returns cleaned (removed leading/trailing whitespace, remove '=v' prefix) and parsed version, or null if version is invalid. + */ + export function clean(version: string, optionsOrLoose?: boolean | Options): string | null; + + /** + * Return the version incremented by the release type (major, minor, patch, or prerelease), or null if it's not valid. + */ + export function inc(version: string | SemVer, release: ReleaseType, optionsOrLoose?: boolean | Options, identifier?: string): string | null; + export function inc(version: string | SemVer, release: ReleaseType, identifier?: string): string | null; + + /** + * Return the major version number. + */ + export function major(version: string | SemVer, optionsOrLoose?: boolean | Options): number; + + /** + * Return the minor version number. + */ + export function minor(version: string | SemVer, optionsOrLoose?: boolean | Options): number; + + /** + * Return the patch version number. + */ + export function patch(version: string | SemVer, optionsOrLoose?: boolean | Options): number; + + /** + * Returns an array of prerelease components, or null if none exist. + */ + export function prerelease(version: string | SemVer, optionsOrLoose?: boolean | Options): ReadonlyArray | null; + + // Comparison + /** + * v1 > v2 + */ + export function gt(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + /** + * v1 >= v2 + */ + export function gte(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + /** + * v1 < v2 + */ + export function lt(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + /** + * v1 <= v2 + */ + export function lte(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + /** + * v1 == v2 This is true if they're logically equivalent, even if they're not the exact same string. You already know how to compare strings. + */ + export function eq(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + /** + * v1 != v2 The opposite of eq. + */ + export function neq(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + + /** + * Pass in a comparison string, and it'll call the corresponding semver comparison function. + * "===" and "!==" do simple string comparison, but are included for completeness. + * Throws if an invalid comparison string is provided. + */ + export function cmp(v1: string | SemVer, operator: Operator, v2: string | SemVer, optionsOrLoose?: boolean | Options): boolean; + export type Operator = '===' | '!==' | '' | '=' | '==' | '!=' | '>' | '>=' | '<' | '<='; + + /** + * Compares two versions excluding build identifiers (the bit after `+` in the semantic version string). + * + * Sorts in ascending order when passed to `Array.sort()`. + * + * @return + * - `0` if `v1` == `v2` + * - `1` if `v1` is greater + * - `-1` if `v2` is greater. + */ + export function compare(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): 1 | 0 | -1; + /** + * The reverse of compare. + * + * Sorts in descending order when passed to `Array.sort()`. + */ + export function rcompare(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): 1 | 0 | -1; + + /** + * Compares two identifiers, must be numeric strings or truthy/falsy values. + * + * Sorts in ascending order when passed to `Array.sort()`. + */ + export function compareIdentifiers(a: string | null | undefined, b: string | null | undefined): 1 | 0 | -1; + /** + * The reverse of compareIdentifiers. + * + * Sorts in descending order when passed to `Array.sort()`. + */ + export function rcompareIdentifiers(a: string | null | undefined, b: string | null | undefined): 1 | 0 | -1; + + /** + * Compares two versions including build identifiers (the bit after `+` in the semantic version string). + * + * Sorts in ascending order when passed to `Array.sort()`. + * + * @return + * - `0` if `v1` == `v2` + * - `1` if `v1` is greater + * - `-1` if `v2` is greater. + * + * @since 6.1.0 + */ + export function compareBuild(a: string | SemVer, b: string | SemVer): 1 | 0 | -1; + + /** + * Sorts an array of semver entries in ascending order using `compareBuild()`. + */ + export function sort(list: T[], optionsOrLoose?: boolean | Options): T[]; + /** + * Sorts an array of semver entries in descending order using `compareBuild()`. + */ + export function rsort(list: T[], optionsOrLoose?: boolean | Options): T[]; + + /** + * Returns difference between two versions by the release type (major, premajor, minor, preminor, patch, prepatch, or prerelease), or null if the versions are the same. + */ + export function diff(v1: string | SemVer, v2: string | SemVer, optionsOrLoose?: boolean | Options): ReleaseType | null; + + // Ranges + /** + * Return the valid range or null if it's not valid + */ + export function validRange(range: string | Range | null | undefined, optionsOrLoose?: boolean | Options): string; + /** + * Return true if the version satisfies the range. + */ + export function satisfies(version: string | SemVer, range: string | Range, optionsOrLoose?: boolean | Options): boolean; + /** + * Return the highest version in the list that satisfies the range, or null if none of them do. + */ + export function maxSatisfying(versions: ReadonlyArray, range: string | Range, optionsOrLoose?: boolean | Options): T | null; + /** + * Return the lowest version in the list that satisfies the range, or null if none of them do. + */ + export function minSatisfying(versions: ReadonlyArray, range: string | Range, optionsOrLoose?: boolean | Options): T | null; + /** + * Return the lowest version that can possibly match the given range. + */ + export function minVersion(range: string | Range, optionsOrLoose?: boolean | Options): SemVer | null; + /** + * Return true if version is greater than all the versions possible in the range. + */ + export function gtr(version: string | SemVer, range: string | Range, optionsOrLoose?: boolean | Options): boolean; + /** + * Return true if version is less than all the versions possible in the range. + */ + export function ltr(version: string | SemVer, range: string | Range, optionsOrLoose?: boolean | Options): boolean; + /** + * Return true if the version is outside the bounds of the range in either the high or low direction. + * The hilo argument must be either the string '>' or '<'. (This is the function called by gtr and ltr.) + */ + export function outside(version: string | SemVer, range: string | Range, hilo: '>' | '<', optionsOrLoose?: boolean | Options): boolean; + /** + * Return true if any of the ranges comparators intersect + */ + export function intersects(range1: string | Range, range2: string | Range, optionsOrLoose?: boolean | Options): boolean; + + export class SemVer { + constructor(version: string | SemVer, optionsOrLoose?: boolean | Options); + + raw: string; + loose: boolean; + options: Options; + format(): string; + inspect(): string; + + major: number; + minor: number; + patch: number; + version: string; + build: ReadonlyArray; + prerelease: ReadonlyArray; + + /** + * Compares two versions excluding build identifiers (the bit after `+` in the semantic version string). + * + * @return + * - `0` if `this` == `other` + * - `1` if `this` is greater + * - `-1` if `other` is greater. + */ + compare(other: string | SemVer): 1 | 0 | -1; + + /** + * Compares the release portion of two versions. + * + * @return + * - `0` if `this` == `other` + * - `1` if `this` is greater + * - `-1` if `other` is greater. + */ + compareMain(other: string | SemVer): 1 | 0 | -1; + + /** + * Compares the prerelease portion of two versions. + * + * @return + * - `0` if `this` == `other` + * - `1` if `this` is greater + * - `-1` if `other` is greater. + */ + comparePre(other: string | SemVer): 1 | 0 | -1; + + /** + * Compares the build identifier of two versions. + * + * @return + * - `0` if `this` == `other` + * - `1` if `this` is greater + * - `-1` if `other` is greater. + */ + compareBuild(other: string | SemVer): 1 | 0 | -1; + + inc(release: ReleaseType, identifier?: string): SemVer; + } + + export class Comparator { + constructor(comp: string | Comparator, optionsOrLoose?: boolean | Options); + + semver: SemVer; + operator: '' | '=' | '<' | '>' | '<=' | '>='; + value: string; + loose: boolean; + options: Options; + parse(comp: string): void; + test(version: string | SemVer): boolean; + intersects(comp: Comparator, optionsOrLoose?: boolean | Options): boolean; + } + + export class Range { + constructor(range: string | Range, optionsOrLoose?: boolean | Options); + + range: string; + raw: string; + loose: boolean; + options: Options; + includePrerelease: boolean; + format(): string; + inspect(): string; + + set: ReadonlyArray>; + parseRange(range: string): ReadonlyArray; + test(version: string | SemVer): boolean; + intersects(range: Range, optionsOrLoose?: boolean | Options): boolean; + } + +} diff --git a/src/vs/base/common/semver/semver.js b/src/vs/base/common/semver/semver.js new file mode 100644 index 00000000000..3eacfb1975f --- /dev/null +++ b/src/vs/base/common/semver/semver.js @@ -0,0 +1,11 @@ +/** + * Semver UMD module + * Copyright (c) Isaac Z. Schlueter and Contributors + * https://github.com/npm/node-semver + */ + +/** + * DO NOT EDIT THIS FILE + */ + +!function(e,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r();else if("function"==typeof define&&define.amd)define([],r);else{var t=r();for(var n in t)("object"==typeof exports?exports:e)[n]=t[n]}}("undefined"!=typeof self?self:this,(function(){return function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){(function(t){var n;r=e.exports=H,n="object"==typeof t&&t.env&&t.env.NODE_DEBUG&&/\bsemver\b/i.test(t.env.NODE_DEBUG)?function(){var e=Array.prototype.slice.call(arguments,0);e.unshift("SEMVER"),console.log.apply(console,e)}:function(){},r.SEMVER_SPEC_VERSION="2.0.0";var o=256,i=Number.MAX_SAFE_INTEGER||9007199254740991,s=r.re=[],a=r.src=[],u=0,c=u++;a[c]="0|[1-9]\\d*";var p=u++;a[p]="[0-9]+";var f=u++;a[f]="\\d*[a-zA-Z-][a-zA-Z0-9-]*";var l=u++;a[l]="("+a[c]+")\\.("+a[c]+")\\.("+a[c]+")";var h=u++;a[h]="("+a[p]+")\\.("+a[p]+")\\.("+a[p]+")";var v=u++;a[v]="(?:"+a[c]+"|"+a[f]+")";var m=u++;a[m]="(?:"+a[p]+"|"+a[f]+")";var w=u++;a[w]="(?:-("+a[v]+"(?:\\."+a[v]+")*))";var g=u++;a[g]="(?:-?("+a[m]+"(?:\\."+a[m]+")*))";var y=u++;a[y]="[0-9A-Za-z-]+";var d=u++;a[d]="(?:\\+("+a[y]+"(?:\\."+a[y]+")*))";var b=u++,j="v?"+a[l]+a[w]+"?"+a[d]+"?";a[b]="^"+j+"$";var E="[v=\\s]*"+a[h]+a[g]+"?"+a[d]+"?",T=u++;a[T]="^"+E+"$";var x=u++;a[x]="((?:<|>)?=?)";var $=u++;a[$]=a[p]+"|x|X|\\*";var k=u++;a[k]=a[c]+"|x|X|\\*";var S=u++;a[S]="[v=\\s]*("+a[k]+")(?:\\.("+a[k]+")(?:\\.("+a[k]+")(?:"+a[w]+")?"+a[d]+"?)?)?";var R=u++;a[R]="[v=\\s]*("+a[$]+")(?:\\.("+a[$]+")(?:\\.("+a[$]+")(?:"+a[g]+")?"+a[d]+"?)?)?";var I=u++;a[I]="^"+a[x]+"\\s*"+a[S]+"$";var _=u++;a[_]="^"+a[x]+"\\s*"+a[R]+"$";var O=u++;a[O]="(?:^|[^\\d])(\\d{1,16})(?:\\.(\\d{1,16}))?(?:\\.(\\d{1,16}))?(?:$|[^\\d])";var A=u++;a[A]="(?:~>?)";var M=u++;a[M]="(\\s*)"+a[A]+"\\s+",s[M]=new RegExp(a[M],"g");var V=u++;a[V]="^"+a[A]+a[S]+"$";var P=u++;a[P]="^"+a[A]+a[R]+"$";var C=u++;a[C]="(?:\\^)";var L=u++;a[L]="(\\s*)"+a[C]+"\\s+",s[L]=new RegExp(a[L],"g");var N=u++;a[N]="^"+a[C]+a[S]+"$";var q=u++;a[q]="^"+a[C]+a[R]+"$";var D=u++;a[D]="^"+a[x]+"\\s*("+E+")$|^$";var X=u++;a[X]="^"+a[x]+"\\s*("+j+")$|^$";var z=u++;a[z]="(\\s*)"+a[x]+"\\s*("+E+"|"+a[S]+")",s[z]=new RegExp(a[z],"g");var G=u++;a[G]="^\\s*("+a[S]+")\\s+-\\s+("+a[S]+")\\s*$";var Z=u++;a[Z]="^\\s*("+a[R]+")\\s+-\\s+("+a[R]+")\\s*$";var B=u++;a[B]="(<|>)?=?\\s*\\*";for(var U=0;U<35;U++)n(U,a[U]),s[U]||(s[U]=new RegExp(a[U]));function F(e,r){if(e instanceof H)return e;if("string"!=typeof e)return null;if(e.length>o)return null;if(!(r?s[T]:s[b]).test(e))return null;try{return new H(e,r)}catch(e){return null}}function H(e,r){if(e instanceof H){if(e.loose===r)return e;e=e.version}else if("string"!=typeof e)throw new TypeError("Invalid Version: "+e);if(e.length>o)throw new TypeError("version is longer than "+o+" characters");if(!(this instanceof H))return new H(e,r);n("SemVer",e,r),this.loose=r;var t=e.trim().match(r?s[T]:s[b]);if(!t)throw new TypeError("Invalid Version: "+e);if(this.raw=e,this.major=+t[1],this.minor=+t[2],this.patch=+t[3],this.major>i||this.major<0)throw new TypeError("Invalid major version");if(this.minor>i||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>i||this.patch<0)throw new TypeError("Invalid patch version");t[4]?this.prerelease=t[4].split(".").map((function(e){if(/^[0-9]+$/.test(e)){var r=+e;if(r>=0&&r=0;)"number"==typeof this.prerelease[t]&&(this.prerelease[t]++,t=-2);-1===t&&this.prerelease.push(0)}r&&(this.prerelease[0]===r?isNaN(this.prerelease[1])&&(this.prerelease=[r,0]):this.prerelease=[r,0]);break;default:throw new Error("invalid increment argument: "+e)}return this.format(),this.raw=this.version,this},r.inc=function(e,r,t,n){"string"==typeof t&&(n=t,t=void 0);try{return new H(e,t).inc(r,n).version}catch(e){return null}},r.diff=function(e,r){if(ee(e,r))return null;var t=F(e),n=F(r);if(t.prerelease.length||n.prerelease.length){for(var o in t)if(("major"===o||"minor"===o||"patch"===o)&&t[o]!==n[o])return"pre"+o;return"prerelease"}for(var o in t)if(("major"===o||"minor"===o||"patch"===o)&&t[o]!==n[o])return o},r.compareIdentifiers=K;var J=/^[0-9]+$/;function K(e,r){var t=J.test(e),n=J.test(r);return t&&n&&(e=+e,r=+r),t&&!n?-1:n&&!t?1:er?1:0}function Q(e,r,t){return new H(e,t).compare(new H(r,t))}function W(e,r,t){return Q(e,r,t)>0}function Y(e,r,t){return Q(e,r,t)<0}function ee(e,r,t){return 0===Q(e,r,t)}function re(e,r,t){return 0!==Q(e,r,t)}function te(e,r,t){return Q(e,r,t)>=0}function ne(e,r,t){return Q(e,r,t)<=0}function oe(e,r,t,n){var o;switch(r){case"===":"object"==typeof e&&(e=e.version),"object"==typeof t&&(t=t.version),o=e===t;break;case"!==":"object"==typeof e&&(e=e.version),"object"==typeof t&&(t=t.version),o=e!==t;break;case"":case"=":case"==":o=ee(e,t,n);break;case"!=":o=re(e,t,n);break;case">":o=W(e,t,n);break;case">=":o=te(e,t,n);break;case"<":o=Y(e,t,n);break;case"<=":o=ne(e,t,n);break;default:throw new TypeError("Invalid operator: "+r)}return o}function ie(e,r){if(e instanceof ie){if(e.loose===r)return e;e=e.value}if(!(this instanceof ie))return new ie(e,r);n("comparator",e,r),this.loose=r,this.parse(e),this.semver===se?this.value="":this.value=this.operator+this.semver.version,n("comp",this)}r.rcompareIdentifiers=function(e,r){return K(r,e)},r.major=function(e,r){return new H(e,r).major},r.minor=function(e,r){return new H(e,r).minor},r.patch=function(e,r){return new H(e,r).patch},r.compare=Q,r.compareLoose=function(e,r){return Q(e,r,!0)},r.rcompare=function(e,r,t){return Q(r,e,t)},r.sort=function(e,t){return e.sort((function(e,n){return r.compare(e,n,t)}))},r.rsort=function(e,t){return e.sort((function(e,n){return r.rcompare(e,n,t)}))},r.gt=W,r.lt=Y,r.eq=ee,r.neq=re,r.gte=te,r.lte=ne,r.cmp=oe,r.Comparator=ie;var se={};function ae(e,r){if(e instanceof ae)return e.loose===r?e:new ae(e.raw,r);if(e instanceof ie)return new ae(e.value,r);if(!(this instanceof ae))return new ae(e,r);if(this.loose=r,this.raw=e,this.set=e.split(/\s*\|\|\s*/).map((function(e){return this.parseRange(e.trim())}),this).filter((function(e){return e.length})),!this.set.length)throw new TypeError("Invalid SemVer Range: "+e);this.format()}function ue(e){return!e||"x"===e.toLowerCase()||"*"===e}function ce(e,r,t,n,o,i,s,a,u,c,p,f,l){return((r=ue(t)?"":ue(n)?">="+t+".0.0":ue(o)?">="+t+"."+n+".0":">="+r)+" "+(a=ue(u)?"":ue(c)?"<"+(+u+1)+".0.0":ue(p)?"<"+u+"."+(+c+1)+".0":f?"<="+u+"."+c+"."+p+"-"+f:"<="+a)).trim()}function pe(e,r){for(var t=0;t0){var o=e[t].semver;if(o.major===r.major&&o.minor===r.minor&&o.patch===r.patch)return!0}return!1}return!0}function fe(e,r,t){try{r=new ae(r,t)}catch(e){return!1}return r.test(e)}function le(e,r,t,n){var o,i,s,a,u;switch(e=new H(e,n),r=new ae(r,n),t){case">":o=W,i=ne,s=Y,a=">",u=">=";break;case"<":o=Y,i=te,s=W,a="<",u="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(fe(e,r,n))return!1;for(var c=0;c=0.0.0")),f=f||e,l=l||e,o(e.semver,f.semver,n)?f=e:s(e.semver,l.semver,n)&&(l=e)})),f.operator===a||f.operator===u)return!1;if((!l.operator||l.operator===a)&&i(e,l.semver))return!1;if(l.operator===u&&s(e,l.semver))return!1}return!0}ie.prototype.parse=function(e){var r=this.loose?s[D]:s[X],t=e.match(r);if(!t)throw new TypeError("Invalid comparator: "+e);this.operator=t[1],"="===this.operator&&(this.operator=""),t[2]?this.semver=new H(t[2],this.loose):this.semver=se},ie.prototype.toString=function(){return this.value},ie.prototype.test=function(e){return n("Comparator.test",e,this.loose),this.semver===se||("string"==typeof e&&(e=new H(e,this.loose)),oe(e,this.operator,this.semver,this.loose))},ie.prototype.intersects=function(e,r){if(!(e instanceof ie))throw new TypeError("a Comparator is required");var t;if(""===this.operator)return t=new ae(e.value,r),fe(this.value,t,r);if(""===e.operator)return t=new ae(this.value,r),fe(e.semver,t,r);var n=!(">="!==this.operator&&">"!==this.operator||">="!==e.operator&&">"!==e.operator),o=!("<="!==this.operator&&"<"!==this.operator||"<="!==e.operator&&"<"!==e.operator),i=this.semver.version===e.semver.version,s=!(">="!==this.operator&&"<="!==this.operator||">="!==e.operator&&"<="!==e.operator),a=oe(this.semver,"<",e.semver,r)&&(">="===this.operator||">"===this.operator)&&("<="===e.operator||"<"===e.operator),u=oe(this.semver,">",e.semver,r)&&("<="===this.operator||"<"===this.operator)&&(">="===e.operator||">"===e.operator);return n||o||i&&s||a||u},r.Range=ae,ae.prototype.format=function(){return this.range=this.set.map((function(e){return e.join(" ").trim()})).join("||").trim(),this.range},ae.prototype.toString=function(){return this.range},ae.prototype.parseRange=function(e){var r=this.loose;e=e.trim(),n("range",e,r);var t=r?s[Z]:s[G];e=e.replace(t,ce),n("hyphen replace",e),e=e.replace(s[z],"$1$2$3"),n("comparator trim",e,s[z]),e=(e=(e=e.replace(s[M],"$1~")).replace(s[L],"$1^")).split(/\s+/).join(" ");var o=r?s[D]:s[X],i=e.split(" ").map((function(e){return function(e,r){return n("comp",e),e=function(e,r){return e.trim().split(/\s+/).map((function(e){return function(e,r){n("caret",e,r);var t=r?s[q]:s[N];return e.replace(t,(function(r,t,o,i,s){var a;return n("caret",e,r,t,o,i,s),ue(t)?a="":ue(o)?a=">="+t+".0.0 <"+(+t+1)+".0.0":ue(i)?a="0"===t?">="+t+"."+o+".0 <"+t+"."+(+o+1)+".0":">="+t+"."+o+".0 <"+(+t+1)+".0.0":s?(n("replaceCaret pr",s),"-"!==s.charAt(0)&&(s="-"+s),a="0"===t?"0"===o?">="+t+"."+o+"."+i+s+" <"+t+"."+o+"."+(+i+1):">="+t+"."+o+"."+i+s+" <"+t+"."+(+o+1)+".0":">="+t+"."+o+"."+i+s+" <"+(+t+1)+".0.0"):(n("no pr"),a="0"===t?"0"===o?">="+t+"."+o+"."+i+" <"+t+"."+o+"."+(+i+1):">="+t+"."+o+"."+i+" <"+t+"."+(+o+1)+".0":">="+t+"."+o+"."+i+" <"+(+t+1)+".0.0"),n("caret return",a),a}))}(e,r)})).join(" ")}(e,r),n("caret",e),e=function(e,r){return e.trim().split(/\s+/).map((function(e){return function(e,r){var t=r?s[P]:s[V];return e.replace(t,(function(r,t,o,i,s){var a;return n("tilde",e,r,t,o,i,s),ue(t)?a="":ue(o)?a=">="+t+".0.0 <"+(+t+1)+".0.0":ue(i)?a=">="+t+"."+o+".0 <"+t+"."+(+o+1)+".0":s?(n("replaceTilde pr",s),"-"!==s.charAt(0)&&(s="-"+s),a=">="+t+"."+o+"."+i+s+" <"+t+"."+(+o+1)+".0"):a=">="+t+"."+o+"."+i+" <"+t+"."+(+o+1)+".0",n("tilde return",a),a}))}(e,r)})).join(" ")}(e,r),n("tildes",e),e=function(e,r){return n("replaceXRanges",e,r),e.split(/\s+/).map((function(e){return function(e,r){e=e.trim();var t=r?s[_]:s[I];return e.replace(t,(function(r,t,o,i,s,a){n("xRange",e,r,t,o,i,s,a);var u=ue(o),c=u||ue(i),p=c||ue(s);return"="===t&&p&&(t=""),u?r=">"===t||"<"===t?"<0.0.0":"*":t&&p?(c&&(i=0),p&&(s=0),">"===t?(t=">=",c?(o=+o+1,i=0,s=0):p&&(i=+i+1,s=0)):"<="===t&&(t="<",c?o=+o+1:i=+i+1),r=t+o+"."+i+"."+s):c?r=">="+o+".0.0 <"+(+o+1)+".0.0":p&&(r=">="+o+"."+i+".0 <"+o+"."+(+i+1)+".0"),n("xRange return",r),r}))}(e,r)})).join(" ")}(e,r),n("xrange",e),e=function(e,r){return n("replaceStars",e,r),e.trim().replace(s[B],"")}(e,r),n("stars",e),e}(e,r)})).join(" ").split(/\s+/);return this.loose&&(i=i.filter((function(e){return!!e.match(o)}))),i=i.map((function(e){return new ie(e,r)}))},ae.prototype.intersects=function(e,r){if(!(e instanceof ae))throw new TypeError("a Range is required");return this.set.some((function(t){return t.every((function(t){return e.set.some((function(e){return e.every((function(e){return t.intersects(e,r)}))}))}))}))},r.toComparators=function(e,r){return new ae(e,r).set.map((function(e){return e.map((function(e){return e.value})).join(" ").trim().split(" ")}))},ae.prototype.test=function(e){if(!e)return!1;"string"==typeof e&&(e=new H(e,this.loose));for(var r=0;r",t)},r.outside=le,r.prerelease=function(e,r){var t=F(e,r);return t&&t.prerelease.length?t.prerelease:null},r.intersects=function(e,r,t){return e=new ae(e,t),r=new ae(r,t),e.intersects(r)},r.coerce=function(e){if(e instanceof H)return e;if("string"!=typeof e)return null;var r=e.match(s[O]);return null==r?null:F((r[1]||"0")+"."+(r[2]||"0")+"."+(r[3]||"0"))}}).call(this,t(1))},function(e,r){var t,n,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function a(e){if(t===setTimeout)return setTimeout(e,0);if((t===i||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(r){try{return t.call(null,e,0)}catch(r){return t.call(this,e,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:i}catch(e){t=i}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(e){n=s}}();var u,c=[],p=!1,f=-1;function l(){p&&u&&(p=!1,u.length?c=u.concat(c):f=-1,c.length&&h())}function h(){if(!p){var e=a(l);p=!0;for(var r=c.length;r;){for(u=c,c=[];++f1)for(var t=1;t { readonly start: number; @@ -32,3 +33,26 @@ export class Sequence implements ISequence, ISpliceable { this._onDidSplice.fire({ start, deleteCount, toInsert }); } } + +export class SimpleSequence implements ISequence { + + private _elements: T[]; + get elements(): T[] { return this._elements; } + + readonly onDidSplice: Event>; + private disposable: IDisposable; + + constructor(elements: T[], onDidAdd: Event, onDidRemove: Event) { + this._elements = [...elements]; + this.onDidSplice = Event.any( + Event.map(onDidAdd, e => ({ start: this.elements.length, deleteCount: 0, toInsert: [e] })), + Event.map(Event.filter(Event.map(onDidRemove, e => this.elements.indexOf(e)), i => i > -1), i => ({ start: i, deleteCount: 1, toInsert: [] })) + ); + + this.disposable = this.onDidSplice(({ start, deleteCount, toInsert }) => this._elements.splice(start, deleteCount, ...toInsert)); + } + + dispose(): void { + this.disposable.dispose(); + } +} diff --git a/src/vs/base/common/severity.ts b/src/vs/base/common/severity.ts index 602df43e598..0937e4ddd19 100644 --- a/src/vs/base/common/severity.ts +++ b/src/vs/base/common/severity.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import * as strings from 'vs/base/common/strings'; enum Severity { @@ -20,11 +19,6 @@ namespace Severity { const _warn = 'warn'; const _info = 'info'; - const _displayStrings: { [value: number]: string; } = Object.create(null); - _displayStrings[Severity.Error] = nls.localize('sev.error', "Error"); - _displayStrings[Severity.Warning] = nls.localize('sev.warning', "Warning"); - _displayStrings[Severity.Info] = nls.localize('sev.info', "Info"); - /** * Parses 'error', 'warning', 'warn', 'info' in call casings * and falls back to ignore. diff --git a/src/vs/base/common/skipList.ts b/src/vs/base/common/skipList.ts new file mode 100644 index 00000000000..6b3bd79ef4c --- /dev/null +++ b/src/vs/base/common/skipList.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +class Node { + readonly forward: Node[]; + constructor(readonly level: number, readonly key: K, public value: V) { + this.forward = []; + } +} + +const NIL: undefined = undefined; + +interface Comparator { + (a: K, b: K): number; +} + +export class SkipList implements Map { + + readonly [Symbol.toStringTag] = 'SkipList'; + + private _maxLevel: number; + private _level: number = 0; + private _header: Node; + private _size: number = 0; + + /** + * + * @param capacity Capacity at which the list performs best + */ + constructor( + readonly comparator: (a: K, b: K) => number, + capacity: number = 2 ** 16 + ) { + this._maxLevel = Math.max(1, Math.log2(capacity) | 0); + this._header = new Node(this._maxLevel, NIL, NIL); + } + + get size(): number { + return this._size; + } + + clear(): void { + this._header = new Node(this._maxLevel, NIL, NIL); + } + + has(key: K): boolean { + return Boolean(SkipList._search(this, key, this.comparator)); + } + + get(key: K): V | undefined { + return SkipList._search(this, key, this.comparator)?.value; + } + + set(key: K, value: V): this { + if (SkipList._insert(this, key, value, this.comparator)) { + this._size += 1; + } + return this; + } + + delete(key: K): boolean { + const didDelete = SkipList._delete(this, key, this.comparator); + if (didDelete) { + this._size -= 1; + } + return didDelete; + } + + // --- iteration + + forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void { + let node = this._header.forward[0]; + while (node) { + callbackfn.call(thisArg, node.value, node.key, this); + node = node.forward[0]; + } + } + + [Symbol.iterator](): IterableIterator<[K, V]> { + return this.entries(); + } + + *entries(): IterableIterator<[K, V]> { + let node = this._header.forward[0]; + while (node) { + yield [node.key, node.value]; + node = node.forward[0]; + } + } + + *keys(): IterableIterator { + let node = this._header.forward[0]; + while (node) { + yield node.key; + node = node.forward[0]; + } + } + + *values(): IterableIterator { + let node = this._header.forward[0]; + while (node) { + yield node.value; + node = node.forward[0]; + } + } + + toString(): string { + // debug string... + let result = '[SkipList]:'; + let node = this._header.forward[0]; + while (node) { + result += `node(${node.key}, ${node.value}, lvl:${node.level})`; + node = node.forward[0]; + } + return result; + } + + // from https://www.epaperpress.com/sortsearch/download/skiplist.pdf + + private static _search(list: SkipList, searchKey: K, comparator: Comparator) { + let x = list._header; + for (let i = list._level - 1; i >= 0; i--) { + while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { + x = x.forward[i]; + } + } + x = x.forward[0]; + if (x && comparator(x.key, searchKey) === 0) { + return x; + } + return undefined; + } + + private static _insert(list: SkipList, searchKey: K, value: V, comparator: Comparator) { + let update: Node[] = []; + let x = list._header; + for (let i = list._level - 1; i >= 0; i--) { + while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { + x = x.forward[i]; + } + update[i] = x; + } + x = x.forward[0]; + if (x && comparator(x.key, searchKey) === 0) { + // update + x.value = value; + return false; + } else { + // insert + let lvl = SkipList._randomLevel(list); + if (lvl > list._level) { + for (let i = list._level; i < lvl; i++) { + update[i] = list._header; + } + list._level = lvl; + } + x = new Node(lvl, searchKey, value); + for (let i = 0; i < lvl; i++) { + x.forward[i] = update[i].forward[i]; + update[i].forward[i] = x; + } + return true; + } + } + + private static _randomLevel(list: SkipList, p: number = 0.5): number { + let lvl = 1; + while (Math.random() < p && lvl < list._maxLevel) { + lvl += 1; + } + return lvl; + } + + private static _delete(list: SkipList, searchKey: K, comparator: Comparator) { + let update: Node[] = []; + let x = list._header; + for (let i = list._level - 1; i >= 0; i--) { + while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { + x = x.forward[i]; + } + update[i] = x; + } + x = x.forward[0]; + if (!x || comparator(x.key, searchKey) !== 0) { + // not found + return false; + } + for (let i = 0; i < list._level; i++) { + if (update[i].forward[i] !== x) { + break; + } + update[i].forward[i] = x.forward[i]; + } + while (list._level > 0 && list._header.forward[list._level - 1] === NIL) { + list._level -= 1; + } + return true; + } + +} diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 1172496a897..260e13f02b3 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; + /** * The payload that flows in readable stream events. */ @@ -31,7 +33,7 @@ export interface ReadableStreamEvents { /** * A interface that emulates the API shape of a node.js readable - * stream for use in desktop and web environments. + * stream for use in native and web environments. */ export interface ReadableStream extends ReadableStreamEvents { @@ -49,11 +51,16 @@ export interface ReadableStream extends ReadableStreamEvents { * Destroys the stream and stops emitting any event. */ destroy(): void; + + /** + * Allows to remove a listener that was previously added. + */ + removeListener(event: string, callback: Function): void; } /** * A interface that emulates the API shape of a node.js readable - * for use in desktop and web environments. + * for use in native and web environments. */ export interface Readable { @@ -66,7 +73,7 @@ export interface Readable { /** * A interface that emulates the API shape of a node.js writeable - * stream for use in desktop and web environments. + * stream for use in native and web environments. */ export interface WriteableStream extends ReadableStream { @@ -74,8 +81,14 @@ export interface WriteableStream extends ReadableStream { * Writing data to the stream will trigger the on('data') * event listener if the stream is flowing and buffer the * data otherwise until the stream is flowing. + * + * If a `highWaterMark` is configured and writing to the + * stream reaches this mark, a promise will be returned + * that should be awaited on before writing more data. + * Otherwise there is a risk of buffering a large number + * of data chunks without consumer. */ - write(data: T): void; + write(data: T): void | Promise; /** * Signals an error to the consumer of the stream via the @@ -95,12 +108,43 @@ export interface WriteableStream extends ReadableStream { end(result?: T | Error): void; } -export function isReadableStream(obj: any): obj is ReadableStream { - const candidate: ReadableStream = obj; +/** + * A stream that has a buffer already read. Returns the original stream + * that was read as well as the chunks that got read. + * + * The `ended` flag indicates if the stream has been fully consumed. + */ +export interface ReadableBufferedStream { + + /** + * The original stream that is being read. + */ + stream: ReadableStream; + + /** + * An array of chunks already read from this stream. + */ + buffer: T[]; + + /** + * Signals if the stream has ended or not. If not, consumers + * should continue to read from the stream until consumed. + */ + ended: boolean; +} + +export function isReadableStream(obj: unknown): obj is ReadableStream { + const candidate = obj as ReadableStream; return candidate && [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function'); } +export function isReadableBufferedStream(obj: unknown): obj is ReadableBufferedStream { + const candidate = obj as ReadableBufferedStream; + + return candidate && isReadableStream(candidate.stream) && Array.isArray(candidate.buffer) && typeof candidate.ended === 'boolean'; +} + export interface IReducer { (data: T[]): T; } @@ -118,8 +162,18 @@ export interface ITransformer { error?: IErrorTransformer; } -export function newWriteableStream(reducer: IReducer): WriteableStream { - return new WriteableStreamImpl(reducer); +export function newWriteableStream(reducer: IReducer, options?: WriteableStreamOptions): WriteableStream { + return new WriteableStreamImpl(reducer, options); +} + +export interface WriteableStreamOptions { + + /** + * The number of objects to buffer before WriteableStream#write() + * signals back that the buffer is full. Can be used to reduce + * the memory pressure when the stream is not flowing. + */ + highWaterMark?: number; } class WriteableStreamImpl implements WriteableStream { @@ -141,7 +195,9 @@ class WriteableStreamImpl implements WriteableStream { end: [] as { (): void }[] }; - constructor(private reducer: IReducer) { } + private readonly pendingWritePromises: Function[] = []; + + constructor(private reducer: IReducer, private options?: WriteableStreamOptions) { } pause(): void { if (this.state.destroyed) { @@ -166,7 +222,7 @@ class WriteableStreamImpl implements WriteableStream { } } - write(data: T): void { + write(data: T): void | Promise { if (this.state.destroyed) { return; } @@ -179,6 +235,11 @@ class WriteableStreamImpl implements WriteableStream { // not yet flowing: buffer data until flowing else { this.buffer.data.push(data); + + // highWaterMark: if configured, signal back when buffer reached limits + if (typeof this.options?.highWaterMark === 'number' && this.buffer.data.length > this.options.highWaterMark) { + return new Promise(resolve => this.pendingWritePromises.push(resolve)); + } } } @@ -267,6 +328,35 @@ class WriteableStreamImpl implements WriteableStream { } } + removeListener(event: string, callback: Function): void { + if (this.state.destroyed) { + return; + } + + let listeners: unknown[] | undefined = undefined; + + switch (event) { + case 'data': + listeners = this.listeners.data; + break; + + case 'end': + listeners = this.listeners.end; + break; + + case 'error': + listeners = this.listeners.error; + break; + } + + if (listeners) { + const index = listeners.indexOf(callback); + if (index >= 0) { + listeners.splice(index, 1); + } + } + } + private flowData(): void { if (this.buffer.data.length > 0) { const fullDataBuffer = this.reducer(this.buffer.data); @@ -274,6 +364,11 @@ class WriteableStreamImpl implements WriteableStream { this.listeners.data.forEach(listener => listener(fullDataBuffer)); this.buffer.data.length = 0; + + // When the buffer is empty, resolve all pending writers + const pendingWritePromises = [...this.pendingWritePromises]; + this.pendingWritePromises.length = 0; + pendingWritePromises.forEach(pendingWritePromise => pendingWritePromise()); } } @@ -308,6 +403,8 @@ class WriteableStreamImpl implements WriteableStream { this.listeners.data.length = 0; this.listeners.error.length = 0; this.listeners.end.length = 0; + + this.pendingWritePromises.length = 0; } } } @@ -331,7 +428,7 @@ export function consumeReadable(readable: Readable, reducer: IReducer): * reached, will return a readable instead to ensure all data can still * be read. */ -export function consumeReadableWithLimit(readable: Readable, reducer: IReducer, maxChunks: number): T | Readable { +export function peekReadable(readable: Readable, reducer: IReducer, maxChunks: number): T | Readable { const chunks: T[] = []; let chunk: T | null | undefined = undefined; @@ -377,7 +474,7 @@ export function consumeReadableWithLimit(readable: Readable, reducer: IRed /** * Helper to fully read a T stream into a T. */ -export function consumeStream(stream: ReadableStream, reducer: IReducer): Promise { +export function consumeStream(stream: ReadableStreamEvents, reducer: IReducer): Promise { return new Promise((resolve, reject) => { const chunks: T[] = []; @@ -388,58 +485,50 @@ export function consumeStream(stream: ReadableStream, reducer: IReducer } /** - * Helper to read a T stream up to a maximum of chunks. If the limit is - * reached, will return a stream instead to ensure all data can still - * be read. + * Helper to peek up to `maxChunks` into a stream. The return type signals if + * the stream has ended or not. If not, caller needs to add a `data` listener + * to continue reading. */ -export function consumeStreamWithLimit(stream: ReadableStream, reducer: IReducer, maxChunks: number): Promise> { +export function peekStream(stream: ReadableStream, maxChunks: number): Promise> { return new Promise((resolve, reject) => { - const chunks: T[] = []; + const streamListeners = new DisposableStore(); - let wrapperStream: WriteableStream | undefined = undefined; + // Data Listener + const buffer: T[] = []; + const dataListener = (chunk: T) => { - stream.on('data', data => { + // Add to buffer + buffer.push(chunk); - // If we reach maxChunks, we start to return a stream - // and make sure that any data we have already read - // is in it as well - if (!wrapperStream && chunks.length === maxChunks) { - wrapperStream = newWriteableStream(reducer); - while (chunks.length) { - wrapperStream.write(chunks.shift()!); - } + // We reached maxChunks and thus need to return + if (buffer.length > maxChunks) { - wrapperStream.write(data); + // Dispose any listeners and ensure to pause the + // stream so that it can be consumed again by caller + streamListeners.dispose(); + stream.pause(); - return resolve(wrapperStream); + return resolve({ stream, buffer, ended: false }); } + }; - if (wrapperStream) { - wrapperStream.write(data); - } else { - chunks.push(data); - } - }); + streamListeners.add(toDisposable(() => stream.removeListener('data', dataListener))); + stream.on('data', dataListener); - stream.on('error', error => { - if (wrapperStream) { - wrapperStream.error(error); - } else { - return reject(error); - } - }); + // Error Listener + const errorListener = (error: Error) => { + return reject(error); + }; - stream.on('end', () => { - if (wrapperStream) { - while (chunks.length) { - wrapperStream.write(chunks.shift()!); - } + streamListeners.add(toDisposable(() => stream.removeListener('error', errorListener))); + stream.on('error', errorListener); - wrapperStream.end(); - } else { - return resolve(reducer(chunks)); - } - }); + const endListener = () => { + return resolve({ stream, buffer, ended: true }); + }; + + streamListeners.add(toDisposable(() => stream.removeListener('end', endListener))); + stream.on('end', endListener); }); } diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 8d67e493772..9e35fd6cb9c 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -13,20 +13,6 @@ export function isFalsyOrWhitespace(str: string | undefined): boolean { return str.trim().length === 0; } -/** - * @returns the provided number with the given number of preceding zeros. - */ -export function pad(n: number, l: number, char: string = '0'): string { - const str = '' + n; - const r = [str]; - - for (let i = str.length; i < l; i++) { - r.push(char); - } - - return r.reverse().join(''); -} - const _formatRegexp = /{(\d+)}/g; /** @@ -69,6 +55,20 @@ export function escapeRegExpCharacters(value: string): string { return value.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g, '\\$&'); } +/** + * Counts how often `character` occurs inside `value`. + */ +export function count(value: string, character: string): number { + let result = 0; + const ch = character.charCodeAt(0); + for (let i = value.length - 1; i >= 0; i--) { + if (value.charCodeAt(i) === ch) { + result++; + } + } + return result; +} + /** * Removes all occurrences of needle from the beginning and end of haystack. * @param haystack string to trim @@ -144,41 +144,6 @@ export function stripWildcards(pattern: string): string { return pattern.replace(/\*/g, ''); } -/** - * Determines if haystack starts with needle. - */ -export function startsWith(haystack: string, needle: string): boolean { - if (haystack.length < needle.length) { - return false; - } - - if (haystack === needle) { - return true; - } - - for (let i = 0; i < needle.length; i++) { - if (haystack[i] !== needle[i]) { - return false; - } - } - - return true; -} - -/** - * Determines if haystack ends with needle. - */ -export function endsWith(haystack: string, needle: string): boolean { - const diff = haystack.length - needle.length; - if (diff > 0) { - return haystack.indexOf(needle, diff) === diff; - } else if (diff === 0) { - return haystack === needle; - } else { - return false; - } -} - export interface RegExpOptions { matchCase?: boolean; wholeWord?: boolean; @@ -240,7 +205,7 @@ export function regExpFlags(regexp: RegExp): string { return (regexp.global ? 'g' : '') + (regexp.ignoreCase ? 'i' : '') + (regexp.multiline ? 'm' : '') - + ((regexp as any).unicode ? 'u' : ''); + + ((regexp as any /* standalone editor compilation */).unicode ? 'u' : ''); } /** @@ -295,47 +260,69 @@ export function compare(a: string, b: string): number { } } +export function compareSubstring(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { + for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) { + let codeA = a.charCodeAt(aStart); + let codeB = b.charCodeAt(bStart); + if (codeA < codeB) { + return -1; + } else if (codeA > codeB) { + return 1; + } + } + const aLen = aEnd - aStart; + const bLen = bEnd - bStart; + if (aLen < bLen) { + return -1; + } else if (aLen > bLen) { + return 1; + } + return 0; +} + export function compareIgnoreCase(a: string, b: string): number { - const len = Math.min(a.length, b.length); - for (let i = 0; i < len; i++) { - let codeA = a.charCodeAt(i); - let codeB = b.charCodeAt(i); + return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length); +} + +export function compareSubstringIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { + + for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) { + + let codeA = a.charCodeAt(aStart); + let codeB = b.charCodeAt(bStart); if (codeA === codeB) { // equal continue; } - if (isUpperAsciiLetter(codeA)) { - codeA += 32; - } - - if (isUpperAsciiLetter(codeB)) { - codeB += 32; - } - const diff = codeA - codeB; - - if (diff === 0) { - // equal -> ignoreCase + if (diff === 32 && isUpperAsciiLetter(codeB)) { //codeB =[65-90] && codeA =[97-122] continue; - } else if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) { + } else if (diff === -32 && isUpperAsciiLetter(codeA)) { //codeB =[97-122] && codeA =[65-90] + continue; + } + + if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) { // return diff; } else { - return compare(a.toLowerCase(), b.toLowerCase()); + return compareSubstring(a.toLowerCase(), b.toLowerCase(), aStart, aEnd, bStart, bEnd); } } - if (a.length < b.length) { + const aLen = aEnd - aStart; + const bLen = bEnd - bStart; + + if (aLen < bLen) { return -1; - } else if (a.length > b.length) { + } else if (aLen > bLen) { return 1; - } else { - return 0; } + + return 0; } export function isLowerAsciiLetter(code: number): boolean { @@ -428,66 +415,27 @@ export function commonSuffixLength(a: string, b: string): number { return len; } -function substrEquals(a: string, aStart: number, aEnd: number, b: string, bStart: number, bEnd: number): boolean { - while (aStart < aEnd && bStart < bEnd) { - if (a[aStart] !== b[bStart]) { - return false; - } - aStart += 1; - bStart += 1; - } - return true; -} - /** - * Return the overlap between the suffix of `a` and the prefix of `b`. - * For instance `overlap("foobar", "arr, I'm a pirate") === 2`. + * See http://en.wikipedia.org/wiki/Surrogate_pair */ -export function overlap(a: string, b: string): number { - const aEnd = a.length; - let bEnd = b.length; - let aStart = aEnd - bEnd; - - if (aStart === 0) { - return a === b ? aEnd : 0; - } else if (aStart < 0) { - bEnd += aStart; - aStart = 0; - } - - while (aStart < aEnd && bEnd > 0) { - if (substrEquals(a, aStart, aEnd, b, 0, bEnd)) { - return bEnd; - } - bEnd -= 1; - aStart += 1; - } - return 0; -} - -// --- unicode -// http://en.wikipedia.org/wiki/Surrogate_pair -// Returns the code point starting at a specified index in a string -// Code points U+0000 to U+D7FF and U+E000 to U+FFFF are represented on a single character -// Code points U+10000 to U+10FFFF are represented on two consecutive characters -//export function getUnicodePoint(str:string, index:number, len:number):number { -// const chrCode = str.charCodeAt(index); -// if (0xD800 <= chrCode && chrCode <= 0xDBFF && index + 1 < len) { -// const nextChrCode = str.charCodeAt(index + 1); -// if (0xDC00 <= nextChrCode && nextChrCode <= 0xDFFF) { -// return (chrCode - 0xD800) << 10 + (nextChrCode - 0xDC00) + 0x10000; -// } -// } -// return chrCode; -//} export function isHighSurrogate(charCode: number): boolean { return (0xD800 <= charCode && charCode <= 0xDBFF); } +/** + * See http://en.wikipedia.org/wiki/Surrogate_pair + */ export function isLowSurrogate(charCode: number): boolean { return (0xDC00 <= charCode && charCode <= 0xDFFF); } +/** + * See http://en.wikipedia.org/wiki/Surrogate_pair + */ +export function computeCodePoint(highSurrogate: number, lowSurrogate: number): number { + return ((highSurrogate - 0xD800) << 10) + (lowSurrogate - 0xDC00) + 0x10000; +} + /** * get the code point that begins at offset `offset` */ @@ -496,7 +444,7 @@ export function getNextCodePoint(str: string, len: number, offset: number): numb if (isHighSurrogate(charCode) && offset + 1 < len) { const nextCharCode = str.charCodeAt(offset + 1); if (isLowSurrogate(nextCharCode)) { - return ((charCode - 0xD800) << 10) + (nextCharCode - 0xDC00) + 0x10000; + return computeCodePoint(charCode, nextCharCode); } } return charCode; @@ -510,7 +458,7 @@ function getPrevCodePoint(str: string, offset: number): number { if (isLowSurrogate(charCode) && offset > 1) { const prevCharCode = str.charCodeAt(offset - 2); if (isHighSurrogate(prevCharCode)) { - return ((prevCharCode - 0xD800) << 10) + (charCode - 0xDC00) + 0x10000; + return computeCodePoint(prevCharCode, charCode); } } return charCode; @@ -745,6 +693,14 @@ export function isBasicASCII(str: string): boolean { return IS_BASIC_ASCII.test(str); } +export const UNUSUAL_LINE_TERMINATORS = /[\u2028\u2029]/; // LINE SEPARATOR (LS) or PARAGRAPH SEPARATOR (PS) +/** + * Returns true if `str` contains unusual line terminators, like LS or PS + */ +export function containsUnusualLineTerminators(str: string): boolean { + return UNUSUAL_LINE_TERMINATORS.test(str); +} + export function containsFullWidthCharacter(str: string): boolean { for (let i = 0, len = str.length; i < len; i++) { if (isFullWidthCharacter(str.charCodeAt(i))) { @@ -852,21 +808,6 @@ export function removeAnsiEscapeCodes(str: string): string { return str; } -export const removeAccents: (str: string) => string = (function () { - if (typeof (String.prototype as any).normalize !== 'function') { - // ☹️ no ES6 features... - return function (str: string) { return str; }; - } else { - // transform into NFD form and remove accents - // see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463 - const regex = /[\u0300-\u036f]/g; - return function (str: string) { - return (str as any).normalize('NFD').replace(regex, ''); - }; - } -})(); - - // -- UTF-8 BOM export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM); @@ -879,18 +820,6 @@ export function stripUTF8BOM(str: string): string { return startsWithUTF8BOM(str) ? str.substr(1) : str; } -export function safeBtoa(str: string): string { - return btoa(encodeURIComponent(str)); // we use encodeURIComponent because btoa fails for non Latin 1 values -} - -export function repeat(s: string, count: number): string { - let result = ''; - for (let i = 0; i < count; i++) { - result += s; - } - return result; -} - /** * Checks if the characters of the provided query string are included in the * target string. The characters do not have to be contiguous within the string. diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 460e1a910ae..eef27600bd7 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -3,45 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const _typeof = { - number: 'number', - string: 'string', - undefined: 'undefined', - object: 'object', - function: 'function' -}; +import { URI, UriComponents } from 'vs/base/common/uri'; /** * @returns whether the provided parameter is a JavaScript Array or not. */ -export function isArray(array: any): array is any[] { - if (Array.isArray) { - return Array.isArray(array); - } - - if (array && typeof (array.length) === _typeof.number && array.constructor === Array) { - return true; - } - - return false; +export function isArray(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] { + return Array.isArray(array); } /** * @returns whether the provided parameter is a JavaScript String or not. */ export function isString(str: any): str is string { - if (typeof (str) === _typeof.string || str instanceof String) { - return true; - } - - return false; + return (typeof str === 'string'); } /** * @returns whether the provided parameter is a JavaScript Array and each element in the array is a string. */ export function isStringArray(value: any): value is string[] { - return isArray(value) && (value).every(elem => isString(elem)); + return Array.isArray(value) && (value).every(elem => isString(elem)); } /** @@ -53,7 +35,7 @@ export function isObject(obj: any): obj is Object { // The method can't do a type cast since there are type (like strings) which // are subclasses of any put not positvely matched by the function. Hence type // narrowing results in wrong results. - return typeof obj === _typeof.object + return typeof obj === 'object' && obj !== null && !Array.isArray(obj) && !(obj instanceof RegExp) @@ -65,32 +47,35 @@ export function isObject(obj: any): obj is Object { * @returns whether the provided parameter is a JavaScript Number or not. */ export function isNumber(obj: any): obj is number { - if ((typeof (obj) === _typeof.number || obj instanceof Number) && !isNaN(obj)) { - return true; - } - - return false; + return (typeof obj === 'number' && !isNaN(obj)); } /** * @returns whether the provided parameter is a JavaScript Boolean or not. */ export function isBoolean(obj: any): obj is boolean { - return obj === true || obj === false; + return (obj === true || obj === false); } /** * @returns whether the provided parameter is undefined. */ export function isUndefined(obj: any): obj is undefined { - return typeof (obj) === _typeof.undefined; + return (typeof obj === 'undefined'); +} + +/** + * @returns whether the provided parameter is defined. + */ +export function isDefined(arg: T | null | undefined): arg is T { + return !isUndefinedOrNull(arg); } /** * @returns whether the provided parameter is undefined or null. */ export function isUndefinedOrNull(obj: any): obj is undefined | null { - return isUndefined(obj) || obj === null; + return (isUndefined(obj) || obj === null); } @@ -156,7 +141,7 @@ export function isEmptyObject(obj: any): obj is any { * @returns whether the provided parameter is a JavaScript Function or not. */ export function isFunction(obj: any): obj is Function { - return typeof obj === _typeof.function; + return (typeof obj === 'function'); } /** @@ -186,7 +171,7 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi if (arg instanceof constraint) { return; } - } catch{ + } catch { // ignore } if (!isUndefinedOrNull(arg) && arg.constructor === constraint) { @@ -262,3 +247,35 @@ export type AddFirstParameterToFunctions = { [K in keyof T]: T[K] extends URI + ? UriComponents + : UriDto }; + +/** + * Mapped-type that replaces all occurrences of URI with UriComponents and + * drops all functions. + */ +export type Dto = T extends { toJSON(): infer U } + ? U + : T extends object + ? { [k in keyof T]: Dto; } + : T; + +export function NotImplementedProxy(name: string): { new(): T } { + return class { + constructor() { + return new Proxy({}, { + get(target: any, prop: PropertyKey) { + if (target[prop]) { + return target[prop]; + } + throw new Error(`Not Implemented: ${name}->${String(prop)}`); + } + }); + } + }; +} diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 78c9fda1d71..5ac44dc8426 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -5,6 +5,7 @@ import { isWindows } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; +import * as paths from 'vs/base/common/path'; const _schemePattern = /^\w[\w\d+.-]*$/; const _singleSlashStart = /^\//; @@ -83,6 +84,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation * and encoding. * + * ```txt * foo://example.com:8042/over/there?name=ferret#nose * \_/ \______________/\_________/ \_________/ \__/ * | | | | | @@ -90,6 +92,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; * | _____________________|__ * / \ / \ * urn:example:animal:ferret:nose + * ``` */ export class URI implements UriComponents { @@ -202,7 +205,7 @@ export class URI implements UriComponents { // if (this.scheme !== 'file') { // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`); // } - return _makeFsPath(this); + return uriToFsPath(this, false); } // ---- modify to new ------------------------- @@ -249,7 +252,7 @@ export class URI implements UriComponents { return this; } - return new _URI(scheme, authority, path, query, fragment); + return new Uri(scheme, authority, path, query, fragment); } // ---- parse & validate ------------------------ @@ -263,9 +266,9 @@ export class URI implements UriComponents { static parse(value: string, _strict: boolean = false): URI { const match = _regexp.exec(value); if (!match) { - return new _URI(_empty, _empty, _empty, _empty, _empty); + return new Uri(_empty, _empty, _empty, _empty, _empty); } - return new _URI( + return new Uri( match[2] || _empty, percentDecode(match[4] || _empty), percentDecode(match[5] || _empty), @@ -320,11 +323,11 @@ export class URI implements UriComponents { } } - return new _URI('file', authority, path, _empty, _empty); + return new Uri('file', authority, path, _empty, _empty); } static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { - return new _URI( + return new Uri( components.scheme, components.authority, components.path, @@ -333,6 +336,26 @@ export class URI implements UriComponents { ); } + /** + * Join a URI path with path fragments and normalizes the resulting path. + * + * @param uri The input URI. + * @param pathFragment The path fragment to add to the URI path. + * @returns The resulting URI. + */ + static joinPath(uri: URI, ...pathFragment: string[]): URI { + if (!uri.path) { + throw new Error(`[UriError]: cannot call joinPaths on URI without path`); + } + let newPath: string; + if (isWindows && uri.scheme === 'file') { + newPath = URI.file(paths.win32.join(uriToFsPath(uri, true), ...pathFragment)).path; + } else { + newPath = paths.posix.join(uri.path, ...pathFragment); + } + return uri.with({ path: newPath }); + } + // ---- printing/externalize --------------------------- /** @@ -364,7 +387,7 @@ export class URI implements UriComponents { } else if (data instanceof URI) { return data; } else { - const result = new _URI(data); + const result = new Uri(data); result._formatted = (data).external; result._fsPath = (data)._sep === _pathSepMarker ? (data).fsPath : null; return result; @@ -389,15 +412,15 @@ interface UriState extends UriComponents { const _pathSepMarker = isWindows ? 1 : undefined; -// eslint-disable-next-line @typescript-eslint/class-name-casing -class _URI extends URI { +// This class exists so that URI is compatibile with vscode.Uri (API). +class Uri extends URI { _formatted: string | null = null; _fsPath: string | null = null; get fsPath(): string { if (!this._fsPath) { - this._fsPath = _makeFsPath(this); + this._fsPath = uriToFsPath(this, false); } return this._fsPath; } @@ -553,7 +576,7 @@ function encodeURIComponentMinimal(path: string): string { /** * Compute `fsPath` for the given uri */ -function _makeFsPath(uri: URI): string { +export function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string { let value: string; if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { @@ -564,8 +587,12 @@ function _makeFsPath(uri: URI): string { && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z) && uri.path.charCodeAt(2) === CharCode.Colon ) { - // windows drive letter: file:///c:/far/boo - value = uri.path[1].toLowerCase() + uri.path.substr(2); + if (!keepDriveLetterCasing) { + // windows drive letter: file:///c:/far/boo + value = uri.path[1].toLowerCase() + uri.path.substr(2); + } else { + value = uri.path.substr(1); + } } else { // other path value = uri.path; diff --git a/src/vs/base/common/uuid.ts b/src/vs/base/common/uuid.ts index 3aceb053cdf..141fd83ceb3 100644 --- a/src/vs/base/common/uuid.ts +++ b/src/vs/base/common/uuid.ts @@ -3,87 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * Represents a UUID as defined by rfc4122. - */ -export interface UUID { - - /** - * @returns the canonical representation in sets of hexadecimal numbers separated by dashes. - */ - asHex(): string; -} - -class ValueUUID implements UUID { - - constructor(public _value: string) { - // empty - } - - public asHex(): string { - return this._value; - } -} - -class V4UUID extends ValueUUID { - - private static readonly _chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; - - private static readonly _timeHighBits = ['8', '9', 'a', 'b']; - - private static _oneOf(array: string[]): string { - return array[Math.floor(array.length * Math.random())]; - } - - private static _randomHex(): string { - return V4UUID._oneOf(V4UUID._chars); - } - - constructor() { - super([ - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - '-', - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - '-', - '4', - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - '-', - V4UUID._oneOf(V4UUID._timeHighBits), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - '-', - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - V4UUID._randomHex(), - ].join('')); - } -} - -export function v4(): UUID { - return new V4UUID(); -} const _UUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; @@ -91,18 +10,53 @@ export function isUUID(value: string): boolean { return _UUIDPattern.test(value); } -/** - * Parses a UUID that is of the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. - * @param value A uuid string. - */ -export function parse(value: string): UUID { - if (!isUUID(value)) { - throw new Error('invalid uuid'); - } - - return new ValueUUID(value); +// prep-work +const _data = new Uint8Array(16); +const _hex: string[] = []; +for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); } +// todo@jrieken +// 1. node nodejs use`crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback +// 2. use browser-crypto +const _fillRandomValues = function (bucket: Uint8Array): Uint8Array { + for (let i = 0; i < bucket.length; i++) { + bucket[i] = Math.floor(Math.random() * 256); + } + return bucket; +}; + export function generateUuid(): string { - return v4().asHex(); + // get data + _fillRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; } diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 479c2ceb32c..19f154a7943 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -31,7 +31,7 @@ export function logOnceWebWorkerWarning(err: any): void { } if (!webWorkerWarningLogged) { webWorkerWarningLogged = true; - console.warn('Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/Microsoft/monaco-editor#faq'); + console.warn('Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/microsoft/monaco-editor#faq'); } console.warn(err.message); } diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts deleted file mode 100644 index 6fac5849f96..00000000000 --- a/src/vs/base/node/config.ts +++ /dev/null @@ -1,189 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as fs from 'fs'; -import { dirname } from 'vs/base/common/path'; -import * as objects from 'vs/base/common/objects'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import * as json from 'vs/base/common/json'; -import { statLink } from 'vs/base/node/pfs'; -import { realpath } from 'vs/base/node/extpath'; -import { watchFolder, watchFile } from 'vs/base/node/watcher'; - -export interface IConfigurationChangeEvent { - config: T; -} - -export interface IConfigWatcher { - path: string; - hasParseErrors: boolean; - - reload(callback: (config: T) => void): void; - getConfig(): T; -} - -export interface IConfigOptions { - onError: (error: Error | string) => void; - defaultConfig: T; - changeBufferDelay?: number; - parse?: (content: string, errors: any[]) => T; - initCallback?: (config: T) => void; -} - -/** - * A simple helper to watch a configured file for changes and process its contents as JSON object. - * Supports: - * - comments in JSON files and errors - * - symlinks for the config file itself - * - delayed processing of changes to accomodate for lots of changes - * - configurable defaults - */ -export class ConfigWatcher extends Disposable implements IConfigWatcher { - private cache: T | undefined; - private parseErrors: json.ParseError[] | undefined; - private disposed: boolean | undefined; - private loaded: boolean | undefined; - private timeoutHandle: NodeJS.Timer | null | undefined; - private readonly _onDidUpdateConfiguration: Emitter>; - - constructor(private _path: string, private options: IConfigOptions = { defaultConfig: Object.create(null), onError: error => console.error(error) }) { - super(); - this._onDidUpdateConfiguration = this._register(new Emitter>()); - - this.registerWatcher(); - this.initAsync(); - } - - get path(): string { - return this._path; - } - - get hasParseErrors(): boolean { - return !!this.parseErrors && this.parseErrors.length > 0; - } - - get onDidUpdateConfiguration(): Event> { - return this._onDidUpdateConfiguration.event; - } - - private initAsync(): void { - this.loadAsync(config => { - if (!this.loaded) { - this.updateCache(config); // prevent race condition if config was loaded sync already - } - if (this.options.initCallback) { - this.options.initCallback(this.getConfig()); - } - }); - } - - private updateCache(value: T): void { - this.cache = value; - this.loaded = true; - } - - private loadSync(): T { - try { - return this.parse(fs.readFileSync(this._path).toString()); - } catch (error) { - return this.options.defaultConfig; - } - } - - private loadAsync(callback: (config: T) => void): void { - fs.readFile(this._path, (error, raw) => { - if (error) { - return callback(this.options.defaultConfig); - } - - return callback(this.parse(raw.toString())); - }); - } - - private parse(raw: string): T { - let res: T; - try { - this.parseErrors = []; - res = this.options.parse ? this.options.parse(raw, this.parseErrors) : json.parse(raw, this.parseErrors); - - return res || this.options.defaultConfig; - } catch (error) { - return this.options.defaultConfig; // Ignore parsing errors - } - } - - private registerWatcher(): void { - - // Watch the parent of the path so that we detect ADD and DELETES - const parentFolder = dirname(this._path); - this.watch(parentFolder, true); - - // Check if the path is a symlink and watch its target if so - this.handleSymbolicLink().then(undefined, () => { /* ignore error */ }); - } - - private async handleSymbolicLink(): Promise { - const { stat, isSymbolicLink } = await statLink(this._path); - if (isSymbolicLink && !stat.isDirectory()) { - const realPath = await realpath(this._path); - - this.watch(realPath, false); - } - } - - private watch(path: string, isFolder: boolean): void { - if (this.disposed) { - return; // avoid watchers that will never get disposed by checking for being disposed - } - - if (isFolder) { - this._register(watchFolder(path, (type, path) => path === this._path ? this.onConfigFileChange() : undefined, error => this.options.onError(error))); - } else { - this._register(watchFile(path, () => this.onConfigFileChange(), error => this.options.onError(error))); - } - } - - private onConfigFileChange(): void { - if (this.timeoutHandle) { - global.clearTimeout(this.timeoutHandle); - this.timeoutHandle = null; - } - - // we can get multiple change events for one change, so we buffer through a timeout - this.timeoutHandle = global.setTimeout(() => this.reload(), this.options.changeBufferDelay || 0); - } - - reload(callback?: (config: T) => void): void { - this.loadAsync(currentConfig => { - if (!objects.equals(currentConfig, this.cache)) { - this.updateCache(currentConfig); - - this._onDidUpdateConfiguration.fire({ config: currentConfig }); - } - - if (callback) { - return callback(currentConfig); - } - }); - } - - getConfig(): T { - this.ensureLoaded(); - - return this.cache!; - } - - private ensureLoaded(): void { - if (!this.loaded) { - this.updateCache(this.loadSync()); - } - } - - dispose(): void { - this.disposed = true; - super.dispose(); - } -} diff --git a/src/vs/base/node/crypto.ts b/src/vs/base/node/crypto.ts index f18503ab882..6156c7daa48 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -5,38 +5,34 @@ import * as fs from 'fs'; import * as crypto from 'crypto'; -import * as stream from 'stream'; import { once } from 'vs/base/common/functional'; -export function checksum(path: string, sha1hash: string | undefined): Promise { - const promise = new Promise((c, e) => { +export async function checksum(path: string, sha1hash: string | undefined): Promise { + const checksumPromise = new Promise((resolve, reject) => { const input = fs.createReadStream(path); const hash = crypto.createHash('sha1'); - const hashStream = hash as any as stream.PassThrough; - input.pipe(hashStream); + input.pipe(hash); const done = once((err?: Error, result?: string) => { input.removeAllListeners(); - hashStream.removeAllListeners(); + hash.removeAllListeners(); if (err) { - e(err); + reject(err); } else { - c(result); + resolve(result); } }); input.once('error', done); input.once('end', done); - hashStream.once('error', done); - hashStream.once('data', (data: Buffer) => done(undefined, data.toString('hex'))); + hash.once('error', done); + hash.once('data', (data: Buffer) => done(undefined, data.toString('hex'))); }); - return promise.then(hash => { - if (hash !== sha1hash) { - return Promise.reject(new Error('Hash mismatch')); - } + const hash = await checksumPromise; - return Promise.resolve(); - }); + if (hash !== sha1hash) { + throw new Error('Hash mismatch'); + } } diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts deleted file mode 100644 index 29468755138..00000000000 --- a/src/vs/base/node/encoding.ts +++ /dev/null @@ -1,354 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as iconv from 'iconv-lite'; -import { Readable, Writable } from 'stream'; -import { VSBuffer } from 'vs/base/common/buffer'; - -export const UTF8 = 'utf8'; -export const UTF8_with_bom = 'utf8bom'; -export const UTF16be = 'utf16be'; -export const UTF16le = 'utf16le'; - -export type UTF_ENCODING = typeof UTF8 | typeof UTF8_with_bom | typeof UTF16be | typeof UTF16le; - -export function isUTFEncoding(encoding: string): encoding is UTF_ENCODING { - return [UTF8, UTF8_with_bom, UTF16be, UTF16le].some(utfEncoding => utfEncoding === encoding); -} - -export const UTF16be_BOM = [0xFE, 0xFF]; -export const UTF16le_BOM = [0xFF, 0xFE]; -export const UTF8_BOM = [0xEF, 0xBB, 0xBF]; - -const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not -const NO_ENCODING_GUESS_MIN_BYTES = 512; // when not auto guessing the encoding, small number of bytes are enough -const AUTO_ENCODING_GUESS_MIN_BYTES = 512 * 8; // with auto guessing we want a lot more content to be read for guessing -const AUTO_ENCODING_GUESS_MAX_BYTES = 512 * 128; // set an upper limit for the number of bytes we pass on to jschardet - -export interface IDecodeStreamOptions { - guessEncoding: boolean; - minBytesRequiredForDetection?: number; - - overwriteEncoding(detectedEncoding: string | null): string; -} - -export interface IDecodeStreamResult { - stream: NodeJS.ReadableStream; - detected: IDetectedEncodingResult; -} - -export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise { - if (!options.minBytesRequiredForDetection) { - options.minBytesRequiredForDetection = options.guessEncoding ? AUTO_ENCODING_GUESS_MIN_BYTES : NO_ENCODING_GUESS_MIN_BYTES; - } - - return new Promise((resolve, reject) => { - const writer = new class extends Writable { - private decodeStream: NodeJS.ReadWriteStream | undefined; - private decodeStreamPromise: Promise | undefined; - - private bufferedChunks: Buffer[] = []; - private bytesBuffered = 0; - - _write(chunk: Buffer, encoding: string, callback: (error: Error | null) => void): void { - if (!Buffer.isBuffer(chunk)) { - return callback(new Error('toDecodeStream(): data must be a buffer')); - } - - // if the decode stream is ready, we just write directly - if (this.decodeStream) { - this.decodeStream.write(chunk, callback); - - return; - } - - // otherwise we need to buffer the data until the stream is ready - this.bufferedChunks.push(chunk); - this.bytesBuffered += chunk.byteLength; - - // waiting for the decoder to be ready - if (this.decodeStreamPromise) { - this.decodeStreamPromise.then(() => callback(null), error => callback(error)); - } - - // buffered enough data for encoding detection, create stream and forward data - else if (typeof options.minBytesRequiredForDetection === 'number' && this.bytesBuffered >= options.minBytesRequiredForDetection) { - this._startDecodeStream(callback); - } - - // only buffering until enough data for encoding detection is there - else { - callback(null); - } - } - - _startDecodeStream(callback: (error: Error | null) => void): void { - - // detect encoding from buffer - this.decodeStreamPromise = Promise.resolve(detectEncodingFromBuffer({ - buffer: Buffer.concat(this.bufferedChunks), - bytesRead: this.bytesBuffered - }, options.guessEncoding)).then(detected => { - - // ensure to respect overwrite of encoding - detected.encoding = options.overwriteEncoding(detected.encoding); - - // decode and write buffer - this.decodeStream = decodeStream(detected.encoding); - this.decodeStream.write(Buffer.concat(this.bufferedChunks), callback); - this.bufferedChunks.length = 0; - - // signal to the outside our detected encoding - // and final decoder stream - resolve({ detected, stream: this.decodeStream }); - }, error => { - this.emit('error', error); - - callback(error); - }); - } - - _final(callback: () => void) { - - // normal finish - if (this.decodeStream) { - this.decodeStream.end(callback); - } - - // we were still waiting for data to do the encoding - // detection. thus, wrap up starting the stream even - // without all the data to get things going - else { - this._startDecodeStream(() => { - if (this.decodeStream) { - this.decodeStream.end(callback); - } - }); - } - } - }; - - // errors - readable.on('error', reject); - - // pipe through - readable.pipe(writer); - }); -} - -export function decode(buffer: Buffer, encoding: string): string { - return iconv.decode(buffer, toNodeEncoding(encoding)); -} - -export function encode(content: string | Buffer, encoding: string, options?: { addBOM?: boolean }): Buffer { - return iconv.encode(content as string /* TODO report into upstream typings */, toNodeEncoding(encoding), options); -} - -export function encodingExists(encoding: string): boolean { - return iconv.encodingExists(toNodeEncoding(encoding)); -} - -function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { - return iconv.decodeStream(toNodeEncoding(encoding)); -} - -export function encodeStream(encoding: string, options?: { addBOM?: boolean }): NodeJS.ReadWriteStream { - return iconv.encodeStream(toNodeEncoding(encoding), options); -} - -function toNodeEncoding(enc: string | null): string { - if (enc === UTF8_with_bom || enc === null) { - return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it - } - - return enc; -} - -export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): typeof UTF8_with_bom | typeof UTF16le | typeof UTF16be | null { - if (!buffer || bytesRead < UTF16be_BOM.length) { - return null; - } - - const b0 = buffer.readUInt8(0); - const b1 = buffer.readUInt8(1); - - // UTF-16 BE - if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) { - return UTF16be; - } - - // UTF-16 LE - if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) { - return UTF16le; - } - - if (bytesRead < UTF8_BOM.length) { - return null; - } - - const b2 = buffer.readUInt8(2); - - // UTF-8 - if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) { - return UTF8_with_bom; - } - - return null; -} - -// we explicitly ignore a specific set of encodings from auto guessing -// - ASCII: we never want this encoding (most UTF-8 files would happily detect as -// ASCII files and then you could not type non-ASCII characters anymore) -// - UTF-16: we have our own detection logic for UTF-16 -// - UTF-32: we do not support this encoding in VSCode -const IGNORE_ENCODINGS = ['ascii', 'utf-16', 'utf-32']; - -/** - * Guesses the encoding from buffer. - */ -async function guessEncodingByBuffer(buffer: Buffer): Promise { - const jschardet = await import('jschardet'); - - const guessed = jschardet.detect(buffer.slice(0, AUTO_ENCODING_GUESS_MAX_BYTES)); // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53 - if (!guessed || !guessed.encoding) { - return null; - } - - const enc = guessed.encoding.toLowerCase(); - if (0 <= IGNORE_ENCODINGS.indexOf(enc)) { - return null; // see comment above why we ignore some encodings - } - - return toIconvLiteEncoding(guessed.encoding); -} - -const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { - 'ibm866': 'cp866', - 'big5': 'cp950' -}; - -function toIconvLiteEncoding(encodingName: string): string { - const normalizedEncodingName = encodingName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); - const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName]; - - return mapped || normalizedEncodingName; -} - -/** - * The encodings that are allowed in a settings file don't match the canonical encoding labels specified by WHATWG. - * See https://encoding.spec.whatwg.org/#names-and-labels - * Iconv-lite strips all non-alphanumeric characters, but ripgrep doesn't. For backcompat, allow these labels. - */ -export function toCanonicalName(enc: string): string { - switch (enc) { - case 'shiftjis': - return 'shift-jis'; - case 'utf16le': - return 'utf-16le'; - case 'utf16be': - return 'utf-16be'; - case 'big5hkscs': - return 'big5-hkscs'; - case 'eucjp': - return 'euc-jp'; - case 'euckr': - return 'euc-kr'; - case 'koi8r': - return 'koi8-r'; - case 'koi8u': - return 'koi8-u'; - case 'macroman': - return 'x-mac-roman'; - case 'utf8bom': - return 'utf8'; - default: - const m = enc.match(/windows(\d+)/); - if (m) { - return 'windows-' + m[1]; - } - - return enc; - } -} - -export interface IDetectedEncodingResult { - encoding: string | null; - seemsBinary: boolean; -} - -export interface IReadResult { - buffer: Buffer | null; - bytesRead: number; -} - -export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: false): IDetectedEncodingResult; -export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: boolean): Promise; -export function detectEncodingFromBuffer({ buffer, bytesRead }: IReadResult, autoGuessEncoding?: boolean): Promise | IDetectedEncodingResult { - - // Always first check for BOM to find out about encoding - let encoding = detectEncodingByBOMFromBuffer(buffer, bytesRead); - - // Detect 0 bytes to see if file is binary or UTF-16 LE/BE - // unless we already know that this file has a UTF-16 encoding - let seemsBinary = false; - if (encoding !== UTF16be && encoding !== UTF16le && buffer) { - let couldBeUTF16LE = true; // e.g. 0xAA 0x00 - let couldBeUTF16BE = true; // e.g. 0x00 0xAA - let containsZeroByte = false; - - // This is a simplified guess to detect UTF-16 BE or LE by just checking if - // the first 512 bytes have the 0-byte at a specific location. For UTF-16 LE - // this would be the odd byte index and for UTF-16 BE the even one. - // Note: this can produce false positives (a binary file that uses a 2-byte - // encoding of the same format as UTF-16) and false negatives (a UTF-16 file - // that is using 4 bytes to encode a character). - for (let i = 0; i < bytesRead && i < ZERO_BYTE_DETECTION_BUFFER_MAX_LEN; i++) { - const isEndian = (i % 2 === 1); // assume 2-byte sequences typical for UTF-16 - const isZeroByte = (buffer.readInt8(i) === 0); - - if (isZeroByte) { - containsZeroByte = true; - } - - // UTF-16 LE: expect e.g. 0xAA 0x00 - if (couldBeUTF16LE && (isEndian && !isZeroByte || !isEndian && isZeroByte)) { - couldBeUTF16LE = false; - } - - // UTF-16 BE: expect e.g. 0x00 0xAA - if (couldBeUTF16BE && (isEndian && isZeroByte || !isEndian && !isZeroByte)) { - couldBeUTF16BE = false; - } - - // Return if this is neither UTF16-LE nor UTF16-BE and thus treat as binary - if (isZeroByte && !couldBeUTF16LE && !couldBeUTF16BE) { - break; - } - } - - // Handle case of 0-byte included - if (containsZeroByte) { - if (couldBeUTF16LE) { - encoding = UTF16le; - } else if (couldBeUTF16BE) { - encoding = UTF16be; - } else { - seemsBinary = true; - } - } - } - - // Auto guess encoding if configured - if (autoGuessEncoding && !seemsBinary && !encoding && buffer) { - return guessEncodingByBuffer(buffer.slice(0, bytesRead)).then(guessedEncoding => { - return { - seemsBinary: false, - encoding: guessedEncoding - }; - }); - } - - return { seemsBinary, encoding }; -} diff --git a/src/vs/base/node/extpath.ts b/src/vs/base/node/extpath.ts index b3b55b7aaee..0096544ada5 100644 --- a/src/vs/base/node/extpath.ts +++ b/src/vs/base/node/extpath.ts @@ -10,7 +10,7 @@ import { readdirSync } from 'vs/base/node/pfs'; import { promisify } from 'util'; /** - * Copied from: https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83 + * Copied from: https://github.com/microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83 * * Given an absolute, normalized, and existing file path 'realcase' returns the exact path that the file has on disk. * On a case insensitive file system, the returned path might differ from the original path by character casing. @@ -88,4 +88,4 @@ export function realpathSync(path: string): string { function normalizePath(path: string): string { return rtrim(normalize(path), sep); -} \ No newline at end of file +} diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index c2faa20632f..2799ffc718d 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -21,7 +21,7 @@ import { getMac } from 'vs/base/node/macAddress'; // Sun xVM VirtualBox 08-00-27 export const virtualMachineHint: { value(): number } = new class { - private _virtualMachineOUIs?: TernarySearchTree; + private _virtualMachineOUIs?: TernarySearchTree; private _value?: number; private _isVirtualMachineMacAdress(mac: string): boolean { diff --git a/src/vs/base/node/macAddress.ts b/src/vs/base/node/macAddress.ts index 524b136c06b..35fec9fc86b 100644 --- a/src/vs/base/node/macAddress.ts +++ b/src/vs/base/node/macAddress.ts @@ -3,13 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { exec } from 'child_process'; -import { isWindows } from 'vs/base/common/platform'; - -const cmdline = { - windows: 'getmac.exe', - unix: '/sbin/ifconfig -a || /sbin/ip link' -}; +import { networkInterfaces } from 'os'; const invalidMacAddresses = new Set([ '00:00:00:00:00:00', @@ -39,23 +33,16 @@ export function getMac(): Promise { function doGetMac(): Promise { return new Promise((resolve, reject) => { try { - exec(isWindows ? cmdline.windows : cmdline.unix, { timeout: 10000 }, (err, stdout, stdin) => { - if (err) { - return reject(`Unable to retrieve mac address (${err.toString()})`); - } else { - const regex = /(?:[a-f\d]{2}[:\-]){5}[a-f\d]{2}/gi; - - let match; - while ((match = regex.exec(stdout)) !== null) { - const macAddressCandidate = match[0]; - if (validateMacAddress(macAddressCandidate)) { - return resolve(macAddressCandidate); - } + const ifaces = networkInterfaces(); + for (const [, infos] of Object.entries(ifaces)) { + for (const info of infos) { + if (validateMacAddress(info.mac)) { + return resolve(info.mac); } - - return reject('Unable to retrieve mac address (unexpected format)'); } - }); + } + + reject('Unable to retrieve mac address (unexpected format)'); } catch (err) { reject(err); } diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts index 66930cdaf4b..977eaf8806f 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/node/paths.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; interface IPaths { getAppDataPath(platform: string): string; getDefaultUserDataPath(platform: string): string; } -const pathsPath = getPathFromAmdModule(require, 'paths'); +const pathsPath = FileAccess.asFileUri('paths', require).fsPath; const paths = require.__$__nodeRequire(pathsPath); export const getAppDataPath = paths.getAppDataPath; export const getDefaultUserDataPath = paths.getDefaultUserDataPath; diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 1b294073eed..3a17bd3fc78 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -9,12 +9,21 @@ import * as fs from 'fs'; import * as os from 'os'; import * as platform from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; -import { endsWith } from 'vs/base/common/strings'; import { promisify } from 'util'; import { isRootOrDriveLetter } from 'vs/base/common/extpath'; import { generateUuid } from 'vs/base/common/uuid'; import { normalizeNFC } from 'vs/base/common/normalization'; -import { encode, encodeStream } from 'vs/base/node/encoding'; + +// See https://github.com/microsoft/vscode/issues/30180 +const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB +const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB + +// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149 +const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB +const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB + +export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; +export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; export enum RimRafMode { @@ -178,30 +187,52 @@ export function stat(path: string): Promise { } export interface IStatAndLink { + + // The stats of the file. If the file is a symbolic + // link, the stats will be of that target file and + // not the link itself. + // If the file is a symbolic link pointing to a non + // existing file, the stat will be of the link and + // the `dangling` flag will indicate this. stat: fs.Stats; - isSymbolicLink: boolean; + + // Will be provided if the resource is a symbolic link + // on disk. Use the `dangling` flag to find out if it + // points to a resource that does not exist on disk. + symbolicLink?: { dangling: boolean }; } export async function statLink(path: string): Promise { // First stat the link - let linkStat: fs.Stats | undefined; - let linkStatError: NodeJS.ErrnoException | undefined; + let lstats: fs.Stats | undefined; try { - linkStat = await lstat(path); + lstats = await lstat(path); + + // Return early if the stat is not a symbolic link at all + if (!lstats.isSymbolicLink()) { + return { stat: lstats }; + } } catch (error) { - linkStatError = error; + /* ignore - use stat() instead */ } - // Then stat the target and return that - const isLink = !!(linkStat && linkStat.isSymbolicLink()); - if (linkStatError || isLink) { - const fileStat = await stat(path); + // If the stat is a symbolic link or failed to stat, use fs.stat() + // which for symbolic links will stat the target they point to + try { + const stats = await stat(path); - return { stat: fileStat, isSymbolicLink: isLink }; + return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined }; + } catch (error) { + + // If the link points to a non-existing file we still want + // to return it as result while setting dangling: true flag + if (error.code === 'ENOENT' && lstats) { + return { stat: lstats, symbolicLink: { dangling: true } }; + } + + throw error; } - - return { stat: linkStat!, isSymbolicLink: false }; } export function lstat(path: string): Promise { @@ -213,9 +244,7 @@ export function rename(oldPath: string, newPath: string): Promise { } export function renameIgnoreError(oldPath: string, newPath: string): Promise { - return new Promise(resolve => { - fs.rename(oldPath, newPath, () => resolve()); - }); + return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve())); } export function unlink(path: string): Promise { @@ -236,6 +265,10 @@ export function readFile(path: string, encoding?: string): Promise { + return promisify(fs.mkdir)(path, { mode, recursive: true }); +} + // According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback) // it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return. // Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly. @@ -244,18 +277,21 @@ const writeFilePathQueues: Map> = new Map(); export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise; export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise; export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise; -export function writeFile(path: string, data: NodeJS.ReadableStream, options?: IWriteFileOptions): Promise; -export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise; -export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise { +export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise; +export function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise { const queueKey = toQueueKey(path); - return ensureWriteFileQueue(queueKey).queue(() => writeFileAndFlush(path, data, options)); + return ensureWriteFileQueue(queueKey).queue(() => { + const ensuredOptions = ensureWriteOptions(options); + + return new Promise((resolve, reject) => doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve())); + }); } function toQueueKey(path: string): string { let queueKey = path; if (platform.isWindows || platform.isMacintosh) { - queueKey = queueKey.toLowerCase(); // accomodate for case insensitive file systems + queueKey = queueKey.toLowerCase(); // accommodate for case insensitive file systems } return queueKey; @@ -282,10 +318,6 @@ function ensureWriteFileQueue(queueKey: string): Queue { export interface IWriteFileOptions { mode?: number; flag?: string; - encoding?: { - charset: string; - addBOM: boolean; - }; } interface IEnsuredWriteFileOptions extends IWriteFileOptions { @@ -294,103 +326,6 @@ interface IEnsuredWriteFileOptions extends IWriteFileOptions { } let canFlush = true; -function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions | undefined): Promise { - const ensuredOptions = ensureWriteOptions(options); - - return new Promise((resolve, reject) => { - if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) { - doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()); - } else { - doWriteFileStreamAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()); - } - }); -} - -function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void { - - // finish only once - let finished = false; - const finish = (error?: Error) => { - if (!finished) { - finished = true; - - // in error cases we need to manually close streams - // if the write stream was successfully opened - if (error) { - if (isOpen) { - writer.once('close', () => callback(error)); - writer.destroy(); - } else { - callback(error); - } - } - - // otherwise just return without error - else { - callback(); - } - } - }; - - // create writer to target. we set autoClose: false because we want to use the streams - // file descriptor to call fs.fdatasync to ensure the data is flushed to disk - const writer = fs.createWriteStream(path, { mode: options.mode, flags: options.flag, autoClose: false }); - - // Event: 'open' - // Purpose: save the fd for later use and start piping - // Notes: will not be called when there is an error opening the file descriptor! - let fd: number; - let isOpen: boolean; - writer.once('open', descriptor => { - fd = descriptor; - isOpen = true; - - // if an encoding is provided, we need to pipe the stream through - // an encoder stream and forward the encoding related options - if (options.encoding) { - reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM })); - } - - // start data piping only when we got a successful open. this ensures that we do - // not consume the stream when an error happens and helps to fix this issue: - // https://github.com/Microsoft/vscode/issues/42542 - reader.pipe(writer); - }); - - // Event: 'error' - // Purpose: to return the error to the outside and to close the write stream (does not happen automatically) - reader.once('error', error => finish(error)); - writer.once('error', error => finish(error)); - - // Event: 'finish' - // Purpose: use fs.fdatasync to flush the contents to disk - // Notes: event is called when the writer has finished writing to the underlying resource. we must call writer.close() - // because we have created the WriteStream with autoClose: false - writer.once('finish', () => { - - // flush to disk - if (canFlush && isOpen) { - fs.fdatasync(fd, (syncError: Error) => { - - // In some exotic setups it is well possible that node fails to sync - // In that case we disable flushing and warn to the console - if (syncError) { - console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError); - canFlush = false; - } - - writer.destroy(); - }); - } else { - writer.destroy(); - } - }); - - // Event: 'close' - // Purpose: signal we are done to the outside - // Notes: event is called when the writer's filedescriptor is closed - writer.once('close', () => finish()); -} // Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk // We do this in cases where we want to make sure the data is really on disk and @@ -398,10 +333,6 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, // // See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194 function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, options: IEnsuredWriteFileOptions, callback: (error: Error | null) => void): void { - if (options.encoding) { - data = encode(data instanceof Uint8Array ? Buffer.from(data) : data, options.encoding.charset, { addBOM: options.encoding.addBOM }); - } - if (!canFlush) { return fs.writeFile(path, data, { mode: options.mode, flag: options.flag }, callback); } @@ -419,7 +350,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o } // Flush contents (not metadata) of the file to disk - fs.fdatasync(fd, (syncError: Error) => { + fs.fdatasync(fd, (syncError: Error | null) => { // In some exotic setups it is well possible that node fails to sync // In that case we disable flushing and warn to the console @@ -437,10 +368,6 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void { const ensuredOptions = ensureWriteOptions(options); - if (ensuredOptions.encoding) { - data = encode(data, ensuredOptions.encoding.charset, { addBOM: ensuredOptions.encoding.addBOM }); - } - if (!canFlush) { return fs.writeFileSync(path, data, { mode: ensuredOptions.mode, flag: ensuredOptions.flag }); } @@ -472,8 +399,7 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio return { mode: typeof options.mode === 'number' ? options.mode : 0o666, - flag: typeof options.flag === 'string' ? options.flag : 'w', - encoding: options.encoding + flag: typeof options.flag === 'string' ? options.flag : 'w' }; } @@ -565,7 +491,7 @@ export async function move(source: string, target: string): Promise { // // 2.) The user tries to rename a file/folder that ends with a dot. This is not // really possible to move then, at least on UNC devices. - if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || endsWith(source, '.')) { + if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) { await copy(source, target); await rimraf(source, RimRafMode.MOVE); await updateMtime(target); @@ -631,18 +557,3 @@ async function doCopyFile(source: string, target: string, mode: number): Promise reader.pipe(writer); }); } - -export async function mkdirp(path: string, mode?: number): Promise { - return promisify(fs.mkdir)(path, { mode, recursive: true }); -} - -// See https://github.com/Microsoft/vscode/issues/30180 -const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB -const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB - -// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149 -const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB -const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB - -export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; -export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index b6f7742e2b3..213d0bdf512 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -15,10 +15,10 @@ import * as extpath from 'vs/base/common/extpath'; import * as Platform from 'vs/base/common/platform'; import { LineDecoder } from 'vs/base/node/decoder'; import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode, Executable } from 'vs/base/common/processes'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; -export type ValueCallback = (value?: T | Promise) => void; +export type ValueCallback = (value: T | Promise) => void; export type ErrorCallback = (error?: any) => void; export type ProgressCallback = (progress: T) => void; @@ -67,7 +67,7 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise { cp.execFile(cmd, [process.pid.toString()], { encoding: 'utf8', shell: true } as cp.ExecFileOptions, (err, stdout, stderr) => { if (err) { @@ -86,8 +86,8 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise { @@ -98,7 +98,7 @@ export abstract class AbstractProcess { private childProcess: cp.ChildProcess | null; protected childProcessPromise: Promise | null; - private pidResolve?: ValueCallback; + private pidResolve: ValueCallback | undefined; protected terminateRequested: boolean; private static WellKnowCommands: IStringDictionary = { @@ -318,16 +318,16 @@ export abstract class AbstractProcess { } private useExec(): Promise { - return new Promise((c, e) => { + return new Promise(resolve => { if (!this.shell || !Platform.isWindows) { - return c(false); + return resolve(false); } const cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); cmdShell.on('error', (error: Error) => { - return c(true); + return resolve(true); }); cmdShell.on('exit', (data: any) => { - return c(false); + return resolve(false); }); }); } @@ -409,7 +409,7 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender return; } - const result = childProcess.send(msg, (error: Error) => { + const result = childProcess.send(msg, (error: Error | null) => { if (error) { console.error(error); // unlikely to happen, best we can do is log this error } @@ -457,7 +457,7 @@ export namespace win32 { async function fileExists(path: string): Promise { if (await promisify(fs.exists)(path)) { - return !((await promisify(fs.stat)(path)).isDirectory); + return !((await promisify(fs.stat)(path)).isDirectory()); } return false; } diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 55c3a73da57..dd5d53d67b4 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -5,7 +5,7 @@ import { exec } from 'child_process'; import { ProcessItem } from 'vs/base/common/processes'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export function listProcesses(rootPid: number): Promise { @@ -180,7 +180,7 @@ export function listProcesses(rootPid: number): Promise { // The cpu usage value reported on Linux is the average over the process lifetime, // recalculate the usage over a one second interval // JSON.stringify is needed to escape spaces, https://github.com/nodejs/node/issues/6803 - let cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh')); + let cmd = JSON.stringify(FileAccess.asFileUri('vs/base/node/cpuUsage.sh', require).fsPath); cmd += ' ' + pids.join(' '); exec(cmd, {}, (err, stdout, stderr) => { @@ -193,6 +193,11 @@ export function listProcesses(rootPid: number): Promise { processInfo.load = parseFloat(cpuUsage[i]); } + if (!rootItem) { + reject(new Error(`Root process ${rootPid} not found`)); + return; + } + resolve(rootItem); } }); @@ -203,7 +208,7 @@ export function listProcesses(rootPid: number): Promise { if (process.platform !== 'linux') { reject(err || new Error(stderr.toString())); } else { - const cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/ps.sh')); + const cmd = JSON.stringify(FileAccess.asFileUri('vs/base/node/ps.sh', require).fsPath); exec(cmd, {}, (err, stdout, stderr) => { if (err || stderr) { reject(err || new Error(stderr.toString())); @@ -219,7 +224,8 @@ export function listProcesses(rootPid: number): Promise { // Set numeric locale to ensure '.' is used as the decimal separator exec(`${ps} ${args}`, { maxBuffer: 1000 * 1024, env: { LC_NUMERIC: 'en_US.UTF-8' } }, (err, stdout, stderr) => { - if (err || stderr) { + // Silently ignoring the screen size is bogus error. See https://github.com/microsoft/vscode/issues/98590 + if (err || (stderr && !stderr.includes('screen size is bogus'))) { reject(err || new Error(stderr.toString())); } else { parsePsOutput(stdout, addToTree); @@ -227,7 +233,11 @@ export function listProcesses(rootPid: number): Promise { if (process.platform === 'linux') { calculateLinuxCpuUsage(); } else { - resolve(rootItem); + if (!rootItem) { + reject(new Error(`Root process ${rootPid} not found`)); + } else { + resolve(rootItem); + } } } }); @@ -246,4 +256,4 @@ function parsePsOutput(stdout: string, addToTree: (pid: number, ppid: number, cm addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4])); } } -} \ No newline at end of file +} diff --git a/src/vs/base/node/stream.ts b/src/vs/base/node/stream.ts deleted file mode 100644 index 16b73835bd1..00000000000 --- a/src/vs/base/node/stream.ts +++ /dev/null @@ -1,176 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as fs from 'fs'; -import { VSBufferReadableStream, VSBufferReadable, VSBuffer } from 'vs/base/common/buffer'; -import { Readable } from 'stream'; -import { isUndefinedOrNull } from 'vs/base/common/types'; -import { UTF8, UTF8_with_bom, UTF8_BOM, UTF16be, UTF16le_BOM, UTF16be_BOM, UTF16le, UTF_ENCODING } from 'vs/base/node/encoding'; - -/** - * Reads a file until a matching string is found. - * - * @param file The file to read. - * @param matchingString The string to search for. - * @param chunkBytes The number of bytes to read each iteration. - * @param maximumBytesToRead The maximum number of bytes to read before giving up. - * @param callback The finished callback. - */ -export function readToMatchingString(file: string, matchingString: string, chunkBytes: number, maximumBytesToRead: number): Promise { - return new Promise((resolve, reject) => - fs.open(file, 'r', null, (err, fd) => { - if (err) { - return reject(err); - } - - function end(err: Error | null, result: string | null): void { - fs.close(fd, closeError => { - if (closeError) { - return reject(closeError); - } - - if (err && (err).code === 'EISDIR') { - return reject(err); // we want to bubble this error up (file is actually a folder) - } - - return resolve(result); - }); - } - - const buffer = Buffer.allocUnsafe(maximumBytesToRead); - let offset = 0; - - function readChunk(): void { - fs.read(fd, buffer, offset, chunkBytes, null, (err, bytesRead) => { - if (err) { - return end(err, null); - } - - if (bytesRead === 0) { - return end(null, null); - } - - offset += bytesRead; - - const newLineIndex = buffer.indexOf(matchingString); - if (newLineIndex >= 0) { - return end(null, buffer.toString('utf8').substr(0, newLineIndex)); - } - - if (offset >= maximumBytesToRead) { - return end(new Error(`Could not find ${matchingString} in first ${maximumBytesToRead} bytes of ${file}`), null); - } - - return readChunk(); - }); - } - - readChunk(); - }) - ); -} - -export function streamToNodeReadable(stream: VSBufferReadableStream): Readable { - return new class extends Readable { - private listening = false; - - _read(size?: number): void { - if (!this.listening) { - this.listening = true; - - // Data - stream.on('data', data => { - try { - if (!this.push(data.buffer)) { - stream.pause(); // pause the stream if we should not push anymore - } - } catch (error) { - this.emit(error); - } - }); - - // End - stream.on('end', () => { - try { - this.push(null); // signal EOS - } catch (error) { - this.emit(error); - } - }); - - // Error - stream.on('error', error => this.emit('error', error)); - } - - // ensure the stream is flowing - stream.resume(); - } - - _destroy(error: Error | null, callback: (error: Error | null) => void): void { - stream.destroy(); - - callback(null); - } - }; -} - -export function nodeReadableToString(stream: NodeJS.ReadableStream): Promise { - return new Promise((resolve, reject) => { - let result = ''; - - stream.on('data', chunk => result += chunk); - stream.on('error', reject); - stream.on('end', () => resolve(result)); - }); -} - -export function nodeStreamToVSBufferReadable(stream: NodeJS.ReadWriteStream, addBOM?: { encoding: UTF_ENCODING }): VSBufferReadable { - let bytesRead = 0; - let done = false; - - return { - read(): VSBuffer | null { - if (done) { - return null; - } - - const res = stream.read(); - if (isUndefinedOrNull(res)) { - done = true; - - // If we are instructed to add a BOM but we detect that no - // bytes have been read, we must ensure to return the BOM - // ourselves so that we comply with the contract. - if (bytesRead === 0 && addBOM) { - switch (addBOM.encoding) { - case UTF8: - case UTF8_with_bom: - return VSBuffer.wrap(Buffer.from(UTF8_BOM)); - case UTF16be: - return VSBuffer.wrap(Buffer.from(UTF16be_BOM)); - case UTF16le: - return VSBuffer.wrap(Buffer.from(UTF16le_BOM)); - } - } - - return null; - } - - // Handle String - if (typeof res === 'string') { - bytesRead += res.length; - - return VSBuffer.fromString(res); - } - - // Handle Buffer - else { - bytesRead += res.byteLength; - - return VSBuffer.wrap(res); - } - } - }; -} diff --git a/src/vs/base/node/terminalEncoding.ts b/src/vs/base/node/terminalEncoding.ts index 3077f3a9633..32ff35ea028 100644 --- a/src/vs/base/node/terminalEncoding.ts +++ b/src/vs/base/node/terminalEncoding.ts @@ -7,7 +7,7 @@ * This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small. */ import { exec } from 'child_process'; -import * as os from 'os'; +import { isWindows } from 'vs/base/common/platform'; const windowsTerminalEncodings = { '437': 'cp437', // United States @@ -39,9 +39,8 @@ const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { const UTF8 = 'utf8'; - export async function resolveTerminalEncoding(verbose?: boolean): Promise { - let rawEncodingPromise: Promise; + let rawEncodingPromise: Promise; // Support a global environment variable to win over other mechanics const cliEncodingEnv = process.env['VSCODE_CLI_ENCODING']; @@ -54,14 +53,18 @@ export async function resolveTerminalEncoding(verbose?: boolean): Promise(resolve => { + else if (isWindows) { + rawEncodingPromise = new Promise(resolve => { if (verbose) { console.log('Running "chcp" to detect terminal encoding...'); } exec('chcp', (err, stdout, stderr) => { if (stdout) { + if (verbose) { + console.log(`Output from "chcp" command is: ${stdout}`); + } + const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings) as Array; for (const key of windowsTerminalEncodingKeys) { if (stdout.indexOf(key) >= 0) { diff --git a/src/vs/base/node/watcher.ts b/src/vs/base/node/watcher.ts index b71a84a63f1..a15daf9ce1b 100644 --- a/src/vs/base/node/watcher.ts +++ b/src/vs/base/node/watcher.ts @@ -58,7 +58,7 @@ function doWatchNonRecursive(file: { path: string, isDirectory: boolean }, onCha // Normalize file name let changedFileName: string = ''; - if (raw) { // https://github.com/Microsoft/vscode/issues/38191 + if (raw) { // https://github.com/microsoft/vscode/issues/38191 changedFileName = raw.toString(); if (isMacintosh) { // Mac: uses NFD unicode form on disk, but we want NFC diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 93ebb6ad607..4d98c0f29ce 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -12,7 +12,7 @@ import { mkdirp, rimraf } from 'vs/base/node/pfs'; import { open as _openZip, Entry, ZipFile } from 'yauzl'; import * as yazl from 'yazl'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event } from 'vs/base/common/event'; +import { assertIsDefined } from 'vs/base/common/types'; export interface IExtractOptions { overwrite?: boolean; @@ -73,14 +73,14 @@ function toExtractError(err: Error): ExtractError { function extractEntry(stream: Readable, fileName: string, mode: number, targetPath: string, options: IOptions, token: CancellationToken): Promise { const dirName = path.dirname(fileName); const targetDirName = path.join(targetPath, dirName); - if (targetDirName.indexOf(targetPath) !== 0) { + if (!targetDirName.startsWith(targetPath)) { return Promise.reject(new Error(nls.localize('invalid file', "Error extracting {0}. Invalid file.", fileName))); } const targetFileName = path.join(targetPath, fileName); let istream: WriteStream; - Event.once(token.onCancellationRequested)(() => { + token.onCancellationRequested(() => { if (istream) { istream.destroy(); } @@ -107,7 +107,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok let last = createCancelablePromise(() => Promise.resolve()); let extractedEntriesCount = 0; - Event.once(token.onCancellationRequested)(() => { + token.onCancellationRequested(() => { last.cancel(); zipfile.close(); }); @@ -162,24 +162,24 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok } function openZip(zipFile: string, lazy: boolean = false): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { _openZip(zipFile, lazy ? { lazyEntries: true } : undefined!, (error?: Error, zipfile?: ZipFile) => { if (error) { reject(toExtractError(error)); } else { - resolve(zipfile); + resolve(assertIsDefined(zipfile)); } }); }); } function openZipStream(zipFile: ZipFile, entry: Entry): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { zipFile.openReadStream(entry, (error?: Error, stream?: Readable) => { if (error) { reject(toExtractError(error)); } else { - resolve(stream); + resolve(assertIsDefined(stream)); } }); }); diff --git a/src/vs/base/parts/contextmenu/common/contextmenu.ts b/src/vs/base/parts/contextmenu/common/contextmenu.ts index c31a49474f1..0b99dbcb328 100644 --- a/src/vs/base/parts/contextmenu/common/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/common/contextmenu.ts @@ -36,8 +36,7 @@ export interface IPopupOptions { x?: number; y?: number; positioningItem?: number; - onHide?: () => void; } export const CONTEXT_MENU_CHANNEL = 'vscode:contextmenu'; -export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; \ No newline at end of file +export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index 6222be78028..ebd389cfa9d 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -5,18 +5,19 @@ import { Menu, MenuItem, BrowserWindow, ipcMain, IpcMainEvent } from 'electron'; import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu'; +import { withNullAsUndefined } from 'vs/base/common/types'; export function registerContextMenuListener(): void { ipcMain.on(CONTEXT_MENU_CHANNEL, (event: IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { const menu = createMenu(event, onClickChannel, items); menu.popup({ - window: BrowserWindow.fromWebContents(event.sender), + window: withNullAsUndefined(BrowserWindow.fromWebContents(event.sender)), x: options ? options.x : undefined, y: options ? options.y : undefined, positioningItem: options ? options.positioningItem : undefined, callback: () => { - // Workaround for https://github.com/Microsoft/vscode/issues/72447 + // Workaround for https://github.com/microsoft/vscode/issues/72447 // It turns out that the menu gets GC'ed if not referenced anymore // As such we drag it into this scope so that it is not being GC'ed if (menu) { diff --git a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts similarity index 84% rename from src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts rename to src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts index da008e59930..4b8917c3426 100644 --- a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcRenderer, Event } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IContextMenuItem, ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions, IContextMenuEvent } from 'vs/base/parts/contextmenu/common/contextmenu'; let contextMenuIdPool = 0; -export function popup(items: IContextMenuItem[], options?: IPopupOptions): void { +export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide?: () => void): void { const processedItems: IContextMenuItem[] = []; const contextMenuId = contextMenuIdPool++; const onClickChannel = `vscode:onContextMenu${contextMenuId}`; - const onClickChannelHandler = (_event: Event, itemId: number, context: IContextMenuEvent) => { + const onClickChannelHandler = (event: unknown, itemId: number, context: IContextMenuEvent) => { const item = processedItems[itemId]; if (item.click) { item.click(context); @@ -21,15 +21,15 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void }; ipcRenderer.once(onClickChannel, onClickChannelHandler); - ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, (_event: Event, closedContextMenuId: number) => { + ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, (event: unknown, closedContextMenuId: number) => { if (closedContextMenuId !== contextMenuId) { return; } ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); - if (options?.onHide) { - options.onHide(); + if (onHide) { + onHide(); } }); diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/common/ipc.electron.ts similarity index 83% rename from src/vs/base/parts/ipc/node/ipc.electron.ts rename to src/vs/base/parts/ipc/common/ipc.electron.ts index 09c97ba47b1..516351e1509 100644 --- a/src/vs/base/parts/ipc/node/ipc.electron.ts +++ b/src/vs/base/parts/ipc/common/ipc.electron.ts @@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event'; import { VSBuffer } from 'vs/base/common/buffer'; export interface Sender { - send(channel: string, msg: Buffer | null): void; + send(channel: string, msg: unknown): void; } export class Protocol implements IMessagePassingProtocol { @@ -17,13 +17,13 @@ export class Protocol implements IMessagePassingProtocol { send(message: VSBuffer): void { try { - this.sender.send('ipc:message', (message.buffer)); + this.sender.send('vscode:message', message.buffer); } catch (e) { // systems are going down } } dispose(): void { - this.sender.send('ipc:disconnect', null); + this.sender.send('vscode:disconnect', null); } -} \ No newline at end of file +} diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 132654b320f..bc8e97c269c 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol, IPCClient, IIPCLogger } from 'vs/base/parts/ipc/common/ipc'; import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import * as platform from 'vs/base/common/platform'; @@ -16,6 +16,7 @@ export interface ISocket extends IDisposable { onEnd(listener: () => void): IDisposable; write(buffer: VSBuffer): void; end(): void; + drain(): Promise; } let emptyBuffer: VSBuffer | null = null; @@ -277,6 +278,11 @@ class ProtocolWriter { this._isDisposed = true; } + public drain(): Promise { + this.flush(); + return this._socket.drain(); + } + public flush(): void { // flush this._writeNow(); @@ -372,6 +378,10 @@ export class Protocol extends Disposable implements IMessagePassingProtocol { this._register(this._socket.onClose(() => this._onClose.fire())); } + drain(): Promise { + return this._socketWriter.drain(); + } + getSocket(): ISocket { return this._socket; } @@ -393,8 +403,8 @@ export class Client extends IPCClient { get onClose(): Event { return this.protocol.onClose; } - constructor(private protocol: Protocol | PersistentProtocol, id: TContext) { - super(protocol, id); + constructor(private protocol: Protocol | PersistentProtocol, id: TContext, ipcLogger: IIPCLogger | null = null) { + super(protocol, id, ipcLogger); } dispose(): void { @@ -523,6 +533,53 @@ class Queue { } } +class LoadEstimator { + + private static _HISTORY_LENGTH = 10; + private static _INSTANCE: LoadEstimator | null = null; + public static getInstance(): LoadEstimator { + if (!LoadEstimator._INSTANCE) { + LoadEstimator._INSTANCE = new LoadEstimator(); + } + return LoadEstimator._INSTANCE; + } + + private lastRuns: number[]; + + constructor() { + this.lastRuns = []; + const now = Date.now(); + for (let i = 0; i < LoadEstimator._HISTORY_LENGTH; i++) { + this.lastRuns[i] = now - 1000 * i; + } + setInterval(() => { + for (let i = LoadEstimator._HISTORY_LENGTH; i >= 1; i--) { + this.lastRuns[i] = this.lastRuns[i - 1]; + } + this.lastRuns[0] = Date.now(); + }, 1000); + } + + /** + * returns an estimative number, from 0 (low load) to 1 (high load) + */ + public load(): number { + const now = Date.now(); + const historyLimit = (1 + LoadEstimator._HISTORY_LENGTH) * 1000; + let score = 0; + for (let i = 0; i < LoadEstimator._HISTORY_LENGTH; i++) { + if (now - this.lastRuns[i] <= historyLimit) { + score++; + } + } + return 1 - score / LoadEstimator._HISTORY_LENGTH; + } + + public hasHighLoad(): boolean { + return this.load() >= 0.5; + } +} + /** * Same as Protocol, but will actually track messages and acks. * Moreover, it will ensure no messages are lost if there are no event listeners. @@ -549,6 +606,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { private _socketReader: ProtocolReader; private _socketDisposables: IDisposable[]; + private readonly _loadEstimator = LoadEstimator.getInstance(); + private readonly _onControlMessage = new BufferedEmitter(); readonly onControlMessage: Event = this._onControlMessage.event; @@ -619,6 +678,10 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._socketDisposables = dispose(this._socketDisposables); } + drain(): Promise { + return this._socketWriter.drain(); + } + sendDisconnect(): void { const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer()); this._socketWriter.write(msg); @@ -656,15 +719,19 @@ export class PersistentProtocol implements IMessagePassingProtocol { const timeSinceLastIncomingMsg = Date.now() - this._socketReader.lastReadTime; if (timeSinceLastIncomingMsg >= ProtocolConstants.KeepAliveTimeoutTime) { - // Trash the socket - this._onSocketTimeout.fire(undefined); - return; + // It's been a long time since we received a server message + // But this might be caused by the event loop being busy and failing to read messages + if (!this._loadEstimator.hasHighLoad()) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } } this._incomingKeepAliveTimeout = setTimeout(() => { this._incomingKeepAliveTimeout = null; this._recvKeepAliveCheck(); - }, ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg + 5); + }, Math.max(ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg, 0) + 5); } public getSocket(): ISocket { @@ -807,15 +874,19 @@ export class PersistentProtocol implements IMessagePassingProtocol { const oldestUnacknowledgedMsg = this._outgoingUnackMsg.peek()!; const timeSinceOldestUnacknowledgedMsg = Date.now() - oldestUnacknowledgedMsg.writtenTime; if (timeSinceOldestUnacknowledgedMsg >= ProtocolConstants.AcknowledgeTimeoutTime) { - // Trash the socket - this._onSocketTimeout.fire(undefined); - return; + // It's been a long time since our sent message was acknowledged + // But this might be caused by the event loop being busy and failing to read messages + if (!this._loadEstimator.hasHighLoad()) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } } this._outgoingAckTimeout = setTimeout(() => { this._outgoingAckTimeout = null; this._recvAckCheck(); - }, ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg + 5); + }, Math.max(ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg, 0) + 5); } private _sendAck(): void { diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 662a29a9d86..a45ee66bb9a 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -3,12 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter, Relay } from 'vs/base/common/event'; -import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter, Relay, EventMultiplexer } from 'vs/base/common/event'; +import { IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { VSBuffer } from 'vs/base/common/buffer'; +import { getRandomElement } from 'vs/base/common/arrays'; +import { isFunction, isUndefinedOrNull } from 'vs/base/common/types'; +import { revive } from 'vs/base/common/marshalling'; +import * as strings from 'vs/base/common/strings'; /** * An `IChannel` is an abstraction over a collection of commands. @@ -38,6 +42,19 @@ export const enum RequestType { EventDispose = 103 } +function requestTypeToStr(type: RequestType): string { + switch (type) { + case RequestType.Promise: + return 'req'; + case RequestType.PromiseCancel: + return 'cancel'; + case RequestType.EventListen: + return 'subscribe'; + case RequestType.EventDispose: + return 'unsubscribe'; + } +} + type IRawPromiseRequest = { type: RequestType.Promise; id: number; channelName: string; name: string; arg: any; }; type IRawPromiseCancelRequest = { type: RequestType.PromiseCancel, id: number }; type IRawEventListenRequest = { type: RequestType.EventListen; id: number; channelName: string; name: string; arg: any; }; @@ -52,6 +69,20 @@ export const enum ResponseType { EventFire = 204 } +function responseTypeToStr(type: ResponseType): string { + switch (type) { + case ResponseType.Initialize: + return `init`; + case ResponseType.PromiseSuccess: + return `reply:`; + case ResponseType.PromiseError: + case ResponseType.PromiseErrorObj: + return `replyErr:`; + case ResponseType.EventFire: + return `event:`; + } +} + type IRawInitializeResponse = { type: ResponseType.Initialize }; type IRawPromiseSuccessResponse = { type: ResponseType.PromiseSuccess; id: number; data: any }; type IRawPromiseErrorResponse = { type: ResponseType.PromiseError; id: number; data: { message: string, name: string, stack: string[] | undefined } }; @@ -66,6 +97,10 @@ interface IHandler { export interface IMessagePassingProtocol { send(buffer: VSBuffer): void; onMessage: Event; + /** + * Wait for the write buffer (if applicable) to become empty. + */ + drain?(): Promise; } enum State { @@ -95,7 +130,8 @@ export interface Client { export interface IConnectionHub { readonly connections: Connection[]; - readonly onDidChangeConnections: Event>; + readonly onDidAddConnection: Event>; + readonly onDidRemoveConnection: Event>; } /** @@ -116,7 +152,7 @@ export interface IClientRouter { * order to pick the right one. */ export interface IRoutingChannelClient { - getChannel(channelName: string, router: IClientRouter): T; + getChannel(channelName: string, router?: IClientRouter): T; } interface IReader { @@ -259,7 +295,7 @@ export class ChannelServer implements IChannelServer(); - constructor(private protocol: IMessagePassingProtocol, private ctx: TContext, private timeoutDelay: number = 1000) { + constructor(private protocol: IMessagePassingProtocol, private ctx: TContext, private logger: IIPCLogger | null = null, private timeoutDelay: number = 1000) { this.protocolListener = this.protocol.onMessage(msg => this.onRawMessage(msg)); this.sendResponse({ type: ResponseType.Initialize }); } @@ -273,29 +309,41 @@ export class ChannelServer implements IChannelServer implements IChannelServer implements IChannelServer(); private lastRequestId: number = 0; private protocolListener: IDisposable | null; + private logger: IIPCLogger | null; private readonly _onDidInitialize = new Emitter(); readonly onDidInitialize = this._onDidInitialize.event; - constructor(private protocol: IMessagePassingProtocol) { + constructor(private protocol: IMessagePassingProtocol, logger: IIPCLogger | null = null) { this.protocolListener = this.protocol.onMessage(msg => this.onBuffer(msg)); + this.logger = logger; } getChannel(channelName: string): T { @@ -477,10 +549,7 @@ export class ChannelClient implements IChannelClient, IDisposable { return e(errors.canceled()); } - let uninitializedPromise: CancelablePromise | null = createCancelablePromise(_ => this.whenInitialized()); - uninitializedPromise.then(() => { - uninitializedPromise = null; - + const doRequest = () => { const handler: IHandler = response => { switch (response.type) { case ResponseType.PromiseSuccess: @@ -505,7 +574,18 @@ export class ChannelClient implements IChannelClient, IDisposable { this.handlers.set(id, handler); this.sendRequest(request); - }); + }; + + let uninitializedPromise: CancelablePromise | null = null; + if (this.state === State.Idle) { + doRequest(); + } else { + uninitializedPromise = createCancelablePromise(_ => this.whenInitialized()); + uninitializedPromise.then(() => { + uninitializedPromise = null; + doRequest(); + }); + } const cancel = () => { if (uninitializedPromise) { @@ -523,7 +603,7 @@ export class ChannelClient implements IChannelClient, IDisposable { this.activeRequests.add(disposable); }); - return result.finally(() => this.activeRequests.delete(disposable)); + return result.finally(() => { this.activeRequests.delete(disposable); }); } private requestEvent(channelName: string, name: string, arg?: any): Event { @@ -562,27 +642,39 @@ export class ChannelClient implements IChannelClient, IDisposable { private sendRequest(request: IRawRequest): void { switch (request.type) { case RequestType.Promise: - case RequestType.EventListen: - return this.send([request.type, request.id, request.channelName, request.name], request.arg); + case RequestType.EventListen: { + const msgLength = this.send([request.type, request.id, request.channelName, request.name], request.arg); + if (this.logger) { + this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); + } + return; + } case RequestType.PromiseCancel: - case RequestType.EventDispose: - return this.send([request.type, request.id]); + case RequestType.EventDispose: { + const msgLength = this.send([request.type, request.id]); + if (this.logger) { + this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); + } + return; + } } } - private send(header: any, body: any = undefined): void { + private send(header: any, body: any = undefined): number { const writer = new BufferWriter(); serialize(writer, header); serialize(writer, body); - this.sendBuffer(writer.buffer); + return this.sendBuffer(writer.buffer); } - private sendBuffer(message: VSBuffer): void { + private sendBuffer(message: VSBuffer): number { try { this.protocol.send(message); + return message.byteLength; } catch (err) { // noop + return 0; } } @@ -594,12 +686,18 @@ export class ChannelClient implements IChannelClient, IDisposable { switch (type) { case ResponseType.Initialize: + if (this.logger) { + this.logger.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); + } return this.onResponse({ type: header[0] }); case ResponseType.PromiseSuccess: case ResponseType.PromiseError: case ResponseType.EventFire: case ResponseType.PromiseErrorObj: + if (this.logger) { + this.logger.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); + } return this.onResponse({ type: header[0], id: header[1], data: body }); } } @@ -659,8 +757,11 @@ export class IPCServer implements IChannelServer, I private channels = new Map>(); private _connections = new Set>(); - private readonly _onDidChangeConnections = new Emitter>(); - readonly onDidChangeConnections: Event> = this._onDidChangeConnections.event; + private readonly _onDidAddConnection = new Emitter>(); + readonly onDidAddConnection: Event> = this._onDidAddConnection.event; + + private readonly _onDidRemoveConnection = new Emitter>(); + readonly onDidRemoveConnection: Event> = this._onDidRemoveConnection.event; get connections(): Connection[] { const result: Connection[] = []; @@ -683,30 +784,59 @@ export class IPCServer implements IChannelServer, I const connection: Connection = { channelServer, channelClient, ctx }; this._connections.add(connection); - this._onDidChangeConnections.fire(connection); + this._onDidAddConnection.fire(connection); onDidClientDisconnect(() => { channelServer.dispose(); channelClient.dispose(); this._connections.delete(connection); + this._onDidRemoveConnection.fire(connection); }); }); }); } - getChannel(channelName: string, router: IClientRouter): T { + /** + * Get a channel from a remote client. When passed a router, + * one can specify which client it wants to call and listen to/from. + * Otherwise, when calling without a router, a random client will + * be selected and when listening without a router, every client + * will be listened to. + */ + getChannel(channelName: string, router: IClientRouter): T; + getChannel(channelName: string, clientFilter: (client: Client) => boolean): T; + getChannel(channelName: string, routerOrClientFilter: IClientRouter | ((client: Client) => boolean)): T { const that = this; return { - call(command: string, arg?: any, cancellationToken?: CancellationToken) { - const channelPromise = router.routeCall(that, command, arg) + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { + let connectionPromise: Promise>; + + if (isFunction(routerOrClientFilter)) { + // when no router is provided, we go random client picking + let connection = getRandomElement(that.connections.filter(routerOrClientFilter)); + + connectionPromise = connection + // if we found a client, let's call on it + ? Promise.resolve(connection) + // else, let's wait for a client to come along + : Event.toPromise(Event.filter(that.onDidAddConnection, routerOrClientFilter)); + } else { + connectionPromise = routerOrClientFilter.routeCall(that, command, arg); + } + + const channelPromise = connectionPromise .then(connection => (connection as Connection).channelClient.getChannel(channelName)); return getDelayedChannel(channelPromise) .call(command, arg, cancellationToken); }, - listen(event: string, arg: any) { - const channelPromise = router.routeEvent(that, event, arg) + listen(event: string, arg: any): Event { + if (isFunction(routerOrClientFilter)) { + return that.getMulticastEvent(channelName, routerOrClientFilter, event, arg); + } + + const channelPromise = routerOrClientFilter.routeEvent(that, event, arg) .then(connection => (connection as Connection).channelClient.getChannel(channelName)); return getDelayedChannel(channelPromise) @@ -715,6 +845,58 @@ export class IPCServer implements IChannelServer, I } as T; } + private getMulticastEvent(channelName: string, clientFilter: (client: Client) => boolean, eventName: string, arg: any): Event { + const that = this; + let disposables = new DisposableStore(); + + // Create an emitter which hooks up to all clients + // as soon as first listener is added. It also + // disconnects from all clients as soon as the last listener + // is removed. + const emitter = new Emitter({ + onFirstListenerAdd: () => { + disposables = new DisposableStore(); + + // The event multiplexer is useful since the active + // client list is dynamic. We need to hook up and disconnection + // to/from clients as they come and go. + const eventMultiplexer = new EventMultiplexer(); + const map = new Map, IDisposable>(); + + const onDidAddConnection = (connection: Connection) => { + const channel = connection.channelClient.getChannel(channelName); + const event = channel.listen(eventName, arg); + const disposable = eventMultiplexer.add(event); + + map.set(connection, disposable); + }; + + const onDidRemoveConnection = (connection: Connection) => { + const disposable = map.get(connection); + + if (!disposable) { + return; + } + + disposable.dispose(); + map.delete(connection); + }; + + that.connections.filter(clientFilter).forEach(onDidAddConnection); + Event.filter(that.onDidAddConnection, clientFilter)(onDidAddConnection, undefined, disposables); + that.onDidRemoveConnection(onDidRemoveConnection, undefined, disposables); + eventMultiplexer.event(emitter.fire, emitter, disposables); + + disposables.add(eventMultiplexer); + }, + onLastListenerRemove: () => { + disposables.dispose(); + } + }); + + return emitter.event; + } + registerChannel(channelName: string, channel: IServerChannel): void { this.channels.set(channelName, channel); @@ -726,7 +908,8 @@ export class IPCServer implements IChannelServer, I dispose(): void { this.channels.clear(); this._connections.clear(); - this._onDidChangeConnections.dispose(); + this._onDidAddConnection.dispose(); + this._onDidRemoveConnection.dispose(); } } @@ -742,13 +925,13 @@ export class IPCClient implements IChannelClient, IChannelSer private channelClient: ChannelClient; private channelServer: ChannelServer; - constructor(protocol: IMessagePassingProtocol, ctx: TContext) { + constructor(protocol: IMessagePassingProtocol, ctx: TContext, ipcLogger: IIPCLogger | null = null) { const writer = new BufferWriter(); serialize(writer, ctx); protocol.send(writer.buffer); - this.channelClient = new ChannelClient(protocol); - this.channelServer = new ChannelServer(protocol, ctx); + this.channelClient = new ChannelClient(protocol, ipcLogger); + this.channelServer = new ChannelServer(protocol, ctx, ipcLogger); } getChannel(channelName: string): T { @@ -827,7 +1010,207 @@ export class StaticRouter implements IClientRouter } } - await Event.toPromise(hub.onDidChangeConnections); + await Event.toPromise(hub.onDidAddConnection); return await this.route(hub); } } + + +//#region createChannelReceiver / createChannelSender + +/** + * Use both `createChannelReceiver` and `createChannelSender` + * for automated process <=> process communication over methods + * and events. You do not need to spell out each method on both + * sides, a proxy will take care of this. + * + * Rules: + * - if marshalling is enabled, only `URI` and `RegExp` is converted + * automatically for you + * - events must follow the naming convention `onUppercase` + * - `CancellationToken` is currently not supported + * - if a context is provided, you can use `AddFirstParameterToFunctions` + * utility to signal this in the receiving side type + */ + +export interface IBaseChannelOptions { + + /** + * Disables automatic marshalling of `URI`. + * If marshalling is disabled, `UriComponents` + * must be used instead. + */ + disableMarshalling?: boolean; +} + +export interface IChannelReceiverOptions extends IBaseChannelOptions { } + +export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel { + const handler = service as { [key: string]: unknown }; + const disableMarshalling = options && options.disableMarshalling; + + // Buffer any event that should be supported by + // iterating over all property keys and finding them + const mapEventNameToEvent = new Map>(); + for (const key in handler) { + if (propertyIsEvent(key)) { + mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event, true)); + } + } + + return new class implements IServerChannel { + + listen(_: unknown, event: string): Event { + const eventImpl = mapEventNameToEvent.get(event); + if (eventImpl) { + return eventImpl as Event; + } + + throw new Error(`Event not found: ${event}`); + } + + call(_: unknown, command: string, args?: any[]): Promise { + const target = handler[command]; + if (typeof target === 'function') { + + // Revive unless marshalling disabled + if (!disableMarshalling && Array.isArray(args)) { + for (let i = 0; i < args.length; i++) { + args[i] = revive(args[i]); + } + } + + return target.apply(handler, args); + } + + throw new Error(`Method not found: ${command}`); + } + }; +} + +export interface IChannelSenderOptions extends IBaseChannelOptions { + + /** + * If provided, will add the value of `context` + * to each method call to the target. + */ + context?: unknown; + + /** + * If provided, will not proxy any of the properties + * that are part of the Map but rather return that value. + */ + properties?: Map; +} + +export function createChannelSender(channel: IChannel, options?: IChannelSenderOptions): T { + const disableMarshalling = options && options.disableMarshalling; + + return new Proxy({}, { + get(_target: T, propKey: PropertyKey) { + if (typeof propKey === 'string') { + + // Check for predefined values + if (options?.properties?.has(propKey)) { + return options.properties.get(propKey); + } + + // Event + if (propertyIsEvent(propKey)) { + return channel.listen(propKey); + } + + // Function + return async function (...args: any[]) { + + // Add context if any + let methodArgs: any[]; + if (options && !isUndefinedOrNull(options.context)) { + methodArgs = [options.context, ...args]; + } else { + methodArgs = args; + } + + const result = await channel.call(propKey, methodArgs); + + // Revive unless marshalling disabled + if (!disableMarshalling) { + return revive(result); + } + + return result; + }; + } + + throw new Error(`Property not found: ${String(propKey)}`); + } + }) as T; +} + +function propertyIsEvent(name: string): boolean { + // Assume a property is an event if it has a form of "onSomething" + return name[0] === 'o' && name[1] === 'n' && strings.isUpperAsciiLetter(name.charCodeAt(2)); +} + +//#endregion + + +const colorTables = [ + ['#2977B1', '#FC802D', '#34A13A', '#D3282F', '#9366BA'], + ['#8B564C', '#E177C0', '#7F7F7F', '#BBBE3D', '#2EBECD'] +]; + +function prettyWithoutArrays(data: any): any { + if (Array.isArray(data)) { + return data; + } + if (data && typeof data === 'object' && typeof data.toString === 'function') { + let result = data.toString(); + if (result !== '[object Object]') { + return result; + } + } + return data; +} + +function pretty(data: any): any { + if (Array.isArray(data)) { + return data.map(prettyWithoutArrays); + } + return prettyWithoutArrays(data); +} + +export function logWithColors(direction: string, totalLength: number, msgLength: number, req: number, initiator: RequestInitiator, str: string, data: any): void { + data = pretty(data); + + const colorTable = colorTables[initiator]; + const color = colorTable[req % colorTable.length]; + let args = [`%c[${direction}]%c[${String(totalLength).padStart(7, ' ')}]%c[len: ${String(msgLength).padStart(5, ' ')}]%c${String(req).padStart(5, ' ')} - ${str}`, 'color: darkgreen', 'color: grey', 'color: grey', `color: ${color}`]; + if (/\($/.test(str)) { + args = args.concat(data); + args.push(')'); + } else { + args.push(data); + } + console.log.apply(console, args as [string, ...string[]]); +} + +export class IPCLogger implements IIPCLogger { + private _totalIncoming = 0; + private _totalOutgoing = 0; + + constructor( + private readonly _outgoingPrefix: string, + private readonly _incomingPrefix: string, + ) { } + + public logOutgoing(msgLength: number, requestId: number, initiator: RequestInitiator, str: string, data?: any): void { + this._totalOutgoing += msgLength; + logWithColors(this._outgoingPrefix, this._totalOutgoing, msgLength, requestId, initiator, str, data); + } + + public logIncoming(msgLength: number, requestId: number, initiator: RequestInitiator, str: string, data?: any): void { + this._totalIncoming += msgLength; + logWithColors(this._incomingPrefix, this._totalIncoming, msgLength, requestId, initiator, str, data); + } +} diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index bac2223ede6..b84a1d653dc 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -5,7 +5,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; +import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; import { ipcMain, WebContents } from 'electron'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -26,7 +26,7 @@ export class Server extends IPCServer { private static readonly Clients = new Map(); private static getOnDidClientConnect(): Event { - const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + const onHello = Event.fromNodeEventEmitter(ipcMain, 'vscode:hello', ({ sender }) => sender); return Event.map(onHello, webContents => { const id = webContents.id; @@ -39,8 +39,8 @@ export class Server extends IPCServer { const onDidClientReconnect = new Emitter(); Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire())); - const onMessage = createScopedOnMessageEvent(id, 'ipc:message') as Event; - const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event); + const onMessage = createScopedOnMessageEvent(id, 'vscode:message') as Event; + const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'vscode:disconnect')), onDidClientReconnect.event); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts similarity index 80% rename from src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts rename to src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts index 2690300b83a..19ca487c890 100644 --- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts +++ b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts @@ -5,18 +5,18 @@ import { Event } from 'vs/base/common/event'; import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; -import { ipcRenderer } from 'electron'; +import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; import { IDisposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; export class Client extends IPCClient implements IDisposable { private protocol: Protocol; private static createProtocol(): Protocol { - const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: Buffer) => VSBuffer.wrap(message)); - ipcRenderer.send('ipc:hello'); + const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'vscode:message', (_, message) => VSBuffer.wrap(message)); + ipcRenderer.send('vscode:hello'); return new Protocol(ipcRenderer, onMessage); } @@ -29,4 +29,4 @@ export class Client extends IPCClient implements IDisposable { dispose(): void { this.protocol.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index a5632fdd069..6733816b83c 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -6,7 +6,7 @@ import { ChildProcess, fork, ForkOptions } from 'child_process'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { Delayer, createCancelablePromise } from 'vs/base/common/async'; -import { deepClone, assign } from 'vs/base/common/objects'; +import { deepClone } from 'vs/base/common/objects'; import { Emitter, Event } from 'vs/base/common/event'; import { createQueuedSender } from 'vs/base/node/processes'; import { IChannel, ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient } from 'vs/base/parts/ipc/common/ipc'; @@ -14,6 +14,7 @@ import { isRemoteConsoleLog, log } from 'vs/base/common/console'; import { CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { VSBuffer } from 'vs/base/common/buffer'; +import { isMacintosh } from 'vs/base/common/platform'; /** * This implementation doesn't perform well since it uses base64 encoding for buffers. @@ -70,7 +71,7 @@ export interface IIPCOptions { debugBrk?: number; /** - * See https://github.com/Microsoft/vscode/issues/27665 + * See https://github.com/microsoft/vscode/issues/27665 * Allows to pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`. * e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host * results in the forked process inheriting `--inspect-brk=xxx`. @@ -179,10 +180,10 @@ export class Client implements IChannelClient, IDisposable { const args = this.options && this.options.args ? this.options.args : []; const forkOpts: ForkOptions = Object.create(null); - forkOpts.env = assign(deepClone(process.env), { 'VSCODE_PARENT_PID': String(process.pid) }); + forkOpts.env = { ...deepClone(process.env), 'VSCODE_PARENT_PID': String(process.pid) }; if (this.options && this.options.env) { - forkOpts.env = assign(forkOpts.env, this.options.env); + forkOpts.env = { ...forkOpts.env, ...this.options.env }; } if (this.options && this.options.freshExecArgv) { @@ -197,6 +198,12 @@ export class Client implements IChannelClient, IDisposable { forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk]; } + if (isMacintosh && forkOpts.env) { + // Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes + // See https://github.com/microsoft/vscode/issues/105848 + delete forkOpts.env['DYLD_LIBRARY_PATH']; + } + this.child = fork(this.modulePath, args, forkOpts); const onMessageEmitter = new Emitter(); diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index ec80ba3f1c3..39ef87b8464 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { createHash } from 'crypto'; import { Socket, Server as NetServer, createConnection, createServer } from 'net'; import { Event, Emitter } from 'vs/base/common/event'; import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc'; @@ -12,6 +13,8 @@ import { generateUuid } from 'vs/base/common/uuid'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Platform, platform } from 'vs/base/common/platform'; export class NodeSocket implements ISocket { public readonly socket: Socket; @@ -57,12 +60,47 @@ export class NodeSocket implements ISocket { // > https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback // > However, the false return value is only advisory and the writable stream will unconditionally // > accept and buffer chunk even if it has not been allowed to drain. - this.socket.write(buffer.buffer); + try { + this.socket.write(buffer.buffer); + } catch (err) { + if (err.code === 'EPIPE') { + // An EPIPE exception at the wrong time can lead to a renderer process crash + // so ignore the error since the socket will fire the close event soon anyways: + // > https://nodejs.org/api/errors.html#errors_common_system_errors + // > EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no + // > process to read the data. Commonly encountered at the net and http layers, + // > indicative that the remote side of the stream being written to has been closed. + return; + } + onUnexpectedError(err); + } } public end(): void { this.socket.end(); } + + public drain(): Promise { + return new Promise((resolve, reject) => { + if (this.socket.bufferSize === 0) { + resolve(); + return; + } + const finished = () => { + this.socket.off('close', finished); + this.socket.off('end', finished); + this.socket.off('error', finished); + this.socket.off('timeout', finished); + this.socket.off('drain', finished); + resolve(); + }; + this.socket.on('close', finished); + this.socket.on('end', finished); + this.socket.on('error', finished); + this.socket.on('timeout', finished); + this.socket.on('drain', finished); + }); + } } const enum Constants { @@ -229,6 +267,10 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { } } } + + public drain(): Promise { + return this.socket.drain(); + } } function unmask(buffer: VSBuffer, mask: number): void { @@ -256,13 +298,67 @@ function unmask(buffer: VSBuffer, mask: number): void { } } -export function generateRandomPipeName(): string { +// Read this before there's any chance it is overwritten +// Related to https://github.com/microsoft/vscode/issues/30624 +export const XDG_RUNTIME_DIR = process.env['XDG_RUNTIME_DIR']; + +const safeIpcPathLengths: { [platform: number]: number } = { + [Platform.Linux]: 107, + [Platform.Mac]: 103 +}; + +export function createRandomIPCHandle(): string { const randomSuffix = generateUuid(); + + // Windows: use named pipe if (process.platform === 'win32') { return `\\\\.\\pipe\\vscode-ipc-${randomSuffix}-sock`; + } + + // Mac/Unix: use socket file and prefer + // XDG_RUNTIME_DIR over tmpDir + let result: string; + if (XDG_RUNTIME_DIR) { + result = join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`); } else { - // Mac/Unix: use socket file - return join(tmpdir(), `vscode-ipc-${randomSuffix}.sock`); + result = join(tmpdir(), `vscode-ipc-${randomSuffix}.sock`); + } + + // Validate length + validateIPCHandleLength(result); + + return result; +} + +export function createStaticIPCHandle(directoryPath: string, type: string, version: string): string { + const scope = createHash('md5').update(directoryPath).digest('hex'); + + // Windows: use named pipe + if (process.platform === 'win32') { + return `\\\\.\\pipe\\${scope}-${version}-${type}-sock`; + } + + // Mac/Unix: use socket file and prefer + // XDG_RUNTIME_DIR over user data path + // unless portable + let result: string; + if (XDG_RUNTIME_DIR && !process.env['VSCODE_PORTABLE']) { + result = join(XDG_RUNTIME_DIR, `vscode-${scope.substr(0, 8)}-${version}-${type}.sock`); + } else { + result = join(directoryPath, `${version}-${type}.sock`); + } + + // Validate length + validateIPCHandleLength(result); + + return result; +} + +function validateIPCHandleLength(handle: string): void { + const limit = safeIpcPathLengths[platform]; + if (typeof limit === 'number' && handle.length >= limit) { + // https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections + console.warn(`WARNING: IPC handle "${handle}" is longer than ${limit} chars, try a shorter --user-data-dir`); } } diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts deleted file mode 100644 index 631e5139133..00000000000 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ /dev/null @@ -1,133 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { revive } from 'vs/base/common/marshalling'; -import { isUndefinedOrNull } from 'vs/base/common/types'; -import { isUpperAsciiLetter } from 'vs/base/common/strings'; - -/** - * Use both `createChannelReceiver` and `createChannelSender` - * for automated process <=> process communication over methods - * and events. You do not need to spell out each method on both - * sides, a proxy will take care of this. - * - * Rules: - * - if marshalling is enabled, only `URI` and `RegExp` is converted - * automatically for you - * - events must follow the naming convention `onUppercase` - * - `CancellationToken` is currently not supported - * - if a context is provided, you can use `AddFirstParameterToFunctions` - * utility to signal this in the receiving side type - */ - -export interface IBaseChannelOptions { - - /** - * Disables automatic marshalling of `URI`. - * If marshalling is disabled, `UriComponents` - * must be used instead. - */ - disableMarshalling?: boolean; -} - -export interface IChannelReceiverOptions extends IBaseChannelOptions { } - -export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel { - const handler = service as { [key: string]: unknown }; - const disableMarshalling = options && options.disableMarshalling; - - // Buffer any event that should be supported by - // iterating over all property keys and finding them - const mapEventNameToEvent = new Map>(); - for (const key in handler) { - if (propertyIsEvent(key)) { - mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event, true)); - } - } - - return new class implements IServerChannel { - - listen(_: unknown, event: string): Event { - const eventImpl = mapEventNameToEvent.get(event); - if (eventImpl) { - return eventImpl as Event; - } - - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, args?: any[]): Promise { - const target = handler[command]; - if (typeof target === 'function') { - - // Revive unless marshalling disabled - if (!disableMarshalling && Array.isArray(args)) { - for (let i = 0; i < args.length; i++) { - args[i] = revive(args[i]); - } - } - - return target.apply(handler, args); - } - - throw new Error(`Method not found: ${command}`); - } - }; -} - -export interface IChannelSenderOptions extends IBaseChannelOptions { - - /** - * If provided, will add the value of `context` - * to each method call to the target. - */ - context?: unknown; -} - -export function createChannelSender(channel: IChannel, options?: IChannelSenderOptions): T { - const disableMarshalling = options && options.disableMarshalling; - - return new Proxy({}, { - get(_target: T, propKey: PropertyKey) { - if (typeof propKey === 'string') { - - // Event - if (propertyIsEvent(propKey)) { - return channel.listen(propKey); - } - - // Function - return async function (...args: any[]) { - - // Add context if any - let methodArgs: any[]; - if (options && !isUndefinedOrNull(options.context)) { - methodArgs = [options.context, ...args]; - } else { - methodArgs = args; - } - - const result = await channel.call(propKey, methodArgs); - - // Revive unless marshalling disabled - if (!disableMarshalling) { - return revive(result); - } - - return result; - }; - } - - throw new Error(`Property not found: ${String(propKey)}`); - } - }) as T; -} - -function propertyIsEvent(name: string): boolean { - // Assume a property is an event if it has a form of "onSomething" - return name[0] === 'o' && name[1] === 'n' && isUpperAsciiLetter(name.charCodeAt(2)); -} diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/common/ipc.test.ts similarity index 78% rename from src/vs/base/parts/ipc/test/node/ipc.test.ts rename to src/vs/base/parts/ipc/test/common/ipc.test.ts index 589acae53d4..8ed0f3797f1 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/common/ipc.test.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; @@ -103,7 +102,7 @@ interface ITestService { error(message: string): Promise; neverComplete(): Promise; neverCompleteCT(cancellationToken: CancellationToken): Promise; - buffersLength(buffers: Buffer[]): Promise; + buffersLength(buffers: VSBuffer[]): Promise; marshall(uri: URI): Promise; context(): Promise; @@ -135,8 +134,8 @@ class TestService implements ITestService { return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled()))); } - buffersLength(buffers: Buffer[]): Promise { - return Promise.resolve(buffers.reduce((r, b) => r + b.length, 0)); + buffersLength(buffers: VSBuffer[]): Promise { + return Promise.resolve(buffers.reduce((r, b) => r + b.buffer.length, 0)); } ping(msg: string): void { @@ -199,7 +198,7 @@ class TestChannelClient implements ITestService { return this.channel.call('neverCompleteCT', undefined, cancellationToken); } - buffersLength(buffers: Buffer[]): Promise { + buffersLength(buffers: VSBuffer[]): Promise { return this.channel.call('buffersLength', buffers); } @@ -317,7 +316,7 @@ suite('Base IPC', function () { }); test('buffers in arrays', async function () { - const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]); + const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]); return assert.equal(r, 5); }); }); @@ -383,7 +382,7 @@ suite('Base IPC', function () { }); test('buffers in arrays', async function () { - const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]); + const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]); return assert.equal(r, 5); }); }); @@ -415,4 +414,80 @@ suite('Base IPC', function () { return assert.equal(r, 'Super Context'); }); }); + + suite('one to many', function () { + test('all clients get pinged', async function () { + const service = new TestService(); + const channel = new TestChannel(service); + const server = new TestIPCServer(); + server.registerChannel('channel', channel); + + let client1GotPinged = false; + const client1 = server.createConnection('client1'); + const ipcService1 = new TestChannelClient(client1.getChannel('channel')); + ipcService1.onPong(() => client1GotPinged = true); + + let client2GotPinged = false; + const client2 = server.createConnection('client2'); + const ipcService2 = new TestChannelClient(client2.getChannel('channel')); + ipcService2.onPong(() => client2GotPinged = true); + + await timeout(1); + service.ping('hello'); + + await timeout(1); + assert(client1GotPinged, 'client 1 got pinged'); + assert(client2GotPinged, 'client 2 got pinged'); + + client1.dispose(); + client2.dispose(); + server.dispose(); + }); + + test('server gets pings from all clients (broadcast channel)', async function () { + const server = new TestIPCServer(); + + const client1 = server.createConnection('client1'); + const clientService1 = new TestService(); + const clientChannel1 = new TestChannel(clientService1); + client1.registerChannel('channel', clientChannel1); + + const pings: string[] = []; + const channel = server.getChannel('channel', () => true); + const service = new TestChannelClient(channel); + service.onPong(msg => pings.push(msg)); + + await timeout(1); + clientService1.ping('hello 1'); + + await timeout(1); + assert.deepEqual(pings, ['hello 1']); + + const client2 = server.createConnection('client2'); + const clientService2 = new TestService(); + const clientChannel2 = new TestChannel(clientService2); + client2.registerChannel('channel', clientChannel2); + + await timeout(1); + clientService2.ping('hello 2'); + + await timeout(1); + assert.deepEqual(pings, ['hello 1', 'hello 2']); + + client1.dispose(); + clientService1.ping('hello 1'); + + await timeout(1); + assert.deepEqual(pings, ['hello 1', 'hello 2']); + + await timeout(1); + clientService2.ping('hello again 2'); + + await timeout(1); + assert.deepEqual(pings, ['hello 1', 'hello 2', 'hello again 2']); + + client2.dispose(); + server.dispose(); + }); + }); }); diff --git a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts index 957329d032b..0f395deb96e 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Socket } from 'net'; +import { createServer, Socket } from 'net'; import { EventEmitter } from 'events'; import { Protocol, PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; -import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { createRandomIPCHandle, createStaticIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { VSBuffer } from 'vs/base/common/buffer'; +import { tmpdir } from 'os'; +import product from 'vs/platform/product/common/product'; class MessageStream { @@ -222,3 +224,35 @@ suite('PersistentProtocol reconnection', () => { assert.equal(b.unacknowledgedCount, 0); }); }); + +suite('IPC, create handle', () => { + + test('createRandomIPCHandle', async () => { + return testIPCHandle(createRandomIPCHandle()); + }); + + test('createStaticIPCHandle', async () => { + return testIPCHandle(createStaticIPCHandle(tmpdir(), 'test', product.version)); + }); + + function testIPCHandle(handle: string): Promise { + return new Promise((resolve, reject) => { + const pipeName = createRandomIPCHandle(); + + const server = createServer(); + + server.on('error', () => { + return new Promise(() => server.close(() => reject())); + }); + + server.listen(pipeName, () => { + server.removeListener('error', reject); + + return new Promise(() => { + server.close(() => resolve()); + }); + }); + }); + } + +}); diff --git a/src/vs/workbench/browser/parts/quickinput/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css similarity index 70% rename from src/vs/workbench/browser/parts/quickinput/media/quickInput.css rename to src/vs/base/parts/quickinput/browser/media/quickInput.css index 1a5f4d35f6c..29a3f02bca7 100644 --- a/src/vs/workbench/browser/parts/quickinput/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -49,12 +49,22 @@ margin: 6px; } +.quick-input-header .quick-input-description { + margin: 4px 2px; +} + .quick-input-header { display: flex; padding: 6px 6px 0px 6px; margin-bottom: -2px; } +.quick-input-widget.hidden-input .quick-input-header { + /* reduce margins and paddings when input box hidden */ + padding: 0; + margin-bottom: 0; +} + .quick-input-and-message { display: flex; flex-direction: column; @@ -91,10 +101,16 @@ align-self: center; position: absolute; right: 4px; + display: flex; + align-items: center; } .quick-input-count .monaco-count-badge { vertical-align: middle; + padding: 2px 4px; + border-radius: 2px; + min-height: auto; + line-height: normal; } .quick-input-action { @@ -102,9 +118,11 @@ } .quick-input-action .monaco-text-button { - font-size: 85%; - padding: 7px 6px 5.5px 6px; - line-height: initial; + font-size: 11px; + padding: 0 6px; + display: flex; + height: 100%; + align-items: center; } .quick-input-message { @@ -126,6 +144,10 @@ margin-top: 6px; } +.quick-input-widget.hidden-input .quick-input-list { + margin-top: 0; /* reduce margins when input box hidden */ +} + .quick-input-list .monaco-list { overflow: hidden; max-height: calc(20 * 22px); @@ -186,7 +208,12 @@ align-items: center; } -.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon { +.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label, +.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container { + flex: 1; /* make sure the icon label grows within the row */ +} + +.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon[class*='codicon-'] { vertical-align: sub; } @@ -194,6 +221,10 @@ opacity: 1; } +.quick-input-list .quick-input-list-entry .quick-input-list-entry-keybinding { + margin-right: 8px; /* separate from the separator label or scrollbar if any */ +} + .quick-input-list .quick-input-list-label-meta { opacity: 0.7; line-height: normal; @@ -205,25 +236,28 @@ font-weight: bold; } -.quick-input-list .quick-input-list-separator { - margin-right: 18px; -} - -.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-separator, -.quick-input-list .monaco-list-row.focused .quick-input-list-entry.has-actions .quick-input-list-separator { - margin-right: 0; +.quick-input-list .quick-input-list-entry .quick-input-list-separator { + margin-right: 8px; /* separate from keybindings or actions */ } .quick-input-list .quick-input-list-entry-action-bar { - display: none; + display: flex; flex: 0; overflow: visible; } +.quick-input-list .quick-input-list-entry-action-bar .action-label { + /* + * By default, actions in the quick input action bar are hidden + * until hovered over them or selected. + */ + display: none; +} + .quick-input-list .quick-input-list-entry-action-bar .action-label.codicon { margin: 0; - width: 19px; height: 100%; + padding: 0 2px; vertical-align: middle; } @@ -231,15 +265,16 @@ margin-top: 1px; } -.quick-input-list .quick-input-list-entry-action-bar ul:first-child .action-label.codicon { - margin-left: 2px; +.quick-input-list .quick-input-list-entry-action-bar { + margin-right: 4px; /* separate from scrollbar */ } -.quick-input-list .quick-input-list-entry-action-bar ul:last-child .action-label.codicon { - margin-right: 8px; +.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon { + margin-right: 4px; /* separate actions */ } -.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar, -.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar { +.quick-input-list .quick-input-list-entry .quick-input-list-entry-action-bar .action-label.always-visible, +.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar .action-label, +.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar .action-label { display: flex; } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts similarity index 69% rename from src/vs/workbench/browser/parts/quickinput/quickInput.ts rename to src/vs/base/parts/quickinput/browser/quickInput.ts index ab197d73eda..0a4c9f1ba53 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -4,67 +4,88 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/quickInput'; -import { Component } from 'vs/workbench/common/component'; -import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent, NO_KEY_MODS, ItemActivation } from 'vs/base/parts/quickinput/common/quickInput'; import * as dom from 'vs/base/browser/dom'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; -import { QUICK_INPUT_BACKGROUND, QUICK_INPUT_FOREGROUND } from 'vs/workbench/common/theme'; -import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { QuickInputList } from './quickInputList'; +import { QuickInputList, QuickInputListFocus } from './quickInputList'; import { QuickInputBox } from './quickInputBox'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { localize } from 'vs/nls'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen'; -import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { attachBadgeStyler, attachProgressBarStyler, attachButtonStyler } from 'vs/platform/theme/common/styler'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { CountBadge, ICountBadgetyles } from 'vs/base/browser/ui/countBadge/countBadge'; +import { ProgressBar, IProgressBarStyles } from 'vs/base/browser/ui/progressbar/progressbar'; import { Emitter, Event } from 'vs/base/common/event'; -import { Button } from 'vs/base/browser/ui/button/button'; +import { Button, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { inQuickOpenContext, InQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; -import { URI } from 'vs/base/common/uri'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { equals } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; -import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; +import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; +import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; +import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; +import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; +import { Color } from 'vs/base/common/color'; +import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; + +export interface IQuickInputOptions { + idPrefix: string; + container: HTMLElement; + ignoreFocusOut(): boolean; + isScreenReaderOptimized(): boolean; + backKeybindingLabel(): string | undefined; + setContextKey(id?: string): void; + returnFocus(): void; + createList( + user: string, + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: IListRenderer[], + options: IListOptions, + ): List; + styles: IQuickInputStyles; +} + +export interface IQuickInputStyles { + widget: IQuickInputWidgetStyles; + inputBox: IInputBoxStyles; + countBadge: ICountBadgetyles; + button: IButtonStyles; + progressBar: IProgressBarStyles; + list: IListStyles & { listInactiveFocusForeground?: Color; pickerGroupBorder?: Color; pickerGroupForeground?: Color; }; +} + +export interface IQuickInputWidgetStyles { + quickInputBackground?: Color; + quickInputForeground?: Color; + quickInputTitleBackground?: Color; + contrastBorder?: Color; + widgetShadow?: Color; +} const $ = dom.$; type Writeable = { -readonly [P in keyof T]: T[P] }; + +const backButtonIcon = registerIcon('quick-input-back', Codicon.arrowLeft); + const backButton = { - iconPath: { - dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/quickinput/media/arrow-left-dark.svg')), - light: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/quickinput/media/arrow-left-light.svg')) - }, + iconClass: backButtonIcon.classNames, tooltip: localize('quickInput.back', "Back"), handle: -1 // TODO }; interface QuickInputUI { container: HTMLElement; + styleSheet: HTMLStyleElement; leftActionBar: ActionBar; titleBar: HTMLElement; title: HTMLElement; - description: HTMLElement; + description1: HTMLElement; + description2: HTMLElement; rightActionBar: ActionBar; checkAll: HTMLInputElement; filterContainer: HTMLElement; @@ -99,12 +120,14 @@ type Visibilities = { description?: boolean; checkAll?: boolean; inputBox?: boolean; + checkBox?: boolean; visibleCount?: boolean; count?: boolean; message?: boolean; list?: boolean; ok?: boolean; customButton?: boolean; + progressBar?: boolean; }; class QuickInput extends Disposable implements IQuickInput { @@ -122,6 +145,7 @@ class QuickInput extends Disposable implements IQuickInput { private buttonsUpdated = false; private readonly onDidTriggerButtonEmitter = this._register(new Emitter()); private readonly onDidHideEmitter = this._register(new Emitter()); + private readonly onDisposeEmitter = this._register(new Emitter()); protected readonly visibleDisposables = this._register(new DisposableStore()); @@ -215,7 +239,7 @@ class QuickInput extends Disposable implements IQuickInput { this.update(); } - onDidTriggerButton = this.onDidTriggerButtonEmitter.event; + readonly onDidTriggerButton = this.onDidTriggerButtonEmitter.event; show(): void { if (this.visible) { @@ -246,19 +270,24 @@ class QuickInput extends Disposable implements IQuickInput { this.onDidHideEmitter.fire(); } - onDidHide = this.onDidHideEmitter.event; + readonly onDidHide = this.onDidHideEmitter.event; protected update() { if (!this.visible) { return; } const title = this.getTitle(); - if (this.ui.title.textContent !== title) { + if (title && this.ui.title.textContent !== title) { this.ui.title.textContent = title; + } else if (!title && this.ui.title.innerHTML !== ' ') { + this.ui.title.innerText = '\u00a0;'; } const description = this.getDescription(); - if (this.ui.description.textContent !== description) { - this.ui.description.textContent = description; + if (this.ui.description1.textContent !== description) { + this.ui.description1.textContent = description; + } + if (this.ui.description2.textContent !== description) { + this.ui.description2.textContent = description; } if (this.busy && !this.busyDelay) { this.busyDelay = new TimeoutTimer(); @@ -278,9 +307,8 @@ class QuickInput extends Disposable implements IQuickInput { this.ui.leftActionBar.clear(); const leftButtons = this.buttons.filter(button => button === backButton); this.ui.leftActionBar.push(leftButtons.map((button, index) => { - const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => { + const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, async () => { this.onDidTriggerButtonEmitter.fire(button); - return Promise.resolve(null); }); action.tooltip = button.tooltip || ''; return action; @@ -288,9 +316,8 @@ class QuickInput extends Disposable implements IQuickInput { this.ui.rightActionBar.clear(); const rightButtons = this.buttons.filter(button => button !== backButton); this.ui.rightActionBar.push(rightButtons.map((button, index) => { - const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => { + const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, async () => { this.onDidTriggerButtonEmitter.fire(button); - return Promise.resolve(null); }); action.tooltip = button.tooltip || ''; return action; @@ -332,39 +359,48 @@ class QuickInput extends Disposable implements IQuickInput { this.ui.inputBox.showDecoration(severity); if (severity === Severity.Error) { const styles = this.ui.inputBox.stylesForType(severity); + this.ui.message.style.color = styles.foreground ? `${styles.foreground}` : ''; this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : ''; this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : ''; this.ui.message.style.paddingBottom = '4px'; } else { + this.ui.message.style.color = ''; this.ui.message.style.backgroundColor = ''; this.ui.message.style.border = ''; this.ui.message.style.paddingBottom = ''; } } - public dispose(): void { + readonly onDispose = this.onDisposeEmitter.event; + + dispose(): void { this.hide(); + this.onDisposeEmitter.fire(); + super.dispose(); } } class QuickPick extends QuickInput implements IQuickPick { - private static readonly INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); + private static readonly DEFAULT_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); private _value = ''; + private _ariaLabel: string | undefined; private _placeholder: string | undefined; private readonly onDidChangeValueEmitter = this._register(new Emitter()); - private readonly onDidAcceptEmitter = this._register(new Emitter()); + private readonly onDidAcceptEmitter = this._register(new Emitter()); private readonly onDidCustomEmitter = this._register(new Emitter()); private _items: Array = []; private itemsUpdated = false; private _canSelectMany = false; + private _canAcceptInBackground = false; private _matchOnDescription = false; private _matchOnDetail = false; private _matchOnLabel = true; private _sortByLabel = true; private _autoFocusOnList = true; + private _itemActivation = this.ui.isScreenReaderOptimized() ? ItemActivation.NONE /* https://github.com/microsoft/vscode/issues/57501 */ : ItemActivation.FIRST; private _activeItems: T[] = []; private activeItemsUpdated = false; private activeItemsToConfirm: T[] | null = []; @@ -377,13 +413,22 @@ class QuickPick extends QuickInput implements IQuickPi private _valueSelection: Readonly<[number, number]> | undefined; private valueSelectionUpdated = true; private _validationMessage: string | undefined; - private _ok = false; + private _ok: boolean | 'default' = 'default'; private _customButton = false; private _customButtonLabel: string | undefined; private _customButtonHover: string | undefined; + private _quickNavigate: IQuickNavigateConfiguration | undefined; + private _hideInput: boolean | undefined; + private _hideCheckAll: boolean | undefined; - quickNavigate: IQuickNavigateConfiguration | undefined; + get quickNavigate() { + return this._quickNavigate; + } + set quickNavigate(quickNavigate: IQuickNavigateConfiguration | undefined) { + this._quickNavigate = quickNavigate; + this.update(); + } get value() { return this._value; @@ -394,6 +439,17 @@ class QuickPick extends QuickInput implements IQuickPi this.update(); } + filterValue = (value: string) => value; + + set ariaLabel(ariaLabel: string | undefined) { + this._ariaLabel = ariaLabel; + this.update(); + } + + get ariaLabel() { + return this._ariaLabel; + } + get placeholder() { return this._placeholder; } @@ -428,6 +484,14 @@ class QuickPick extends QuickInput implements IQuickPi this.update(); } + get canAcceptInBackground() { + return this._canAcceptInBackground; + } + + set canAcceptInBackground(canAcceptInBackground: boolean) { + this._canAcceptInBackground = canAcceptInBackground; + } + get matchOnDescription() { return this._matchOnDescription; } @@ -464,7 +528,6 @@ class QuickPick extends QuickInput implements IQuickPi this.update(); } - get autoFocusOnList() { return this._autoFocusOnList; } @@ -474,6 +537,14 @@ class QuickPick extends QuickInput implements IQuickPi this.update(); } + get itemActivation() { + return this._itemActivation; + } + + set itemActivation(itemActivation: ItemActivation) { + this._itemActivation = itemActivation; + } + get activeItems() { return this._activeItems; } @@ -497,6 +568,13 @@ class QuickPick extends QuickInput implements IQuickPi } get keyMods() { + if (this._quickNavigate) { + // Disable keyMods when quick navigate is enabled + // because in this model the interaction is purely + // keyboard driven and Ctrl/Alt are typically + // pressed and hold during this interaction. + return NO_KEY_MODS; + } return this.ui.keyMods; } @@ -546,23 +624,45 @@ class QuickPick extends QuickInput implements IQuickPi return this._ok; } - set ok(showOkButton: boolean) { + set ok(showOkButton: boolean | 'default') { this._ok = showOkButton; this.update(); } - public inputHasFocus(): boolean { + inputHasFocus(): boolean { return this.visible ? this.ui.inputBox.hasFocus() : false; } + focusOnInput() { + this.ui.inputBox.setFocus(); + } + + get hideInput() { + return !!this._hideInput; + } + + set hideInput(hideInput: boolean) { + this._hideInput = hideInput; + this.update(); + } + + get hideCheckAll() { + return !!this._hideCheckAll; + } + + set hideCheckAll(hideCheckAll: boolean) { + this._hideCheckAll = hideCheckAll; + this.update(); + } + onDidChangeSelection = this.onDidChangeSelectionEmitter.event; onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; private trySelectFirst() { if (this.autoFocusOnList) { - if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) { - this.ui.list.focus('First'); + if (!this.canSelectMany) { + this.ui.list.focus(QuickInputListFocus.First); } } } @@ -575,8 +675,10 @@ class QuickPick extends QuickInput implements IQuickPi return; } this._value = value; - this.ui.list.filter(this.ui.inputBox.value); - this.trySelectFirst(); + const didFilter = this.ui.list.filter(this.filterValue(this.ui.inputBox.value)); + if (didFilter) { + this.trySelectFirst(); + } this.onDidChangeValueEmitter.fire(value); })); this.visibleDisposables.add(this.ui.inputBox.onMouseDown(event => { @@ -584,47 +686,67 @@ class QuickPick extends QuickInput implements IQuickPi this.ui.list.clearFocus(); } })); - this.visibleDisposables.add(this.ui.inputBox.onKeyDown(event => { + this.visibleDisposables.add((this._hideInput ? this.ui.list : this.ui.inputBox).onKeyDown((event: KeyboardEvent | StandardKeyboardEvent) => { switch (event.keyCode) { case KeyCode.DownArrow: - this.ui.list.focus('Next'); + this.ui.list.focus(QuickInputListFocus.Next); if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.UpArrow: if (this.ui.list.getFocusedElements().length) { - this.ui.list.focus('Previous'); + this.ui.list.focus(QuickInputListFocus.Previous); } else { - this.ui.list.focus('Last'); + this.ui.list.focus(QuickInputListFocus.Last); } if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.PageDown: - if (this.ui.list.getFocusedElements().length) { - this.ui.list.focus('NextPage'); - } else { - this.ui.list.focus('First'); - } + this.ui.list.focus(QuickInputListFocus.NextPage); if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.PageUp: - if (this.ui.list.getFocusedElements().length) { - this.ui.list.focus('PreviousPage'); - } else { - this.ui.list.focus('Last'); - } + this.ui.list.focus(QuickInputListFocus.PreviousPage); if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); + break; + case KeyCode.RightArrow: + if (!this._canAcceptInBackground) { + return; // needs to be enabled + } + + if (!this.ui.inputBox.isSelectionAtEnd()) { + return; // ensure input box selection at end + } + + if (this.activeItems[0]) { + this._selectedItems = [this.activeItems[0]]; + this.onDidChangeSelectionEmitter.fire(this.selectedItems); + this.onDidAcceptEmitter.fire({ inBackground: true }); + } + + break; + case KeyCode.Home: + if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) { + this.ui.list.focus(QuickInputListFocus.First); + dom.EventHelper.stop(event, true); + } + break; + case KeyCode.End: + if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) { + this.ui.list.focus(QuickInputListFocus.Last); + dom.EventHelper.stop(event, true); + } break; } })); @@ -633,10 +755,10 @@ class QuickPick extends QuickInput implements IQuickPi this._selectedItems = [this.activeItems[0]]; this.onDidChangeSelectionEmitter.fire(this.selectedItems); } - this.onDidAcceptEmitter.fire(undefined); + this.onDidAcceptEmitter.fire({ inBackground: false }); })); this.visibleDisposables.add(this.ui.onDidCustom(() => { - this.onDidCustomEmitter.fire(undefined); + this.onDidCustomEmitter.fire(); })); this.visibleDisposables.add(this.ui.list.onDidChangeFocus(focusedItems => { if (this.activeItemsUpdated) { @@ -648,7 +770,7 @@ class QuickPick extends QuickInput implements IQuickPi this._activeItems = focusedItems as T[]; this.onDidChangeActiveEmitter.fire(focusedItems as T[]); })); - this.visibleDisposables.add(this.ui.list.onDidChangeSelection(selectedItems => { + this.visibleDisposables.add(this.ui.list.onDidChangeSelection(({ items: selectedItems, event }) => { if (this.canSelectMany) { if (selectedItems.length) { this.ui.list.setSelectedElements([]); @@ -661,7 +783,7 @@ class QuickPick extends QuickInput implements IQuickPi this._selectedItems = selectedItems as T[]; this.onDidChangeSelectionEmitter.fire(selectedItems as T[]); if (selectedItems.length) { - this.onDidAcceptEmitter.fire(undefined); + this.onDidAcceptEmitter.fire({ inBackground: event instanceof MouseEvent && event.button === 1 /* mouse middle click */ }); } })); this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => { @@ -683,7 +805,7 @@ class QuickPick extends QuickInput implements IQuickPi private registerQuickNavigation() { return dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, e => { - if (this.canSelectMany || !this.quickNavigate) { + if (this.canSelectMany || !this._quickNavigate) { return; } @@ -691,7 +813,7 @@ class QuickPick extends QuickInput implements IQuickPi const keyCode = keyboardEvent.keyCode; // Select element when keys are pressed that signal it - const quickNavKeys = this.quickNavigate.keybindings; + const quickNavKeys = this._quickNavigate.keybindings; const wasTriggerKeyPressed = quickNavKeys.some(k => { const [firstPart, chordPart] = k.getParts(); if (chordPart) { @@ -700,7 +822,7 @@ class QuickPick extends QuickInput implements IQuickPi if (firstPart.shiftKey && keyCode === KeyCode.Shift) { if (keyboardEvent.ctrlKey || keyboardEvent.altKey || keyboardEvent.metaKey) { - return false; // this is an optimistic check for the shift key being used to navigate back in quick open + return false; // this is an optimistic check for the shift key being used to navigate back in quick input } return true; @@ -721,10 +843,16 @@ class QuickPick extends QuickInput implements IQuickPi return false; }); - if (wasTriggerKeyPressed && this.activeItems[0]) { - this._selectedItems = [this.activeItems[0]]; - this.onDidChangeSelectionEmitter.fire(this.selectedItems); - this.onDidAcceptEmitter.fire(undefined); + if (wasTriggerKeyPressed) { + if (this.activeItems[0]) { + this._selectedItems = [this.activeItems[0]]; + this.onDidChangeSelectionEmitter.fire(this.selectedItems); + this.onDidAcceptEmitter.fire({ inBackground: false }); + } + // Unset quick navigate after press. It is only valid once + // and should not result in any behaviour change afterwards + // if the picker remains open because there was no active item + this._quickNavigate = undefined; } }); } @@ -733,7 +861,32 @@ class QuickPick extends QuickInput implements IQuickPi if (!this.visible) { return; } - this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, description: !!this.description, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: this.ok, list: true, message: !!this.validationMessage, customButton: this.customButton } : { title: !!this.title || !!this.step, description: !!this.description, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage, customButton: this.customButton, ok: this.ok }); + let hideInput = false; + let inputShownJustForScreenReader = false; + if (!!this._hideInput && this._items.length > 0) { + if (this.ui.isScreenReaderOptimized()) { + // Always show input if screen reader attached https://github.com/microsoft/vscode/issues/94360 + inputShownJustForScreenReader = true; + } else { + hideInput = true; + } + } + this.ui.container.classList.toggle('hidden-input', hideInput && !this.description); + const visibilities: Visibilities = { + title: !!this.title || !!this.step || !!this.buttons.length, + description: !!this.description, + checkAll: this.canSelectMany && !this._hideCheckAll, + checkBox: this.canSelectMany, + inputBox: !hideInput, + progressBar: !hideInput, + visibleCount: true, + count: this.canSelectMany, + ok: this.ok === 'default' ? this.canSelectMany : this.ok, + list: true, + message: !!this.validationMessage, + customButton: this.customButton + }; + this.ui.setVisibilities(visibilities); super.update(); if (this.ui.inputBox.value !== this.value) { this.ui.inputBox.value = this.value; @@ -745,14 +898,41 @@ class QuickPick extends QuickInput implements IQuickPi if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { this.ui.inputBox.placeholder = (this.placeholder || ''); } + if (inputShownJustForScreenReader) { + this.ui.inputBox.ariaLabel = ''; + } else { + const ariaLabel = this.ariaLabel || this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; + if (this.ui.inputBox.ariaLabel !== ariaLabel) { + this.ui.inputBox.ariaLabel = ariaLabel; + } + } + this.ui.list.matchOnDescription = this.matchOnDescription; + this.ui.list.matchOnDetail = this.matchOnDetail; + this.ui.list.matchOnLabel = this.matchOnLabel; + this.ui.list.sortByLabel = this.sortByLabel; if (this.itemsUpdated) { this.itemsUpdated = false; this.ui.list.setElements(this.items); - this.ui.list.filter(this.ui.inputBox.value); + this.ui.list.filter(this.filterValue(this.ui.inputBox.value)); this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked(); this.ui.visibleCount.setCount(this.ui.list.getVisibleCount()); this.ui.count.setCount(this.ui.list.getCheckedCount()); - this.trySelectFirst(); + switch (this._itemActivation) { + case ItemActivation.NONE: + this._itemActivation = ItemActivation.FIRST; // only valid once, then unset + break; + case ItemActivation.SECOND: + this.ui.list.focus(QuickInputListFocus.Second); + this._itemActivation = ItemActivation.FIRST; // only valid once, then unset + break; + case ItemActivation.LAST: + this.ui.list.focus(QuickInputListFocus.Last); + this._itemActivation = ItemActivation.FIRST; // only valid once, then unset + break; + default: + this.trySelectFirst(); + break; + } } if (this.ui.container.classList.contains('show-checkboxes') !== !!this.canSelectMany) { if (this.canSelectMany) { @@ -790,12 +970,17 @@ class QuickPick extends QuickInput implements IQuickPi } this.ui.customButton.label = this.customLabel || ''; this.ui.customButton.element.title = this.customHover || ''; - this.ui.list.matchOnDescription = this.matchOnDescription; - this.ui.list.matchOnDetail = this.matchOnDetail; - this.ui.list.matchOnLabel = this.matchOnLabel; - this.ui.list.sortByLabel = this.sortByLabel; this.ui.setComboboxAccessibility(true); - this.ui.inputBox.setAttribute('aria-label', QuickPick.INPUT_BOX_ARIA_LABEL); + if (!visibilities.inputBox) { + // we need to move focus into the tree to detect keybindings + // properly when the input box is not visible (quick nav) + this.ui.list.domFocus(); + + // Focus the first element in the list if multiselect is enabled + if (this.canSelectMany) { + this.ui.list.focus(QuickInputListFocus.First); + } + } } } @@ -882,7 +1067,7 @@ class InputBox extends QuickInput implements IInputBox { this._value = value; this.onDidValueChangeEmitter.fire(value); })); - this.visibleDisposables.add(this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire(undefined))); + this.visibleDisposables.add(this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire())); this.valueSelectionUpdated = true; } super.show(); @@ -892,7 +1077,12 @@ class InputBox extends QuickInput implements IInputBox { if (!this.visible) { return; } - this.ui.setVisibilities({ title: !!this.title || !!this.step, description: !!this.description || !!this.step, inputBox: true, message: true }); + const visibilities: Visibilities = { + title: !!this.title || !!this.step || !!this.buttons.length, + description: !!this.description || !!this.step, + inputBox: true, message: true + }; + this.ui.setVisibilities(visibilities); super.update(); if (this.ui.inputBox.value !== this.value) { this.ui.inputBox.value = this.value; @@ -918,21 +1108,15 @@ class InputBox extends QuickInput implements IInputBox { } } -export class QuickInputService extends Component implements IQuickInputService { +export class QuickInputController extends Disposable { + private static readonly MAX_WIDTH = 600; // Max total width of quick input widget - public _serviceBrand: undefined; - - private static readonly ID = 'workbench.component.quickinput'; - private static readonly MAX_WIDTH = 600; // Max total width of quick open widget - - private idPrefix = 'quickInput_'; // Constant since there is still only one. + private idPrefix: string; private ui: QuickInputUI | undefined; - private dimension?: dom.Dimension; + private dimension?: dom.IDimension; + private titleBarOffset?: number; private comboboxAccessibility = false; private enabled = true; - private inQuickOpenWidgets: Record = {}; - private inQuickOpenContext: IContextKey; - private contexts: Map> = new Map(); private readonly onDidAcceptEmitter = this._register(new Emitter()); private readonly onDidCustomEmitter = this._register(new Emitter()); private readonly onDidTriggerButtonEmitter = this._register(new Emitter()); @@ -940,101 +1124,33 @@ export class QuickInputService extends Component implements IQuickInputService { private controller: QuickInput | null = null; - constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IQuickOpenService private readonly quickOpenService: IQuickOpenService, - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IThemeService themeService: IThemeService, - @IStorageService storageService: IStorageService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(QuickInputService.ID, themeService, storageService); - this.inQuickOpenContext = InQuickOpenContextKey.bindTo(contextKeyService); - this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true))); - this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false))); - this._register(this.layoutService.onLayout(dimension => this.layout(dimension))); - this.layout(this.layoutService.dimension); + private parentElement: HTMLElement; + private styles: IQuickInputStyles; + + private onShowEmitter = this._register(new Emitter()); + readonly onShow = this.onShowEmitter.event; + + private onHideEmitter = this._register(new Emitter()); + readonly onHide = this.onHideEmitter.event; + + private previousFocusElement?: HTMLElement; + + constructor(private options: IQuickInputOptions) { + super(); + this.idPrefix = options.idPrefix; + this.parentElement = options.container; + this.styles = options.styles; this.registerKeyModsListeners(); } - private inQuickOpen(widget: 'quickInput' | 'quickOpen', open: boolean) { - if (open) { - this.inQuickOpenWidgets[widget] = true; - } else { - delete this.inQuickOpenWidgets[widget]; - } - if (Object.keys(this.inQuickOpenWidgets).length) { - if (!this.inQuickOpenContext.get()) { - this.inQuickOpenContext.set(true); - } - } else { - if (this.inQuickOpenContext.get()) { - this.inQuickOpenContext.reset(); - } - } - } - - private setContextKey(id?: string) { - let key: IContextKey | undefined; - if (id) { - key = this.contexts.get(id); - if (!key) { - key = new RawContextKey(id, false) - .bindTo(this.contextKeyService); - this.contexts.set(id, key); - } - } - - if (key && key.get()) { - return; // already active context - } - - this.resetContextKeys(); - - if (key) { - key.set(true); - } - } - - private resetContextKeys() { - this.contexts.forEach(context => { - if (context.get()) { - context.reset(); - } - }); - } - private registerKeyModsListeners() { - const workbench = this.layoutService.getWorkbenchElement(); - this._register(dom.addDisposableListener(workbench, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { - const event = new StandardKeyboardEvent(e); - switch (event.keyCode) { - case KeyCode.Ctrl: - case KeyCode.Meta: - this.keyMods.ctrlCmd = true; - break; - case KeyCode.Alt: - this.keyMods.alt = true; - break; - } - })); - this._register(dom.addDisposableListener(workbench, dom.EventType.KEY_UP, (e: KeyboardEvent) => { - const event = new StandardKeyboardEvent(e); - switch (event.keyCode) { - case KeyCode.Ctrl: - case KeyCode.Meta: - this.keyMods.ctrlCmd = false; - break; - case KeyCode.Alt: - this.keyMods.alt = false; - break; - } - })); + const listener = (e: KeyboardEvent | MouseEvent) => { + this.keyMods.ctrlCmd = e.ctrlKey || e.metaKey; + this.keyMods.alt = e.altKey; + }; + this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, listener, true)); + this._register(dom.addDisposableListener(window, dom.EventType.KEY_UP, listener, true)); + this._register(dom.addDisposableListener(window, dom.EventType.MOUSE_DOWN, listener, true)); } private getUI() { @@ -1042,11 +1158,12 @@ export class QuickInputService extends Component implements IQuickInputService { return this.ui; } - const workbench = this.layoutService.getWorkbenchElement(); - const container = dom.append(workbench, $('.quick-input-widget.show-file-icons')); + const container = dom.append(this.parentElement, $('.quick-input-widget.show-file-icons')); container.tabIndex = -1; container.style.display = 'none'; + const styleSheet = dom.createStyleSheet(container); + const titleBar = dom.append(container, $('.quick-input-titlebar')); const leftActionBar = this._register(new ActionBar(titleBar)); @@ -1057,8 +1174,7 @@ export class QuickInputService extends Component implements IQuickInputService { const rightActionBar = this._register(new ActionBar(titleBar)); rightActionBar.domNode.classList.add('quick-input-right-action-bar'); - const description = dom.append(container, $('.quick-input-description')); - + const description1 = dom.append(container, $('.quick-input-description')); const headerContainer = dom.append(container, $('.quick-input-header')); const checkAll = dom.append(headerContainer, $('input.quick-input-check-all')); @@ -1073,6 +1189,7 @@ export class QuickInputService extends Component implements IQuickInputService { } })); + const description2 = dom.append(headerContainer, $('.quick-input-description')); const extraContainer = dom.append(headerContainer, $('.quick-input-and-message')); const filterContainer = dom.append(extraContainer, $('.quick-input-filter')); @@ -1087,11 +1204,9 @@ export class QuickInputService extends Component implements IQuickInputService { const countContainer = dom.append(filterContainer, $('.quick-input-count')); countContainer.setAttribute('aria-live', 'polite'); const count = new CountBadge(countContainer, { countFormat: localize({ key: 'quickInput.countSelected', comment: ['This tells the user how many items are selected in a list of items to select from. The items can be anything.'] }, "{0} Selected") }); - this._register(attachBadgeStyler(count, this.themeService)); const okContainer = dom.append(headerContainer, $('.quick-input-action')); const ok = new Button(okContainer); - attachButtonStyler(ok, this.themeService); ok.label = localize('ok', "OK"); this._register(ok.onDidClick(e => { this.onDidAcceptEmitter.fire(); @@ -1099,7 +1214,6 @@ export class QuickInputService extends Component implements IQuickInputService { const customButtonContainer = dom.append(headerContainer, $('.quick-input-action')); const customButton = new Button(customButtonContainer); - attachButtonStyler(customButton, this.themeService); customButton.label = localize('custom', "Custom"); this._register(customButton.onDidClick(e => { this.onDidCustomEmitter.fire(); @@ -1108,10 +1222,9 @@ export class QuickInputService extends Component implements IQuickInputService { const message = dom.append(extraContainer, $(`#${this.idPrefix}message.quick-input-message`)); const progressBar = new ProgressBar(container); - dom.addClass(progressBar.getContainer(), 'quick-input-progress'); - this._register(attachProgressBarStyler(progressBar, this.themeService)); + progressBar.getContainer().classList.add('quick-input-progress'); - const list = this._register(this.instantiationService.createInstance(QuickInputList, container, this.idPrefix + 'list')); + const list = this._register(new QuickInputList(container, this.idPrefix + 'list', this.options)); this._register(list.onChangedAllVisibleChecked(checked => { checkAll.checked = checked; })); @@ -1138,10 +1251,14 @@ export class QuickInputService extends Component implements IQuickInputService { const focusTracker = dom.trackFocus(container); this._register(focusTracker); + this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, e => { + this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; + }, true)); this._register(focusTracker.onDidBlur(() => { - if (!this.getUI().ignoreFocusOut && !this.environmentService.args['sticky-quickopen'] && this.configurationService.getValue(CLOSE_ON_FOCUS_LOST_CONFIG)) { - this.hide(true); + if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { + this.hide(); } + this.previousFocusElement = undefined; })); this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { inputBox.setFocus(); @@ -1181,14 +1298,14 @@ export class QuickInputService extends Component implements IQuickInputService { } })); - this._register(this.quickOpenService.onShow(() => this.hide(true))); - this.ui = { container, + styleSheet, leftActionBar, titleBar, title, - description, + description1, + description2, rightActionBar, checkAll, filterContainer, @@ -1209,21 +1326,22 @@ export class QuickInputService extends Component implements IQuickInputService { onDidTriggerButton: this.onDidTriggerButtonEmitter.event, ignoreFocusOut: false, keyMods: this.keyMods, - isScreenReaderOptimized: () => this.isScreenReaderOptimized(), + isScreenReaderOptimized: () => this.options.isScreenReaderOptimized(), show: controller => this.show(controller), hide: () => this.hide(), setVisibilities: visibilities => this.setVisibilities(visibilities), setComboboxAccessibility: enabled => this.setComboboxAccessibility(enabled), setEnabled: enabled => this.setEnabled(enabled), - setContextKey: contextKey => this.setContextKey(contextKey), + setContextKey: contextKey => this.options.setContextKey(contextKey), }; this.updateStyles(); return this.ui; } - pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise { - return new Promise((doResolve, reject) => { - let resolve = (result: any) => { + pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { + type R = (O extends { canPickMany: true } ? T[] : T) | undefined; + return new Promise((doResolve, reject) => { + let resolve = (result: R) => { resolve = doResolve; if (options.onKeyMods) { options.onKeyMods(input.keyMods); @@ -1240,12 +1358,12 @@ export class QuickInputService extends Component implements IQuickInputService { input, input.onDidAccept(() => { if (input.canSelectMany) { - resolve(input.selectedItems.slice()); + resolve(input.selectedItems.slice()); input.hide(); } else { const result = input.activeItems[0]; if (result) { - resolve(result); + resolve(result); input.hide(); } } @@ -1260,7 +1378,7 @@ export class QuickInputService extends Component implements IQuickInputService { if (!input.canSelectMany) { const result = items[0]; if (result) { - resolve(result); + resolve(result); input.hide(); } } @@ -1319,8 +1437,8 @@ export class QuickInputService extends Component implements IQuickInputService { }); } - input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { - return new Promise((resolve, reject) => { + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((resolve) => { if (token.isCancellationRequested) { resolve(undefined); return; @@ -1390,7 +1508,7 @@ export class QuickInputService extends Component implements IQuickInputService { private show(controller: QuickInput) { const ui = this.getUI(); - this.quickOpenService.close(); + this.onShowEmitter.fire(); const oldController = this.controller; this.controller = controller; if (oldController) { @@ -1400,7 +1518,8 @@ export class QuickInputService extends Component implements IQuickInputService { this.setEnabled(true); ui.leftActionBar.clear(); ui.title.textContent = ''; - ui.description.textContent = ''; + ui.description1.textContent = ''; + ui.description2.textContent = ''; ui.rightActionBar.clear(); ui.checkAll.checked = false; // ui.inputBox.value = ''; Avoid triggering an event. @@ -1418,13 +1537,10 @@ export class QuickInputService extends Component implements IQuickInputService { ui.list.sortByLabel = true; ui.ignoreFocusOut = false; this.setComboboxAccessibility(false); - ui.inputBox.removeAttribute('aria-label'); + ui.inputBox.ariaLabel = ''; - const keybinding = this.keybindingService.lookupKeybinding(QuickPickBack.id); - backButton.tooltip = keybinding ? localize('quickInput.backWithKeybinding', "Back ({0})", keybinding.getLabel()) : localize('quickInput.back', "Back"); - - this.inQuickOpen('quickInput', true); - this.resetContextKeys(); + const backKeybindingLabel = this.options.backKeybindingLabel(); + backButton.tooltip = backKeybindingLabel ? localize('quickInput.backWithKeybinding', "Back ({0})", backKeybindingLabel) : localize('quickInput.back', "Back"); ui.container.style.display = ''; this.updateLayout(); @@ -1434,7 +1550,8 @@ export class QuickInputService extends Component implements IQuickInputService { private setVisibilities(visibilities: Visibilities) { const ui = this.getUI(); ui.title.style.display = visibilities.title ? '' : 'none'; - ui.description.style.display = visibilities.description ? '' : 'none'; + ui.description1.style.display = visibilities.description && (visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; + ui.description2.style.display = visibilities.description && !(visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; ui.checkAll.style.display = visibilities.checkAll ? '' : 'none'; ui.filterContainer.style.display = visibilities.inputBox ? '' : 'none'; ui.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none'; @@ -1442,8 +1559,9 @@ export class QuickInputService extends Component implements IQuickInputService { ui.okContainer.style.display = visibilities.ok ? '' : 'none'; ui.customButtonContainer.style.display = visibilities.customButton ? '' : 'none'; ui.message.style.display = visibilities.message ? '' : 'none'; + ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none'; ui.list.display(!!visibilities.list); - ui.container.classList[visibilities.checkAll ? 'add' : 'remove']('show-checkboxes'); + ui.container.classList[visibilities.checkBox ? 'add' : 'remove']('show-checkboxes'); this.updateLayout(); // TODO } @@ -1465,12 +1583,6 @@ export class QuickInputService extends Component implements IQuickInputService { } } - private isScreenReaderOptimized() { - const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled; - const config = this.configurationService.getValue('editor').accessibilitySupport; - return config === 'on' || (config === 'auto' && detected); - } - private setEnabled(enabled: boolean) { if (enabled !== this.enabled) { this.enabled = enabled; @@ -1487,15 +1599,20 @@ export class QuickInputService extends Component implements IQuickInputService { } } - private hide(focusLost?: boolean) { + hide() { const controller = this.controller; if (controller) { + const focusChanged = !this.ui?.container.contains(document.activeElement); this.controller = null; - this.inQuickOpen('quickInput', false); - this.resetContextKeys(); + this.onHideEmitter.fire(); this.getUI().container.style.display = 'none'; - if (!focusLost) { - this.editorGroupService.activeGroup.focus(); + if (!focusChanged) { + if (this.previousFocusElement && this.previousFocusElement.offsetParent) { + this.previousFocusElement.focus(); + this.previousFocusElement = undefined; + } else { + this.options.returnFocus(); + } } controller.didHide(); } @@ -1515,40 +1632,44 @@ export class QuickInputService extends Component implements IQuickInputService { navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { if (this.isDisplayed() && this.getUI().list.isDisplayed()) { - this.getUI().list.focus(next ? 'Next' : 'Previous'); + this.getUI().list.focus(next ? QuickInputListFocus.Next : QuickInputListFocus.Previous); if (quickNavigate && this.controller instanceof QuickPick) { this.controller.quickNavigate = quickNavigate; } } } - accept() { + async accept(keyMods: IKeyMods = { alt: false, ctrlCmd: false }) { + // When accepting the item programmatically, it is important that + // we update `keyMods` either from the provided set or unset it + // because the accept did not happen from mouse or keyboard + // interaction on the list itself + this.keyMods.alt = keyMods.alt; + this.keyMods.ctrlCmd = keyMods.ctrlCmd; + this.onDidAcceptEmitter.fire(); - return Promise.resolve(undefined); } - back() { + async back() { this.onDidTriggerButtonEmitter.fire(this.backButton); - return Promise.resolve(undefined); } - cancel() { + async cancel() { this.hide(); - return Promise.resolve(undefined); } - layout(dimension: dom.Dimension): void { + layout(dimension: dom.IDimension, titleBarOffset: number): void { this.dimension = dimension; + this.titleBarOffset = titleBarOffset; this.updateLayout(); } private updateLayout() { if (this.ui) { - const titlebarOffset = this.layoutService.getTitleBarOffset(); - this.ui.container.style.top = `${titlebarOffset}px`; + this.ui.container.style.top = `${this.titleBarOffset}px`; const style = this.ui.container.style; - const width = Math.min(this.layoutService.dimension.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH); + const width = Math.min(this.dimension!.width * 0.62 /* golden cut */, QuickInputController.MAX_WIDTH); style.width = width + 'px'; style.marginLeft = '-' + (width / 2) + 'px'; @@ -1557,21 +1678,47 @@ export class QuickInputService extends Component implements IQuickInputService { } } - protected updateStyles() { - const theme = this.themeService.getTheme(); + applyStyles(styles: IQuickInputStyles) { + this.styles = styles; + this.updateStyles(); + } + + private updateStyles() { if (this.ui) { - // TODO - const titleColor = { dark: 'rgba(255, 255, 255, 0.105)', light: 'rgba(0,0,0,.06)', hc: 'black' }[theme.type]; - this.ui.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : ''; - this.ui.inputBox.style(theme); - const quickInputBackground = theme.getColor(QUICK_INPUT_BACKGROUND); + const { + quickInputTitleBackground, + quickInputBackground, + quickInputForeground, + contrastBorder, + widgetShadow, + } = this.styles.widget; + this.ui.titleBar.style.backgroundColor = quickInputTitleBackground ? quickInputTitleBackground.toString() : ''; this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : ''; - const quickInputForeground = theme.getColor(QUICK_INPUT_FOREGROUND); this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : ''; - const contrastBorderColor = theme.getColor(contrastBorder); - this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : ''; - const widgetShadowColor = theme.getColor(widgetShadow); - this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : ''; + this.ui.container.style.border = contrastBorder ? `1px solid ${contrastBorder}` : ''; + this.ui.container.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : ''; + this.ui.inputBox.style(this.styles.inputBox); + this.ui.count.style(this.styles.countBadge); + this.ui.ok.style(this.styles.button); + this.ui.customButton.style(this.styles.button); + this.ui.progressBar.style(this.styles.progressBar); + this.ui.list.style(this.styles.list); + + const content: string[] = []; + if (this.styles.list.listInactiveFocusForeground) { + content.push(`.monaco-list .monaco-list-row.focused { color: ${this.styles.list.listInactiveFocusForeground}; }`); + content.push(`.monaco-list .monaco-list-row.focused:hover { color: ${this.styles.list.listInactiveFocusForeground}; }`); // overwrite :hover style in this case! + } + if (this.styles.list.pickerGroupBorder) { + content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.list.pickerGroupBorder}; }`); + } + if (this.styles.list.pickerGroupForeground) { + content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.list.pickerGroupForeground}; }`); + } + const newStyles = content.join('\n'); + if (newStyles !== this.ui.styleSheet.textContent) { + this.ui.styleSheet.textContent = newStyles; + } } } @@ -1579,30 +1726,3 @@ export class QuickInputService extends Component implements IQuickInputService { return this.ui && this.ui.container.style.display !== 'none'; } } - -export const QuickPickManyToggle: ICommandAndKeybindingRule = { - id: 'workbench.action.quickPickManyToggle', - weight: KeybindingWeight.WorkbenchContrib, - when: inQuickOpenContext, - primary: 0, - handler: accessor => { - const quickInputService = accessor.get(IQuickInputService); - quickInputService.toggle(); - } -}; - -export const QuickPickBack: ICommandAndKeybindingRule = { - id: 'workbench.action.quickInputBack', - weight: KeybindingWeight.WorkbenchContrib + 50, - when: inQuickOpenContext, - primary: 0, - win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, - mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS }, - handler: accessor => { - const quickInputService = accessor.get(IQuickInputService); - quickInputService.back(); - } -}; - -registerSingleton(IQuickInputService, QuickInputService, true); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts b/src/vs/base/parts/quickinput/browser/quickInputBox.ts similarity index 66% rename from src/vs/workbench/browser/parts/quickinput/quickInputBox.ts rename to src/vs/base/parts/quickinput/browser/quickInputBox.ts index ea21d8ff5f4..2e796764a6e 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputBox.ts @@ -5,9 +5,7 @@ import 'vs/css!./media/quickInput'; import * as dom from 'vs/base/browser/dom'; -import { InputBox, IRange, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; -import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme } from 'vs/platform/theme/common/themeService'; +import { InputBox, IRange, MessageType, IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import Severity from 'vs/base/common/severity'; @@ -56,7 +54,11 @@ export class QuickInputBox extends Disposable { this.inputBox.select(range); } - setPlaceholder(placeholder: string) { + isSelectionAtEnd(): boolean { + return this.inputBox.isSelectionAtEnd(); + } + + setPlaceholder(placeholder: string): void { this.inputBox.setPlaceHolder(placeholder); } @@ -68,6 +70,14 @@ export class QuickInputBox extends Disposable { this.inputBox.setPlaceHolder(placeholder); } + get ariaLabel() { + return this.inputBox.getAriaLabel(); + } + + set ariaLabel(ariaLabel: string) { + this.inputBox.setAriaLabel(ariaLabel); + } + get password() { return this.inputBox.inputElement.type === 'password'; } @@ -84,11 +94,11 @@ export class QuickInputBox extends Disposable { return this.inputBox.hasFocus(); } - setAttribute(name: string, value: string) { + setAttribute(name: string, value: string): void { this.inputBox.inputElement.setAttribute(name, value); } - removeAttribute(name: string) { + removeAttribute(name: string): void { this.inputBox.inputElement.removeAttribute(name); } @@ -112,20 +122,7 @@ export class QuickInputBox extends Disposable { this.inputBox.layout(); } - style(theme: ITheme) { - this.inputBox.style({ - inputForeground: theme.getColor(inputForeground), - inputBackground: theme.getColor(inputBackground), - inputBorder: theme.getColor(inputBorder), - inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), - inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground), - inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), - inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), - inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground), - inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), - inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), - inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), - inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder), - }); + style(styles: IInputBoxStyles): void { + this.inputBox.style(styles); } } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts similarity index 74% rename from src/vs/workbench/browser/parts/quickinput/quickInputList.ts rename to src/vs/base/parts/quickinput/browser/quickInputList.ts index 1242effa0ff..ba8bbd0239f 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -7,14 +7,11 @@ import 'vs/css!./media/quickInput'; import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; import * as dom from 'vs/base/browser/dom'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { WorkbenchList, IWorkbenchListOptions } from 'vs/platform/list/browser/listService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput'; import { IMatch } from 'vs/base/common/filters'; import { matchesFuzzyCodiconAware, parseCodicons } from 'vs/base/common/codicon'; import { compareAnything } from 'vs/base/common/comparers'; import { Emitter, Event } from 'vs/base/common/event'; -import { assign } from 'vs/base/common/objects'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -22,13 +19,14 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte import { memoize } from 'vs/base/common/decorators'; import { range } from 'vs/base/common/arrays'; import * as platform from 'vs/base/common/platform'; -import { listFocusBackground, pickerGroupBorder, pickerGroupForeground, activeContrastBorder, listFocusForeground } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; -import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils'; +import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { QUICK_INPUT_BACKGROUND } from 'vs/workbench/common/theme'; +import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'; +import { IListOptions, List, IListStyles, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -36,17 +34,22 @@ interface IListElement { readonly index: number; readonly item: IQuickPickItem; readonly saneLabel: string; + readonly saneAriaLabel: string; readonly saneDescription?: string; readonly saneDetail?: string; + readonly labelHighlights?: IMatch[]; + readonly descriptionHighlights?: IMatch[]; + readonly detailHighlights?: IMatch[]; readonly checked: boolean; readonly separator?: IQuickPickSeparator; readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; } -class ListElement implements IListElement { +class ListElement implements IListElement, IDisposable { index!: number; item!: IQuickPickItem; saneLabel!: string; + saneAriaLabel!: string; saneDescription?: string; saneDetail?: string; hidden = false; @@ -69,7 +72,11 @@ class ListElement implements IListElement { fireButtonTriggered!: (event: IQuickPickItemButtonEvent) => void; constructor(init: IListElement) { - assign(this, init); + Object.assign(this, init); + } + + dispose() { + this._onChecked.dispose(); } } @@ -77,6 +84,7 @@ interface IListElementTemplateData { entry: HTMLDivElement; checkbox: HTMLInputElement; label: IconLabel; + keybinding: KeybindingLabel; detail: HighlightedLabel; separator: HTMLDivElement; actionBar: ActionBar; @@ -102,6 +110,11 @@ class ListElementRenderer implements IListRenderer { + if (!data.checkbox.offsetParent) { // If checkbox not visible: + e.preventDefault(); // Prevent toggle of checkbox when it is immediately shown afterwards. #91740 + } + })); data.checkbox = dom.append(label, $('input.quick-input-list-checkbox')); data.checkbox.type = 'checkbox'; data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => { @@ -116,6 +129,10 @@ class ListElementRenderer implements IListRenderer s && parseCodicons(s).text) - .filter(s => !!s) - .join(', ')); - // Separator if (element.separator && element.separator.label) { data.separator.textContent = element.separator.label; @@ -163,18 +179,18 @@ class ListElementRenderer implements IListRenderer { - const action = new Action(`id-${index}`, '', button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined), true, () => { + let cssClasses = button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined); + if (button.alwaysVisible) { + cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible'; + } + const action = new Action(`id-${index}`, '', cssClasses, true, () => { element.fireButtonTriggered({ button, item: element.item @@ -184,9 +200,9 @@ class ListElementRenderer implements IListRenderer { } } +export enum QuickInputListFocus { + First = 1, + Second, + Last, + Next, + Previous, + NextPage, + PreviousPage +} + export class QuickInputList { readonly id: string; private container: HTMLElement; - private list: WorkbenchList; + private list: List; private inputElements: Array = []; private elements: ListElement[] = []; private elementsToIndexes = new Map(); @@ -233,6 +259,8 @@ export class QuickInputList { onChangedCheckedElements: Event = this._onChangedCheckedElements.event; private readonly _onButtonTriggered = new Emitter>(); onButtonTriggered = this._onButtonTriggered.event; + private readonly _onKeyDown = new Emitter(); + onKeyDown: Event = this._onKeyDown.event; private readonly _onLeave = new Emitter(); onLeave: Event = this._onLeave.event; private _fireCheckedEvents = true; @@ -242,21 +270,19 @@ export class QuickInputList { constructor( private parent: HTMLElement, id: string, - @IInstantiationService private readonly instantiationService: IInstantiationService + options: IQuickInputOptions, ) { this.id = id; this.container = dom.append(this.parent, $('.quick-input-list')); const delegate = new ListElementDelegate(); - this.list = this.instantiationService.createInstance(WorkbenchList, 'QuickInput', this.container, delegate, [new ListElementRenderer()], { + const accessibilityProvider = new QuickInputAccessibilityProvider(); + this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer()], { identityProvider: { getId: element => element.saneLabel }, - openController: { shouldOpen: () => false }, // Workaround #58124 setRowLineHeight: false, multipleSelectionSupport: false, horizontalScrolling: false, - overrideStyles: { - listBackground: QUICK_INPUT_BACKGROUND - } - } as IWorkbenchListOptions); + accessibilityProvider + } as IListOptions); this.list.getHTMLElement().id = id; this.disposables.push(this.list); this.disposables.push(this.list.onKeyDown(e => { @@ -271,20 +297,20 @@ export class QuickInputList { } break; case KeyCode.UpArrow: - case KeyCode.PageUp: const focus1 = this.list.getFocus(); if (focus1.length === 1 && focus1[0] === 0) { this._onLeave.fire(); } break; case KeyCode.DownArrow: - case KeyCode.PageDown: const focus2 = this.list.getFocus(); if (focus2.length === 1 && focus2[0] === this.list.length - 1) { this._onLeave.fire(); } break; } + + this._onKeyDown.fire(event); })); this.disposables.push(this.list.onMouseDown(e => { if (e.browserEvent.button !== 2) { @@ -297,16 +323,40 @@ export class QuickInputList { this._onLeave.fire(); } })); + this.disposables.push(this.list.onMouseMiddleClick(e => { + this._onLeave.fire(); + })); + this.disposables.push(this.list.onContextMenu(e => { + if (typeof e.index === 'number') { + e.browserEvent.preventDefault(); + + // we want to treat a context menu event as + // a gesture to open the item at the index + // since we do not have any context menu + // this enables for example macOS to Ctrl- + // click on an item to open it. + this.list.setSelection([e.index]); + } + })); + this.disposables.push( + this._onChangedAllVisibleChecked, + this._onChangedCheckedCount, + this._onChangedVisibleCount, + this._onChangedCheckedElements, + this._onButtonTriggered, + this._onLeave, + this._onKeyDown + ); } @memoize get onDidChangeFocus() { - return Event.map(this.list.onFocusChange, e => e.elements.map(e => e.item)); + return Event.map(this.list.onDidChangeFocus, e => e.elements.map(e => e.item)); } @memoize get onDidChangeSelection() { - return Event.map(this.list.onSelectionChange, e => e.elements.map(e => e.item)); + return Event.map(this.list.onDidChangeSelection, e => ({ items: e.elements.map(e => e.item), event: e.browserEvent })); } getAllVisibleChecked() { @@ -370,12 +420,24 @@ export class QuickInputList { this.elements = inputElements.reduce((result, item, index) => { if (item.type !== 'separator') { const previous = index && inputElements[index - 1]; + const saneLabel = item.label && item.label.replace(/\r?\n/g, ' '); + const saneDescription = item.description && item.description.replace(/\r?\n/g, ' '); + const saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' '); + const saneAriaLabel = item.ariaLabel || [saneLabel, saneDescription, saneDetail] + .map(s => s && parseCodicons(s).text) + .filter(s => !!s) + .join(', '); + result.push(new ListElement({ index, item, - saneLabel: item.label && item.label.replace(/\r?\n/g, ' '), - saneDescription: item.description && item.description.replace(/\r?\n/g, ' '), - saneDetail: item.detail && item.detail.replace(/\r?\n/g, ' '), + saneLabel, + saneAriaLabel, + saneDescription, + saneDetail, + labelHighlights: item.highlights?.label, + descriptionHighlights: item.highlights?.description, + detailHighlights: item.highlights?.detail, checked: false, separator: previous && previous.type === 'separator' ? previous : undefined, fireButtonTriggered @@ -383,6 +445,7 @@ export class QuickInputList { } return result; }, [] as ListElement[]); + this.elementDisposables.push(...this.elements); this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents()))); this.elementsToIndexes = this.elements.reduce((map, element, index) => { @@ -394,6 +457,10 @@ export class QuickInputList { this._onChangedVisibleCount.fire(this.elements.length); } + getElementsCount(): number { + return this.inputElements.length; + } + getFocusedElements() { return this.list.getFocusedElements() .map(e => e.item); @@ -404,7 +471,10 @@ export class QuickInputList { .filter(item => this.elementsToIndexes.has(item)) .map(item => this.elementsToIndexes.get(item)!)); if (items.length > 0) { - this.list.reveal(this.list.getFocus()[0]); + const focused = this.list.getFocus()[0]; + if (typeof focused === 'number') { + this.list.reveal(focused); + } } } @@ -445,23 +515,54 @@ export class QuickInputList { } set enabled(value: boolean) { - this.list.getHTMLElement().style.pointerEvents = value ? null : 'none'; + this.list.getHTMLElement().style.pointerEvents = value ? '' : 'none'; } - focus(what: 'First' | 'Last' | 'Next' | 'Previous' | 'NextPage' | 'PreviousPage'): void { + focus(what: QuickInputListFocus): void { if (!this.list.length) { return; } - if ((what === 'Next' || what === 'NextPage') && this.list.getFocus()[0] === this.list.length - 1) { - what = 'First'; - } - if ((what === 'Previous' || what === 'PreviousPage') && this.list.getFocus()[0] === 0) { - what = 'Last'; + if (what === QuickInputListFocus.Next && this.list.getFocus()[0] === this.list.length - 1) { + what = QuickInputListFocus.First; } - (this.list as any)['focus' + what](); - this.list.reveal(this.list.getFocus()[0]); + if (what === QuickInputListFocus.Previous && this.list.getFocus()[0] === 0) { + what = QuickInputListFocus.Last; + } + + if (what === QuickInputListFocus.Second && this.list.length < 2) { + what = QuickInputListFocus.First; + } + + switch (what) { + case QuickInputListFocus.First: + this.list.focusFirst(); + break; + case QuickInputListFocus.Second: + this.list.focusNth(1); + break; + case QuickInputListFocus.Last: + this.list.focusLast(); + break; + case QuickInputListFocus.Next: + this.list.focusNext(); + break; + case QuickInputListFocus.Previous: + this.list.focusPrevious(); + break; + case QuickInputListFocus.NextPage: + this.list.focusNextPage(); + break; + case QuickInputListFocus.PreviousPage: + this.list.focusPreviousPage(); + break; + } + + const focused = this.list.getFocus()[0]; + if (typeof focused === 'number') { + this.list.reveal(focused); + } } clearFocus() { @@ -477,9 +578,10 @@ export class QuickInputList { this.list.layout(); } - filter(query: string) { + filter(query: string): boolean { if (!(this.sortByLabel || this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) { - return; + this.list.layout(); + return false; } query = query.trim(); @@ -537,6 +639,8 @@ export class QuickInputList { this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); this._onChangedVisibleCount.fire(shownElements.length); + + return true; } toggleCheckbox() { @@ -577,6 +681,10 @@ export class QuickInputList { private fireButtonTriggered(event: IQuickPickItemButtonEvent) { this._onButtonTriggered.fire(event); } + + style(styles: IListStyles) { + this.list.style(styles); + } } function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number { @@ -591,32 +699,28 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s return 1; } + if (labelHighlightsA.length === 0 && labelHighlightsB.length === 0) { + return 0; + } + return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor); } -registerThemingParticipant((theme, collector) => { - // Override inactive focus foreground with active focus foreground for single-pick case. - const listInactiveFocusForeground = theme.getColor(listFocusForeground); - if (listInactiveFocusForeground) { - collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { color: ${listInactiveFocusForeground}; }`); +class QuickInputAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return localize('quickInput', "Quick Input"); } - // Override inactive focus background with active focus background for single-pick case. - const listInactiveFocusBackground = theme.getColor(listFocusBackground); - if (listInactiveFocusBackground) { - collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color: ${listInactiveFocusBackground}; }`); - collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color: ${listInactiveFocusBackground}; }`); + + getAriaLabel(element: ListElement): string | null { + return element.saneAriaLabel; } - // dotted instead of solid (as in listWidget.ts) to match QuickOpen - const activeContrast = theme.getColor(activeContrastBorder); - if (activeContrast) { - collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { outline: 1px dotted ${activeContrast}; outline-offset: -1px; }`); + + getWidgetRole() { + return 'listbox'; } - const pickerGroupBorderColor = theme.getColor(pickerGroupBorder); - if (pickerGroupBorderColor) { - collector.addRule(`.quick-input-list .quick-input-list-entry { border-top-color: ${pickerGroupBorderColor}; }`); + + getRole() { + return 'option'; } - const pickerGroupForegroundColor = theme.getColor(pickerGroupForeground); - if (pickerGroupForegroundColor) { - collector.addRule(`.quick-input-list .quick-input-list-separator { color: ${pickerGroupForegroundColor}; }`); - } -}); +} diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts b/src/vs/base/parts/quickinput/browser/quickInputUtils.ts similarity index 100% rename from src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts rename to src/vs/base/parts/quickinput/browser/quickInputUtils.ts diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts new file mode 100644 index 00000000000..5312368257c --- /dev/null +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -0,0 +1,365 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IMatch } from 'vs/base/common/filters'; +import { IItemAccessor } from 'vs/base/common/fuzzyScorer'; +import { Schemas } from 'vs/base/common/network'; + +export interface IQuickPickItemHighlights { + label?: IMatch[]; + description?: IMatch[]; + detail?: IMatch[]; +} + +export interface IQuickPickItem { + type?: 'item'; + id?: string; + label: string; + ariaLabel?: string; + description?: string; + detail?: string; + /** + * Allows to show a keybinding next to the item to indicate + * how the item can be triggered outside of the picker using + * keyboard shortcut. + */ + keybinding?: ResolvedKeybinding; + iconClasses?: string[]; + italic?: boolean; + strikethrough?: boolean; + highlights?: IQuickPickItemHighlights; + buttons?: IQuickInputButton[]; + picked?: boolean; + alwaysShow?: boolean; +} + +export interface IQuickPickSeparator { + type: 'separator'; + label?: string; +} + +export interface IKeyMods { + readonly ctrlCmd: boolean; + readonly alt: boolean; +} + +export const NO_KEY_MODS: IKeyMods = { ctrlCmd: false, alt: false }; + +export interface IQuickNavigateConfiguration { + keybindings: ResolvedKeybinding[]; +} + +export interface IPickOptions { + + /** + * an optional string to show as placeholder in the input box to guide the user what she picks on + */ + placeHolder?: string; + + /** + * an optional flag to include the description when filtering the picks + */ + matchOnDescription?: boolean; + + /** + * an optional flag to include the detail when filtering the picks + */ + matchOnDetail?: boolean; + + /** + * an optional flag to filter the picks based on label. Defaults to true. + */ + matchOnLabel?: boolean; + + /** + * an option flag to control whether focus is always automatically brought to a list item. Defaults to true. + */ + autoFocusOnList?: boolean; + + /** + * an optional flag to not close the picker on focus lost + */ + ignoreFocusLost?: boolean; + + /** + * an optional flag to make this picker multi-select + */ + canPickMany?: boolean; + + /** + * enables quick navigate in the picker to open an element without typing + */ + quickNavigate?: IQuickNavigateConfiguration; + + /** + * a context key to set when this picker is active + */ + contextKey?: string; + + /** + * an optional property for the item to focus initially. + */ + activeItem?: Promise | T; + + onKeyMods?: (keyMods: IKeyMods) => void; + onDidFocus?: (entry: T) => void; + onDidTriggerItemButton?: (context: IQuickPickItemButtonContext) => void; +} + +export interface IInputOptions { + + /** + * the value to prefill in the input box + */ + value?: string; + + /** + * the selection of value, default to the whole word + */ + valueSelection?: [number, number]; + + /** + * the text to display underneath the input box + */ + prompt?: string; + + /** + * an optional string to show as placeholder in the input box to guide the user what to type + */ + placeHolder?: string; + + /** + * Controls if a password input is shown. Password input hides the typed text. + */ + password?: boolean; + + ignoreFocusLost?: boolean; + + /** + * an optional function that is used to validate user input. + */ + validateInput?: (input: string) => Promise; +} + +export interface IQuickInput extends IDisposable { + + readonly onDidHide: Event; + readonly onDispose: Event; + + title: string | undefined; + + description: string | undefined; + + step: number | undefined; + + totalSteps: number | undefined; + + enabled: boolean; + + contextKey: string | undefined; + + busy: boolean; + + ignoreFocusOut: boolean; + + show(): void; + + hide(): void; +} + +export interface IQuickPickAcceptEvent { + + /** + * Signals if the picker item is to be accepted + * in the background while keeping the picker open. + */ + inBackground: boolean; +} + +export enum ItemActivation { + NONE, + FIRST, + SECOND, + LAST +} + +export interface IQuickPick extends IQuickInput { + + value: string; + + /** + * A method that allows to massage the value used + * for filtering, e.g, to remove certain parts. + */ + filterValue: (value: string) => string; + + ariaLabel: string | undefined; + + placeholder: string | undefined; + + readonly onDidChangeValue: Event; + + readonly onDidAccept: Event; + + /** + * If enabled, will fire the `onDidAccept` event when + * pressing the arrow-right key with the idea of accepting + * the selected item without closing the picker. + */ + canAcceptInBackground: boolean; + + ok: boolean | 'default'; + + readonly onDidCustom: Event; + + customButton: boolean; + + customLabel: string | undefined; + + customHover: string | undefined; + + buttons: ReadonlyArray; + + readonly onDidTriggerButton: Event; + + readonly onDidTriggerItemButton: Event>; + + items: ReadonlyArray; + + canSelectMany: boolean; + + matchOnDescription: boolean; + + matchOnDetail: boolean; + + matchOnLabel: boolean; + + sortByLabel: boolean; + + autoFocusOnList: boolean; + + quickNavigate: IQuickNavigateConfiguration | undefined; + + activeItems: ReadonlyArray; + + readonly onDidChangeActive: Event; + + /** + * Allows to control which entry should be activated by default. + */ + itemActivation: ItemActivation; + + selectedItems: ReadonlyArray; + + readonly onDidChangeSelection: Event; + + readonly keyMods: IKeyMods; + + valueSelection: Readonly<[number, number]> | undefined; + + validationMessage: string | undefined; + + inputHasFocus(): boolean; + + focusOnInput(): void; + + /** + * Hides the input box from the picker UI. This is typically used + * in combination with quick-navigation where no search UI should + * be presented. + */ + hideInput: boolean; + + hideCheckAll: boolean; +} + +export interface IInputBox extends IQuickInput { + + value: string; + + valueSelection: Readonly<[number, number]> | undefined; + + placeholder: string | undefined; + + password: boolean; + + readonly onDidChangeValue: Event; + + readonly onDidAccept: Event; + + buttons: ReadonlyArray; + + readonly onDidTriggerButton: Event; + + prompt: string | undefined; + + validationMessage: string | undefined; +} + +export interface IQuickInputButton { + /** iconPath or iconClass required */ + iconPath?: { dark: URI; light?: URI; }; + /** iconPath or iconClass required */ + iconClass?: string; + tooltip?: string; + /** + * Whether to always show the button. By default buttons + * are only visible when hovering over them with the mouse + */ + alwaysVisible?: boolean; +} + +export interface IQuickPickItemButtonEvent { + button: IQuickInputButton; + item: T; +} + +export interface IQuickPickItemButtonContext extends IQuickPickItemButtonEvent { + removeItem(): void; +} + +export type QuickPickInput = T | IQuickPickSeparator; + + +//region Fuzzy Scorer Support + +export type IQuickPickItemWithResource = IQuickPickItem & { resource?: URI }; + +export class QuickPickItemScorerAccessor implements IItemAccessor { + + constructor(private options?: { skipDescription?: boolean, skipPath?: boolean }) { } + + getItemLabel(entry: IQuickPickItemWithResource): string { + return entry.label; + } + + getItemDescription(entry: IQuickPickItemWithResource): string | undefined { + if (this.options?.skipDescription) { + return undefined; + } + + return entry.description; + } + + getItemPath(entry: IQuickPickItemWithResource): string | undefined { + if (this.options?.skipPath) { + return undefined; + } + + if (entry.resource?.scheme === Schemas.file) { + return entry.resource.fsPath; + } + + return entry.resource?.path; + } +} + +export const quickPickItemScorerAccessor = new QuickPickItemScorerAccessor(); + +//#endregion diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts deleted file mode 100644 index b846129db46..00000000000 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ /dev/null @@ -1,578 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import * as types from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; -import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; -import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; -import * as DOM from 'vs/base/browser/dom'; -import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; -import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; -import { OS } from 'vs/base/common/platform'; -import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { IItemAccessor } from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { coalesce } from 'vs/base/common/arrays'; -import { IMatch } from 'vs/base/common/filters'; - -export interface IContext { - event: any; - quickNavigateConfiguration: IQuickNavigateConfiguration; -} - -export interface IHighlight extends IMatch { - start: number; - end: number; -} - -let IDS = 0; - -export class QuickOpenItemAccessorClass implements IItemAccessor { - - getItemLabel(entry: QuickOpenEntry): string | null { - return types.withUndefinedAsNull(entry.getLabel()); - } - - getItemDescription(entry: QuickOpenEntry): string | null { - return types.withUndefinedAsNull(entry.getDescription()); - } - - getItemPath(entry: QuickOpenEntry): string | undefined { - const resource = entry.getResource(); - - return resource ? resource.fsPath : undefined; - } -} - -export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass(); - -export class QuickOpenEntry { - private id: string; - private labelHighlights?: IHighlight[]; - private descriptionHighlights?: IHighlight[]; - private detailHighlights?: IHighlight[]; - private hidden: boolean | undefined; - - constructor(highlights: IHighlight[] = []) { - this.id = (IDS++).toString(); - this.labelHighlights = highlights; - this.descriptionHighlights = []; - } - - /** - * A unique identifier for the entry - */ - getId(): string { - return this.id; - } - - /** - * The label of the entry to identify it from others in the list - */ - getLabel(): string | undefined { - return undefined; - } - - /** - * The options for the label to use for this entry - */ - getLabelOptions(): IIconLabelValueOptions | undefined { - return undefined; - } - - /** - * The label of the entry to use when a screen reader wants to read about the entry - */ - getAriaLabel(): string { - return coalesce([this.getLabel(), this.getDescription(), this.getDetail()]) - .join(', '); - } - - /** - * Detail information about the entry that is optional and can be shown below the label - */ - getDetail(): string | undefined { - return undefined; - } - - /** - * The icon of the entry to identify it from others in the list - */ - getIcon(): string | undefined { - return undefined; - } - - /** - * A secondary description that is optional and can be shown right to the label - */ - getDescription(): string | undefined { - return undefined; - } - - /** - * A tooltip to show when hovering over the entry. - */ - getTooltip(): string | undefined { - return undefined; - } - - /** - * A tooltip to show when hovering over the description portion of the entry. - */ - getDescriptionTooltip(): string | undefined { - return undefined; - } - - /** - * An optional keybinding to show for an entry. - */ - getKeybinding(): ResolvedKeybinding | undefined { - return undefined; - } - - /** - * A resource for this entry. Resource URIs can be used to compare different kinds of entries and group - * them together. - */ - getResource(): URI | undefined { - return undefined; - } - - /** - * Allows to reuse the same model while filtering. Hidden entries will not show up in the viewer. - */ - isHidden(): boolean { - return !!this.hidden; - } - - /** - * Allows to reuse the same model while filtering. Hidden entries will not show up in the viewer. - */ - setHidden(hidden: boolean): void { - this.hidden = hidden; - } - - /** - * Allows to set highlight ranges that should show up for the entry label and optionally description if set. - */ - setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void { - this.labelHighlights = labelHighlights; - this.descriptionHighlights = descriptionHighlights; - this.detailHighlights = detailHighlights; - } - - /** - * Allows to return highlight ranges that should show up for the entry label and description. - */ - getHighlights(): [IHighlight[] | undefined /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { - return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights]; - } - - /** - * Called when the entry is selected for opening. Returns a boolean value indicating if an action was performed or not. - * The mode parameter gives an indication if the element is previewed (using arrow keys) or opened. - * - * The context parameter provides additional context information how the run was triggered. - */ - run(mode: Mode, context: IEntryRunContext): boolean { - return false; - } - - /** - * Determines if this quick open entry should merge with the editor history in quick open. If set to true - * and the resource of this entry is the same as the resource for an editor history, it will not show up - * because it is considered to be a duplicate of an editor history. - */ - mergeWithEditorHistory(): boolean { - return false; - } -} - -export class QuickOpenEntryGroup extends QuickOpenEntry { - private entry?: QuickOpenEntry; - private groupLabel?: string; - private withBorder?: boolean; - - constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean) { - super(); - - this.entry = entry; - this.groupLabel = groupLabel; - this.withBorder = withBorder; - } - - /** - * The label of the group or null if none. - */ - getGroupLabel(): string | undefined { - return this.groupLabel; - } - - setGroupLabel(groupLabel: string | undefined): void { - this.groupLabel = groupLabel; - } - - /** - * Whether to show a border on top of the group entry or not. - */ - showBorder(): boolean { - return !!this.withBorder; - } - - setShowBorder(showBorder: boolean): void { - this.withBorder = showBorder; - } - - getLabel(): string | undefined { - return this.entry ? this.entry.getLabel() : super.getLabel(); - } - - getLabelOptions(): IIconLabelValueOptions | undefined { - return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions(); - } - - getAriaLabel(): string { - return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel(); - } - - getDetail(): string | undefined { - return this.entry ? this.entry.getDetail() : super.getDetail(); - } - - getResource(): URI | undefined { - return this.entry ? this.entry.getResource() : super.getResource(); - } - - getIcon(): string | undefined { - return this.entry ? this.entry.getIcon() : super.getIcon(); - } - - getDescription(): string | undefined { - return this.entry ? this.entry.getDescription() : super.getDescription(); - } - - getEntry(): QuickOpenEntry | undefined { - return this.entry; - } - - getHighlights(): [IHighlight[] | undefined, IHighlight[] | undefined, IHighlight[] | undefined] { - return this.entry ? this.entry.getHighlights() : super.getHighlights(); - } - - isHidden(): boolean { - return this.entry ? this.entry.isHidden() : super.isHidden(); - } - - setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void { - this.entry ? this.entry.setHighlights(labelHighlights, descriptionHighlights, detailHighlights) : super.setHighlights(labelHighlights, descriptionHighlights, detailHighlights); - } - - setHidden(hidden: boolean): void { - this.entry ? this.entry.setHidden(hidden) : super.setHidden(hidden); - } - - run(mode: Mode, context: IEntryRunContext): boolean { - return this.entry ? this.entry.run(mode, context) : super.run(mode, context); - } -} - -class NoActionProvider implements IActionProvider { - - hasActions(tree: ITree, element: any): boolean { - return false; - } - - getActions(tree: ITree, element: any): IAction[] | null { - return null; - } -} - -export interface IQuickOpenEntryTemplateData { - container: HTMLElement; - entry: HTMLElement; - icon: HTMLSpanElement; - label: IconLabel; - detail: HighlightedLabel; - keybinding: KeybindingLabel; - actionBar: ActionBar; -} - -export interface IQuickOpenEntryGroupTemplateData extends IQuickOpenEntryTemplateData { - group?: HTMLDivElement; -} - -const templateEntry = 'quickOpenEntry'; -const templateEntryGroup = 'quickOpenEntryGroup'; - -class Renderer implements IRenderer { - - private actionProvider: IActionProvider; - private actionRunner?: IActionRunner; - - constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner?: IActionRunner) { - this.actionProvider = actionProvider; - this.actionRunner = actionRunner; - } - - getHeight(entry: QuickOpenEntry): number { - if (entry.getDetail()) { - return 44; - } - - return 22; - } - - getTemplateId(entry: QuickOpenEntry): string { - if (entry instanceof QuickOpenEntryGroup) { - return templateEntryGroup; - } - - return templateEntry; - } - - renderTemplate(templateId: string, container: HTMLElement, styles: IQuickOpenStyles): IQuickOpenEntryGroupTemplateData { - const entryContainer = document.createElement('div'); - DOM.addClass(entryContainer, 'sub-content'); - container.appendChild(entryContainer); - - // Entry - const row1 = DOM.$('.quick-open-row'); - const row2 = DOM.$('.quick-open-row'); - const entry = DOM.$('.quick-open-entry', undefined, row1, row2); - entryContainer.appendChild(entry); - - // Icon - const icon = document.createElement('span'); - row1.appendChild(icon); - - // Label - const label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true }); - - // Keybinding - const keybindingContainer = document.createElement('span'); - row1.appendChild(keybindingContainer); - DOM.addClass(keybindingContainer, 'quick-open-entry-keybinding'); - const keybinding = new KeybindingLabel(keybindingContainer, OS); - - // Detail - const detailContainer = document.createElement('div'); - row2.appendChild(detailContainer); - DOM.addClass(detailContainer, 'quick-open-entry-meta'); - const detail = new HighlightedLabel(detailContainer, true); - - // Entry Group - let group: HTMLDivElement | undefined; - if (templateId === templateEntryGroup) { - group = document.createElement('div'); - DOM.addClass(group, 'results-group'); - container.appendChild(group); - } - - // Actions - DOM.addClass(container, 'actions'); - - const actionBarContainer = document.createElement('div'); - DOM.addClass(actionBarContainer, 'primary-action-bar'); - container.appendChild(actionBarContainer); - - const actionBar = new ActionBar(actionBarContainer, { - actionRunner: this.actionRunner - }); - - return { - container, - entry, - icon, - label, - detail, - keybinding, - group, - actionBar - }; - } - - renderElement(entry: QuickOpenEntry, templateId: string, data: IQuickOpenEntryGroupTemplateData, styles: IQuickOpenStyles): void { - - // Action Bar - if (this.actionProvider.hasActions(null, entry)) { - DOM.addClass(data.container, 'has-actions'); - } else { - DOM.removeClass(data.container, 'has-actions'); - } - - data.actionBar.context = entry; // make sure the context is the current element - - const actions = this.actionProvider.getActions(null, entry); - if (data.actionBar.isEmpty() && actions && actions.length > 0) { - data.actionBar.push(actions, { icon: true, label: false }); - } else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) { - data.actionBar.clear(); - } - - // Entry group class - if (entry instanceof QuickOpenEntryGroup && entry.getGroupLabel()) { - DOM.addClass(data.container, 'has-group-label'); - } else { - DOM.removeClass(data.container, 'has-group-label'); - } - - // Entry group - if (entry instanceof QuickOpenEntryGroup) { - const group = entry; - const groupData = data; - - // Border - if (group.showBorder()) { - DOM.addClass(groupData.container, 'results-group-separator'); - if (styles.pickerGroupBorder) { - groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString(); - } - } else { - DOM.removeClass(groupData.container, 'results-group-separator'); - groupData.container.style.borderTopColor = ''; - } - - // Group Label - const groupLabel = group.getGroupLabel() || ''; - if (groupData.group) { - groupData.group.textContent = groupLabel; - if (styles.pickerGroupForeground) { - groupData.group.style.color = styles.pickerGroupForeground.toString(); - } - } - } - - // Normal Entry - if (entry instanceof QuickOpenEntry) { - const [labelHighlights, descriptionHighlights, detailHighlights] = entry.getHighlights(); - - // Icon - const iconClass = entry.getIcon() ? ('quick-open-entry-icon ' + entry.getIcon()) : ''; - data.icon.className = iconClass; - - // Label - const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null); - options.matches = labelHighlights || []; - options.title = entry.getTooltip(); - options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow - options.descriptionMatches = descriptionHighlights || []; - data.label.setLabel(entry.getLabel() || '', entry.getDescription(), options); - - // Meta - data.detail.set(entry.getDetail(), detailHighlights); - - // Keybinding - data.keybinding.set(entry.getKeybinding()!); - } - } - - disposeTemplate(templateId: string, templateData: IQuickOpenEntryGroupTemplateData): void { - templateData.actionBar.dispose(); - templateData.actionBar = null!; - templateData.container = null!; - templateData.entry = null!; - templateData.keybinding = null!; - templateData.detail = null!; - templateData.group = null!; - templateData.icon = null!; - templateData.label.dispose(); - templateData.label = null!; - } -} - -export class QuickOpenModel implements - IModel, - IDataSource, - IFilter, - IRunner, - IAccessiblityProvider -{ - private _entries: QuickOpenEntry[]; - private _dataSource: IDataSource; - private _renderer: IRenderer; - private _filter: IFilter; - private _runner: IRunner; - private _accessibilityProvider: IAccessiblityProvider; - - constructor(entries: QuickOpenEntry[] = [], actionProvider: IActionProvider = new NoActionProvider()) { - this._entries = entries; - this._dataSource = this; - this._renderer = new Renderer(actionProvider); - this._filter = this; - this._runner = this; - this._accessibilityProvider = this; - } - - get entries() { return this._entries; } - get dataSource() { return this._dataSource; } - get renderer() { return this._renderer; } - get filter() { return this._filter; } - get runner() { return this._runner; } - get accessibilityProvider() { return this._accessibilityProvider; } - - set entries(entries: QuickOpenEntry[]) { - this._entries = entries; - } - - /** - * Adds entries that should show up in the quick open viewer. - */ - addEntries(entries: QuickOpenEntry[]): void { - if (types.isArray(entries)) { - this._entries = this._entries.concat(entries); - } - } - - /** - * Set the entries that should show up in the quick open viewer. - */ - setEntries(entries: QuickOpenEntry[]): void { - if (types.isArray(entries)) { - this._entries = entries; - } - } - - /** - * Get the entries that should show up in the quick open viewer. - * - * @visibleOnly optional parameter to only return visible entries - */ - getEntries(visibleOnly?: boolean): QuickOpenEntry[] { - if (visibleOnly) { - return this._entries.filter((e) => !e.isHidden()); - } - - return this._entries; - } - - getId(entry: QuickOpenEntry): string { - return entry.getId(); - } - - getLabel(entry: QuickOpenEntry): string | null { - return types.withUndefinedAsNull(entry.getLabel()); - } - - getAriaLabel(entry: QuickOpenEntry): string { - const ariaLabel = entry.getAriaLabel(); - if (ariaLabel) { - return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel()); - } - - return nls.localize('quickOpenAriaLabel', "picker"); - } - - isVisible(entry: QuickOpenEntry): boolean { - return !entry.isHidden(); - } - - run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean { - return entry.run(mode, context); - } -} diff --git a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts deleted file mode 100644 index a87b30dbadc..00000000000 --- a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts +++ /dev/null @@ -1,142 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { isFunction } from 'vs/base/common/types'; -import { ITree, IRenderer, IFilter, IDataSource, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree'; -import { IModel } from 'vs/base/parts/quickopen/common/quickOpen'; -import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; - -export interface IModelProvider { - getModel(): IModel; -} - -export class DataSource implements IDataSource { - - private modelProvider: IModelProvider; - - constructor(model: IModel); - constructor(modelProvider: IModelProvider); - constructor(arg: any) { - this.modelProvider = isFunction(arg.getModel) ? arg : { getModel: () => arg }; - } - - getId(tree: ITree, element: any): string { - if (!element) { - return null!; - } - - const model = this.modelProvider.getModel(); - return model === element ? '__root__' : model.dataSource.getId(element); - } - - hasChildren(tree: ITree, element: any): boolean { - const model = this.modelProvider.getModel(); - return !!(model && model === element && model.entries.length > 0); - } - - getChildren(tree: ITree, element: any): Promise { - const model = this.modelProvider.getModel(); - return Promise.resolve(model === element ? model.entries : []); - } - - getParent(tree: ITree, element: any): Promise { - return Promise.resolve(null); - } -} - -export class AccessibilityProvider implements IAccessibilityProvider { - constructor(private modelProvider: IModelProvider) { } - - getAriaLabel(tree: ITree, element: any): string | null { - const model = this.modelProvider.getModel(); - - return model.accessibilityProvider ? model.accessibilityProvider.getAriaLabel(element) : null; - } - - getPosInSet(tree: ITree, element: any): string { - const model = this.modelProvider.getModel(); - let i = 0; - if (model.filter) { - for (const entry of model.entries) { - if (model.filter.isVisible(entry)) { - i++; - } - if (entry === element) { - break; - } - } - } else { - i = model.entries.indexOf(element) + 1; - } - return String(i); - } - - getSetSize(): string { - const model = this.modelProvider.getModel(); - let n = 0; - if (model.filter) { - for (const entry of model.entries) { - if (model.filter.isVisible(entry)) { - n++; - } - } - } else { - n = model.entries.length; - } - return String(n); - } -} - -export class Filter implements IFilter { - - constructor(private modelProvider: IModelProvider) { } - - isVisible(tree: ITree, element: any): boolean { - const model = this.modelProvider.getModel(); - - if (!model.filter) { - return true; - } - - return model.filter.isVisible(element); - } -} - -export class Renderer implements IRenderer { - private styles: IQuickOpenStyles; - - constructor(private modelProvider: IModelProvider, styles: IQuickOpenStyles) { - this.styles = styles; - } - - updateStyles(styles: IQuickOpenStyles): void { - this.styles = styles; - } - - getHeight(tree: ITree, element: any): number { - const model = this.modelProvider.getModel(); - return model.renderer.getHeight(element); - } - - getTemplateId(tree: ITree, element: any): string { - const model = this.modelProvider.getModel(); - return model.renderer.getTemplateId(element); - } - - renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { - const model = this.modelProvider.getModel(); - return model.renderer.renderTemplate(templateId, container, this.styles); - } - - renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { - const model = this.modelProvider.getModel(); - model.renderer.renderElement(element, templateId, templateData, this.styles); - } - - disposeTemplate(tree: ITree, templateId: string, templateData: any): void { - const model = this.modelProvider.getModel(); - model.renderer.disposeTemplate(templateId, templateData); - } -} diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts deleted file mode 100644 index 7f2761f808a..00000000000 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ /dev/null @@ -1,1040 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./quickopen'; -import * as nls from 'vs/nls'; -import * as platform from 'vs/base/common/platform'; -import * as types from 'vs/base/common/types'; -import { IQuickNavigateConfiguration, IAutoFocus, IEntryRunContext, IModel, Mode, IKeyMods } from 'vs/base/parts/quickopen/common/quickOpen'; -import { Filter, Renderer, DataSource, IModelProvider, AccessibilityProvider } from 'vs/base/parts/quickopen/browser/quickOpenViewer'; -import { ITree, ContextMenuEvent, IActionProvider, ITreeStyles, ITreeOptions, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; -import { InputBox, MessageType, IInputBoxStyles, IRange } from 'vs/base/browser/ui/inputbox/inputBox'; -import Severity from 'vs/base/common/severity'; -import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { DefaultController, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; -import * as DOM from 'vs/base/browser/dom'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Color } from 'vs/base/common/color'; -import { mixin } from 'vs/base/common/objects'; -import { StandardMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IThemable } from 'vs/base/common/styler'; - -export interface IQuickOpenCallbacks { - onOk: () => void; - onCancel: () => void; - onType: (value: string) => void; - onShow?: () => void; - onHide?: (reason: HideReason) => void; - onFocusLost?: () => boolean /* veto close */; -} - -export interface IQuickOpenOptions extends IQuickOpenStyles { - minItemsToShow?: number; - maxItemsToShow?: number; - inputPlaceHolder?: string; - inputAriaLabel?: string; - actionProvider?: IActionProvider; - keyboardSupport?: boolean; - treeCreator?: (container: HTMLElement, configuration: ITreeConfiguration, options?: ITreeOptions) => ITree; -} - -export interface IQuickOpenStyles extends IInputBoxStyles, ITreeStyles { - background?: Color; - foreground?: Color; - borderColor?: Color; - pickerGroupForeground?: Color; - pickerGroupBorder?: Color; - widgetShadow?: Color; - progressBarBackground?: Color; -} - -export interface IShowOptions { - quickNavigateConfiguration?: IQuickNavigateConfiguration; - autoFocus?: IAutoFocus; - inputSelection?: IRange; - value?: string; -} - -export class QuickOpenController extends DefaultController { - - onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean { - if (platform.isMacintosh) { - return this.onLeftClick(tree, element, event); // https://github.com/Microsoft/vscode/issues/1011 - } - - return super.onContextMenu(tree, element, event); - } - - onMouseMiddleClick(tree: ITree, element: any, event: IMouseEvent): boolean { - return this.onLeftClick(tree, element, event); - } -} - -export const enum HideReason { - ELEMENT_SELECTED, - FOCUS_LOST, - CANCELED -} - -const defaultStyles = { - background: Color.fromHex('#1E1E1E'), - foreground: Color.fromHex('#CCCCCC'), - pickerGroupForeground: Color.fromHex('#0097FB'), - pickerGroupBorder: Color.fromHex('#3F3F46'), - widgetShadow: Color.fromHex('#000000'), - progressBarBackground: Color.fromHex('#0E70C0') -}; - -const DEFAULT_INPUT_ARIA_LABEL = nls.localize('quickOpenAriaLabel', "Quick picker. Type to narrow down results."); - -export class QuickOpenWidget extends Disposable implements IModelProvider, IThemable { - - private static readonly MAX_WIDTH = 600; // Max total width of quick open widget - private static readonly MAX_ITEMS_HEIGHT = 20 * 22; // Max height of item list below input field - - private isDisposed: boolean; - private options: IQuickOpenOptions; - // @ts-ignore (legacy widget - to be replaced with quick input) - private element: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private tree: ITree; - // @ts-ignore (legacy widget - to be replaced with quick input) - private inputBox: InputBox; - // @ts-ignore (legacy widget - to be replaced with quick input) - private inputContainer: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private helpText: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private resultCount: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private treeContainer: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private progressBar: ProgressBar; - // @ts-ignore (legacy widget - to be replaced with quick input) - private visible: boolean; - // @ts-ignore (legacy widget - to be replaced with quick input) - private isLoosingFocus: boolean; - private callbacks: IQuickOpenCallbacks; - private quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; - private container: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private treeElement: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private inputElement: HTMLElement; - // @ts-ignore (legacy widget - to be replaced with quick input) - private layoutDimensions: DOM.Dimension; - private model: IModel | null; - private inputChangingTimeoutHandle: any; - // @ts-ignore (legacy widget - to be replaced with quick input) - private styles: IQuickOpenStyles; - // @ts-ignore (legacy widget - to be replaced with quick input) - private renderer: Renderer; - - constructor(container: HTMLElement, callbacks: IQuickOpenCallbacks, options: IQuickOpenOptions) { - super(); - - this.isDisposed = false; - this.container = container; - this.callbacks = callbacks; - this.options = options; - this.styles = options || Object.create(null); - mixin(this.styles, defaultStyles, false); - this.model = null; - } - - getElement(): HTMLElement { - return this.element; - } - - getModel(): IModel { - return this.model!; - } - - setCallbacks(callbacks: IQuickOpenCallbacks): void { - this.callbacks = callbacks; - } - - create(): HTMLElement { - - // Container - this.element = document.createElement('div'); - DOM.addClass(this.element, 'monaco-quick-open-widget'); - this.container.appendChild(this.element); - - this._register(DOM.addDisposableListener(this.element, DOM.EventType.CONTEXT_MENU, e => DOM.EventHelper.stop(e, true))); // Do this to fix an issue on Mac where the menu goes into the way - this._register(DOM.addDisposableListener(this.element, DOM.EventType.FOCUS, e => this.gainingFocus(), true)); - this._register(DOM.addDisposableListener(this.element, DOM.EventType.BLUR, e => this.loosingFocus(e), true)); - this._register(DOM.addDisposableListener(this.element, DOM.EventType.KEY_DOWN, e => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); - if (keyboardEvent.keyCode === KeyCode.Escape) { - DOM.EventHelper.stop(e, true); - - this.hide(HideReason.CANCELED); - } else if (keyboardEvent.keyCode === KeyCode.Tab && !keyboardEvent.altKey && !keyboardEvent.ctrlKey && !keyboardEvent.metaKey) { - const stops = (e.currentTarget as HTMLElement).querySelectorAll('input, .monaco-tree, .monaco-tree-row.focused .action-label.icon') as NodeListOf; - if (keyboardEvent.shiftKey && keyboardEvent.target === stops[0]) { - DOM.EventHelper.stop(e, true); - stops[stops.length - 1].focus(); - } else if (!keyboardEvent.shiftKey && keyboardEvent.target === stops[stops.length - 1]) { - DOM.EventHelper.stop(e, true); - stops[0].focus(); - } - } - })); - - // Progress Bar - this.progressBar = this._register(new ProgressBar(this.element, { progressBarBackground: this.styles.progressBarBackground })); - this.progressBar.hide(); - - // Input Field - this.inputContainer = document.createElement('div'); - DOM.addClass(this.inputContainer, 'quick-open-input'); - this.element.appendChild(this.inputContainer); - - this.inputBox = this._register(new InputBox(this.inputContainer, undefined, { - placeholder: this.options.inputPlaceHolder || '', - ariaLabel: DEFAULT_INPUT_ARIA_LABEL, - inputBackground: this.styles.inputBackground, - inputForeground: this.styles.inputForeground, - inputBorder: this.styles.inputBorder, - inputValidationInfoBackground: this.styles.inputValidationInfoBackground, - inputValidationInfoForeground: this.styles.inputValidationInfoForeground, - inputValidationInfoBorder: this.styles.inputValidationInfoBorder, - inputValidationWarningBackground: this.styles.inputValidationWarningBackground, - inputValidationWarningForeground: this.styles.inputValidationWarningForeground, - inputValidationWarningBorder: this.styles.inputValidationWarningBorder, - inputValidationErrorBackground: this.styles.inputValidationErrorBackground, - inputValidationErrorForeground: this.styles.inputValidationErrorForeground, - inputValidationErrorBorder: this.styles.inputValidationErrorBorder - })); - - this.inputElement = this.inputBox.inputElement; - this.inputElement.setAttribute('role', 'combobox'); - this.inputElement.setAttribute('aria-haspopup', 'false'); - this.inputElement.setAttribute('aria-autocomplete', 'list'); - - this._register(DOM.addDisposableListener(this.inputBox.inputElement, DOM.EventType.INPUT, (e: Event) => this.onType())); - this._register(DOM.addDisposableListener(this.inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); - const shouldOpenInBackground = this.shouldOpenInBackground(keyboardEvent); - - // Do not handle Tab: It is used to navigate between elements without mouse - if (keyboardEvent.keyCode === KeyCode.Tab) { - return; - } - - // Pass tree navigation keys to the tree but leave focus in input field - else if (keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { - DOM.EventHelper.stop(e, true); - - this.navigateInTree(keyboardEvent.keyCode, keyboardEvent.shiftKey); - - // Position cursor at the end of input to allow right arrow (open in background) - // to function immediately unless the user has made a selection - if (this.inputBox.inputElement.selectionStart === this.inputBox.inputElement.selectionEnd) { - this.inputBox.inputElement.selectionStart = this.inputBox.value.length; - } - } - - // Select element on Enter or on Arrow-Right if we are at the end of the input - else if (keyboardEvent.keyCode === KeyCode.Enter || shouldOpenInBackground) { - DOM.EventHelper.stop(e, true); - - const focus = this.tree.getFocus(); - if (focus) { - this.elementSelected(focus, e, shouldOpenInBackground ? Mode.OPEN_IN_BACKGROUND : Mode.OPEN); - } - } - })); - - // Result count for screen readers - this.resultCount = document.createElement('div'); - DOM.addClass(this.resultCount, 'quick-open-result-count'); - this.resultCount.setAttribute('aria-live', 'polite'); - this.resultCount.setAttribute('aria-atomic', 'true'); - this.element.appendChild(this.resultCount); - - // Tree - this.treeContainer = document.createElement('div'); - DOM.addClass(this.treeContainer, 'quick-open-tree'); - this.element.appendChild(this.treeContainer); - - const createTree = this.options.treeCreator || ((container, config, opts) => new Tree(container, config, opts)); - - this.tree = this._register(createTree(this.treeContainer, { - dataSource: new DataSource(this), - controller: new QuickOpenController({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: this.options.keyboardSupport }), - renderer: (this.renderer = new Renderer(this, this.styles)), - filter: new Filter(this), - accessibilityProvider: new AccessibilityProvider(this) - }, { - twistiePixels: 11, - indentPixels: 0, - alwaysFocused: true, - verticalScrollMode: ScrollbarVisibility.Visible, - horizontalScrollMode: ScrollbarVisibility.Hidden, - ariaLabel: nls.localize('treeAriaLabel', "Quick Picker"), - keyboardSupport: this.options.keyboardSupport, - preventRootFocus: false - })); - - this.treeElement = this.tree.getHTMLElement(); - - // Handle Focus and Selection event - this._register(this.tree.onDidChangeFocus(event => { - this.elementFocused(event.focus, event); - })); - - this._register(this.tree.onDidChangeSelection(event => { - if (event.selection && event.selection.length > 0) { - const mouseEvent: StandardMouseEvent = event.payload && event.payload.originalEvent instanceof StandardMouseEvent ? event.payload.originalEvent : undefined; - const shouldOpenInBackground = mouseEvent ? this.shouldOpenInBackground(mouseEvent) : false; - - this.elementSelected(event.selection[0], event, shouldOpenInBackground ? Mode.OPEN_IN_BACKGROUND : Mode.OPEN); - } - })); - - this._register(DOM.addDisposableListener(this.treeContainer, DOM.EventType.KEY_DOWN, e => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); - - // Only handle when in quick navigation mode - if (!this.quickNavigateConfiguration) { - return; - } - - // Support keyboard navigation in quick navigation mode - if (keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { - DOM.EventHelper.stop(e, true); - - this.navigateInTree(keyboardEvent.keyCode); - } - - // Support to open item with Enter still even in quick nav mode - else if (keyboardEvent.keyCode === KeyCode.Enter) { - DOM.EventHelper.stop(e, true); - - const focus = this.tree.getFocus(); - if (focus) { - this.elementSelected(focus, e); - } - } - })); - - this._register(DOM.addDisposableListener(this.treeContainer, DOM.EventType.KEY_UP, e => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); - const keyCode = keyboardEvent.keyCode; - - // Only handle when in quick navigation mode - if (!this.quickNavigateConfiguration) { - return; - } - - // Select element when keys are pressed that signal it - const quickNavKeys = this.quickNavigateConfiguration.keybindings; - const wasTriggerKeyPressed = quickNavKeys.some(k => { - const [firstPart, chordPart] = k.getParts(); - if (chordPart) { - return false; - } - - if (firstPart.shiftKey && keyCode === KeyCode.Shift) { - if (keyboardEvent.ctrlKey || keyboardEvent.altKey || keyboardEvent.metaKey) { - return false; // this is an optimistic check for the shift key being used to navigate back in quick open - } - - return true; - } - - if (firstPart.altKey && keyCode === KeyCode.Alt) { - return true; - } - - if (firstPart.ctrlKey && keyCode === KeyCode.Ctrl) { - return true; - } - - if (firstPart.metaKey && keyCode === KeyCode.Meta) { - return true; - } - - return false; - }); - - if (wasTriggerKeyPressed) { - const focus = this.tree.getFocus(); - if (focus) { - this.elementSelected(focus, e); - } - } - })); - - // Support layout - if (this.layoutDimensions) { - this.layout(this.layoutDimensions); - } - - this.applyStyles(); - - // Allows focus to switch to next/previous entry after tab into an actionbar item - this._register(DOM.addDisposableListener(this.treeContainer, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); - // Only handle when not in quick navigation mode - if (this.quickNavigateConfiguration) { - return; - } - if (keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { - DOM.EventHelper.stop(e, true); - this.navigateInTree(keyboardEvent.keyCode, keyboardEvent.shiftKey); - this.treeElement.focus(); - } - })); - - return this.element; - } - - style(styles: IQuickOpenStyles): void { - this.styles = styles; - - this.applyStyles(); - } - - protected applyStyles(): void { - if (this.element) { - const foreground = this.styles.foreground ? this.styles.foreground.toString() : ''; - const background = this.styles.background ? this.styles.background.toString() : ''; - const borderColor = this.styles.borderColor ? this.styles.borderColor.toString() : ''; - const widgetShadow = this.styles.widgetShadow ? this.styles.widgetShadow.toString() : ''; - - this.element.style.color = foreground; - this.element.style.backgroundColor = background; - this.element.style.borderColor = borderColor; - this.element.style.borderWidth = borderColor ? '1px' : ''; - this.element.style.borderStyle = borderColor ? 'solid' : ''; - this.element.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : ''; - } - - if (this.progressBar) { - this.progressBar.style({ - progressBarBackground: this.styles.progressBarBackground - }); - } - - if (this.inputBox) { - this.inputBox.style({ - inputBackground: this.styles.inputBackground, - inputForeground: this.styles.inputForeground, - inputBorder: this.styles.inputBorder, - inputValidationInfoBackground: this.styles.inputValidationInfoBackground, - inputValidationInfoForeground: this.styles.inputValidationInfoForeground, - inputValidationInfoBorder: this.styles.inputValidationInfoBorder, - inputValidationWarningBackground: this.styles.inputValidationWarningBackground, - inputValidationWarningForeground: this.styles.inputValidationWarningForeground, - inputValidationWarningBorder: this.styles.inputValidationWarningBorder, - inputValidationErrorBackground: this.styles.inputValidationErrorBackground, - inputValidationErrorForeground: this.styles.inputValidationErrorForeground, - inputValidationErrorBorder: this.styles.inputValidationErrorBorder - }); - } - - if (this.tree && !this.options.treeCreator) { - this.tree.style(this.styles); - } - - if (this.renderer) { - this.renderer.updateStyles(this.styles); - } - } - - private shouldOpenInBackground(e: StandardKeyboardEvent | StandardMouseEvent): boolean { - - // Keyboard - if (e instanceof StandardKeyboardEvent) { - if (e.keyCode !== KeyCode.RightArrow) { - return false; // only for right arrow - } - - if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) { - return false; // no modifiers allowed - } - - // validate the cursor is at the end of the input and there is no selection, - // and if not prevent opening in the background such as the selection can be changed - const element = this.inputBox.inputElement; - return element.selectionEnd === this.inputBox.value.length && element.selectionStart === element.selectionEnd; - } - - // Mouse - return e.middleButton; - } - - private onType(): void { - const value = this.inputBox.value; - - // Adjust help text as needed if present - if (this.helpText) { - if (value) { - DOM.hide(this.helpText); - } else { - DOM.show(this.helpText); - } - } - - // Send to callbacks - this.callbacks.onType(value); - } - - navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void { - if (this.isVisible()) { - - // Transition into quick navigate mode if not yet done - if (!this.quickNavigateConfiguration && quickNavigate) { - this.quickNavigateConfiguration = quickNavigate; - this.tree.domFocus(); - } - - // Navigate - this.navigateInTree(next ? KeyCode.DownArrow : KeyCode.UpArrow); - } - } - - private navigateInTree(keyCode: KeyCode, isShift?: boolean): void { - const model: IModel = this.tree.getInput(); - const entries = model ? model.entries : []; - const oldFocus = this.tree.getFocus(); - - // Normal Navigation - switch (keyCode) { - case KeyCode.DownArrow: - this.tree.focusNext(); - break; - - case KeyCode.UpArrow: - this.tree.focusPrevious(); - break; - - case KeyCode.PageDown: - this.tree.focusNextPage(); - break; - - case KeyCode.PageUp: - this.tree.focusPreviousPage(); - break; - - case KeyCode.Tab: - if (isShift) { - this.tree.focusPrevious(); - } else { - this.tree.focusNext(); - } - break; - } - - let newFocus = this.tree.getFocus(); - - // Support cycle-through navigation if focus did not change - if (entries.length > 1 && oldFocus === newFocus) { - - // Up from no entry or first entry goes down to last - if (keyCode === KeyCode.UpArrow || (keyCode === KeyCode.Tab && isShift)) { - this.tree.focusLast(); - } - - // Down from last entry goes to up to first - else if (keyCode === KeyCode.DownArrow || keyCode === KeyCode.Tab && !isShift) { - this.tree.focusFirst(); - } - } - - // Reveal - newFocus = this.tree.getFocus(); - if (newFocus) { - this.tree.reveal(newFocus); - } - } - - private elementFocused(value: any, event?: any): void { - if (!value || !this.isVisible()) { - return; - } - - // ARIA - const arivaActiveDescendant = this.treeElement.getAttribute('aria-activedescendant'); - if (arivaActiveDescendant) { - this.inputElement.setAttribute('aria-activedescendant', arivaActiveDescendant); - } else { - this.inputElement.removeAttribute('aria-activedescendant'); - } - - const context: IEntryRunContext = { event: event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration }; - this.model!.runner.run(value, Mode.PREVIEW, context); - } - - private elementSelected(value: any, event?: any, preferredMode?: Mode): void { - let hide = true; - - // Trigger open of element on selection - if (this.isVisible()) { - let mode = preferredMode || Mode.OPEN; - - const context: IEntryRunContext = { event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration }; - - hide = this.model!.runner.run(value, mode, context); - } - - // Hide if command was run successfully - if (hide) { - this.hide(HideReason.ELEMENT_SELECTED); - } - } - - private extractKeyMods(event: any): IKeyMods { - return { - ctrlCmd: event && (event.ctrlKey || event.metaKey || (event.payload && event.payload.originalEvent && (event.payload.originalEvent.ctrlKey || event.payload.originalEvent.metaKey))), - alt: event && (event.altKey || (event.payload && event.payload.originalEvent && event.payload.originalEvent.altKey)) - }; - } - - show(prefix: string, options?: IShowOptions): void; - show(input: IModel, options?: IShowOptions): void; - show(param: any, options?: IShowOptions): void { - this.visible = true; - this.isLoosingFocus = false; - this.quickNavigateConfiguration = options ? options.quickNavigateConfiguration : undefined; - - // Adjust UI for quick navigate mode - if (this.quickNavigateConfiguration) { - DOM.hide(this.inputContainer); - DOM.show(this.element); - this.tree.domFocus(); - } - - // Otherwise use normal UI - else { - DOM.show(this.inputContainer); - DOM.show(this.element); - this.inputBox.focus(); - } - - // Adjust Help text for IE - if (this.helpText) { - if (this.quickNavigateConfiguration || types.isString(param)) { - DOM.hide(this.helpText); - } else { - DOM.show(this.helpText); - } - } - - // Show based on param - if (types.isString(param)) { - this.doShowWithPrefix(param); - } else { - if (options && options.value) { - this.restoreLastInput(options.value); - } - this.doShowWithInput(param, options && options.autoFocus ? options.autoFocus : {}); - } - - // Respect selectAll option - if (options && options.inputSelection && !this.quickNavigateConfiguration) { - this.inputBox.select(options.inputSelection); - } - - if (this.callbacks.onShow) { - this.callbacks.onShow(); - } - } - - private restoreLastInput(lastInput: string) { - this.inputBox.value = lastInput; - this.inputBox.select(); - this.callbacks.onType(lastInput); - } - - private doShowWithPrefix(prefix: string): void { - this.inputBox.value = prefix; - this.callbacks.onType(prefix); - } - - private doShowWithInput(input: IModel, autoFocus: IAutoFocus): void { - this.setInput(input, autoFocus); - } - - private setInputAndLayout(input: IModel, autoFocus?: IAutoFocus): void { - this.treeContainer.style.height = `${this.getHeight(input)}px`; - - this.tree.setInput(null).then(() => { - this.model = input; - - // ARIA - this.inputElement.setAttribute('aria-haspopup', String(input && input.entries && input.entries.length > 0)); - - return this.tree.setInput(input); - }).then(() => { - - // Indicate entries to tree - this.tree.layout(); - - const entries = input ? input.entries.filter(e => this.isElementVisible(input, e)) : []; - this.updateResultCount(entries.length); - - // Handle auto focus - if (entries.length) { - this.autoFocus(input, entries, autoFocus); - } - }); - } - - private isElementVisible(input: IModel, e: T): boolean { - if (!input.filter) { - return true; - } - - return input.filter.isVisible(e); - } - - private autoFocus(input: IModel, entries: any[], autoFocus: IAutoFocus = {}): void { - - // First check for auto focus of prefix matches - if (autoFocus.autoFocusPrefixMatch) { - let caseSensitiveMatch: any; - let caseInsensitiveMatch: any; - const prefix = autoFocus.autoFocusPrefixMatch; - const lowerCasePrefix = prefix.toLowerCase(); - for (const entry of entries) { - const label = input.dataSource.getLabel(entry) || ''; - - if (!caseSensitiveMatch && label.indexOf(prefix) === 0) { - caseSensitiveMatch = entry; - } else if (!caseInsensitiveMatch && label.toLowerCase().indexOf(lowerCasePrefix) === 0) { - caseInsensitiveMatch = entry; - } - - if (caseSensitiveMatch && caseInsensitiveMatch) { - break; - } - } - - const entryToFocus = caseSensitiveMatch || caseInsensitiveMatch; - if (entryToFocus) { - this.tree.setFocus(entryToFocus); - this.tree.reveal(entryToFocus, 0.5); - - return; - } - } - - // Second check for auto focus of first entry - if (autoFocus.autoFocusFirstEntry) { - this.tree.focusFirst(); - this.tree.reveal(this.tree.getFocus()); - } - - // Third check for specific index option - else if (typeof autoFocus.autoFocusIndex === 'number') { - if (entries.length > autoFocus.autoFocusIndex) { - this.tree.focusNth(autoFocus.autoFocusIndex); - this.tree.reveal(this.tree.getFocus()); - } - } - - // Check for auto focus of second entry - else if (autoFocus.autoFocusSecondEntry) { - if (entries.length > 1) { - this.tree.focusNth(1); - } - } - - // Finally check for auto focus of last entry - else if (autoFocus.autoFocusLastEntry) { - if (entries.length > 1) { - this.tree.focusLast(); - } - } - } - - refresh(input?: IModel, autoFocus?: IAutoFocus): void { - if (!this.isVisible()) { - return; - } - - if (!input) { - input = this.tree.getInput(); - } - - if (!input) { - return; - } - - // Apply height & Refresh - this.treeContainer.style.height = `${this.getHeight(input)}px`; - this.tree.refresh().then(() => { - - // Indicate entries to tree - this.tree.layout(); - - const entries = input ? input.entries!.filter(e => this.isElementVisible(input!, e)) : []; - this.updateResultCount(entries.length); - - // Handle auto focus - if (autoFocus) { - if (entries.length) { - this.autoFocus(input!, entries, autoFocus); - } - } - }); - } - - private getHeight(input: IModel): number { - const renderer = input.renderer; - - if (!input) { - const itemHeight = renderer.getHeight(null); - - return this.options.minItemsToShow ? this.options.minItemsToShow * itemHeight : 0; - } - - let height = 0; - - let preferredItemsHeight: number | undefined; - if (this.layoutDimensions && this.layoutDimensions.height) { - preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.4 /* max 40% of screen */; - } - - if (!preferredItemsHeight || preferredItemsHeight > QuickOpenWidget.MAX_ITEMS_HEIGHT) { - preferredItemsHeight = QuickOpenWidget.MAX_ITEMS_HEIGHT; - } - - const entries = input.entries.filter(e => this.isElementVisible(input, e)); - const maxEntries = this.options.maxItemsToShow || entries.length; - for (let i = 0; i < maxEntries && i < entries.length; i++) { - const entryHeight = renderer.getHeight(entries[i]); - if (height + entryHeight <= preferredItemsHeight) { - height += entryHeight; - } else { - break; - } - } - - return height; - } - - updateResultCount(count: number) { - this.resultCount.textContent = nls.localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results", count); - } - - hide(reason?: HideReason): void { - if (!this.isVisible()) { - return; - } - - this.visible = false; - DOM.hide(this.element); - this.element.blur(); - - // Clear input field and clear tree - this.inputBox.value = ''; - this.tree.setInput(null); - - // ARIA - this.inputElement.setAttribute('aria-haspopup', 'false'); - - // Reset Tree Height - this.treeContainer.style.height = `${this.options.minItemsToShow ? this.options.minItemsToShow * 22 : 0}px`; - - // Clear any running Progress - this.progressBar.stop().hide(); - - // Clear Focus - if (this.tree.isDOMFocused()) { - this.tree.domBlur(); - } else if (this.inputBox.hasFocus()) { - this.inputBox.blur(); - } - - // Callbacks - if (reason === HideReason.ELEMENT_SELECTED) { - this.callbacks.onOk(); - } else { - this.callbacks.onCancel(); - } - - if (this.callbacks.onHide) { - this.callbacks.onHide(reason!); - } - } - - getQuickNavigateConfiguration(): IQuickNavigateConfiguration { - return this.quickNavigateConfiguration!; - } - - setPlaceHolder(placeHolder: string): void { - if (this.inputBox) { - this.inputBox.setPlaceHolder(placeHolder); - } - } - - setValue(value: string, selectionOrStableHint?: [number, number] | null): void { - if (this.inputBox) { - this.inputBox.value = value; - if (selectionOrStableHint === null) { - // null means stable-selection - } else if (Array.isArray(selectionOrStableHint)) { - const [start, end] = selectionOrStableHint; - this.inputBox.select({ start, end }); - } else { - this.inputBox.select(); - } - } - } - - setPassword(isPassword: boolean): void { - if (this.inputBox) { - this.inputBox.inputElement.type = isPassword ? 'password' : 'text'; - } - } - - setInput(input: IModel, autoFocus?: IAutoFocus, ariaLabel?: string): void { - if (!this.isVisible()) { - return; - } - - // If the input changes, indicate this to the tree - if (!!this.getInput()) { - this.onInputChanging(); - } - - // Adapt tree height to entries and apply input - this.setInputAndLayout(input, autoFocus); - - // Apply ARIA - if (this.inputBox) { - this.inputBox.setAriaLabel(ariaLabel || DEFAULT_INPUT_ARIA_LABEL); - } - } - - private onInputChanging(): void { - if (this.inputChangingTimeoutHandle) { - clearTimeout(this.inputChangingTimeoutHandle); - this.inputChangingTimeoutHandle = null; - } - - // when the input is changing in quick open, we indicate this as CSS class to the widget - // for a certain timeout. this helps reducing some hectic UI updates when input changes quickly - DOM.addClass(this.element, 'content-changing'); - this.inputChangingTimeoutHandle = setTimeout(() => { - DOM.removeClass(this.element, 'content-changing'); - }, 500); - } - - getInput(): IModel { - return this.tree.getInput(); - } - - showInputDecoration(decoration: Severity): void { - if (this.inputBox) { - this.inputBox.showMessage({ type: decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR, content: '' }); - } - } - - clearInputDecoration(): void { - if (this.inputBox) { - this.inputBox.hideMessage(); - } - } - - focus(): void { - if (this.isVisible() && this.inputBox) { - this.inputBox.focus(); - } - } - - accept(): void { - if (this.isVisible()) { - const focus = this.tree.getFocus(); - if (focus) { - this.elementSelected(focus); - } - } - } - - getProgressBar(): ProgressBar { - return this.progressBar; - } - - getInputBox(): InputBox { - return this.inputBox; - } - - setExtraClass(clazz: string | null): void { - const previousClass = this.element.getAttribute('quick-open-extra-class'); - if (previousClass) { - DOM.removeClasses(this.element, previousClass); - } - - if (clazz) { - DOM.addClasses(this.element, clazz); - this.element.setAttribute('quick-open-extra-class', clazz); - } else if (previousClass) { - this.element.removeAttribute('quick-open-extra-class'); - } - } - - isVisible(): boolean { - return this.visible; - } - - layout(dimension: DOM.Dimension): void { - this.layoutDimensions = dimension; - - // Apply to quick open width (height is dynamic by number of items to show) - const quickOpenWidth = Math.min(this.layoutDimensions.width * 0.62 /* golden cut */, QuickOpenWidget.MAX_WIDTH); - if (this.element) { - - // quick open - this.element.style.width = `${quickOpenWidth}px`; - this.element.style.marginLeft = `-${quickOpenWidth / 2}px`; - - // input field - this.inputContainer.style.width = `${quickOpenWidth - 12}px`; - } - } - - private gainingFocus(): void { - this.isLoosingFocus = false; - } - - private loosingFocus(e: FocusEvent): void { - if (!this.isVisible()) { - return; - } - - const relatedTarget = e.relatedTarget as HTMLElement; - if (!this.quickNavigateConfiguration && DOM.isAncestor(relatedTarget, this.element)) { - return; // user clicked somewhere into quick open widget, do not close thereby - } - - this.isLoosingFocus = true; - setTimeout(() => { - if (!this.isLoosingFocus || this.isDisposed) { - return; - } - - const veto = this.callbacks.onFocusLost && this.callbacks.onFocusLost(); - if (!veto) { - this.hide(HideReason.FOCUS_LOST); - } - }, 0); - } - - dispose(): void { - super.dispose(); - - this.isDisposed = true; - } -} diff --git a/src/vs/base/parts/quickopen/browser/quickopen.css b/src/vs/base/parts/quickopen/browser/quickopen.css deleted file mode 100644 index c53b0fafe24..00000000000 --- a/src/vs/base/parts/quickopen/browser/quickopen.css +++ /dev/null @@ -1,165 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-quick-open-widget { - position: absolute; - width: 600px; - z-index: 2000; - padding-bottom: 6px; - left: 50%; - margin-left: -300px; -} - -.monaco-quick-open-widget .monaco-progress-container { - position: absolute; - left: 0; - top: 38px; - z-index: 1; - height: 2px; -} - -.monaco-quick-open-widget .monaco-progress-container .progress-bit { - height: 2px; -} - -.monaco-quick-open-widget .quick-open-input { - width: 588px; - border: none; - margin: 6px; -} - -.monaco-quick-open-widget .quick-open-input .monaco-inputbox { - width: 100%; - height: 25px; -} - -.monaco-quick-open-widget .quick-open-result-count { - position: absolute; - left: -10000px; -} - -.monaco-quick-open-widget .quick-open-tree { - line-height: 22px; -} - -.monaco-quick-open-widget .quick-open-tree .monaco-tree-row > .content > .sub-content { - overflow: hidden; -} - -.monaco-quick-open-widget.content-changing .quick-open-tree .monaco-scrollable-element .slider { - display: none; /* scrollbar slider causes some hectic updates when input changes quickly, so hide it while quick open changes */ -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry { - overflow: hidden; - text-overflow: ellipsis; - display: flex; - flex-direction: column; - height: 100%; -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry > .quick-open-row { - display: flex; - align-items: center; -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon { - overflow: hidden; - width: 16px; - height: 16px; - margin-right: 4px; - display: flex; - align-items: center; - vertical-align: middle; - flex-shrink: 0; -} - -.monaco-quick-open-widget .quick-open-tree .monaco-icon-label, -.monaco-quick-open-widget .quick-open-tree .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container { - flex: 1; /* make sure the icon label grows within the row */ -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label span { - opacity: 1; -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry-meta { - opacity: 0.7; - line-height: normal; -} - -.monaco-quick-open-widget .quick-open-tree .content.has-group-label .quick-open-entry-keybinding { - margin-right: 8px; -} - -.monaco-quick-open-widget .quick-open-tree .quick-open-entry-keybinding .monaco-keybinding-key { - vertical-align: text-bottom; -} - -.monaco-quick-open-widget .quick-open-tree .results-group { - margin-right: 18px; -} - -.monaco-quick-open-widget .quick-open-tree .monaco-tree-row.focused > .content.has-actions > .results-group, -.monaco-quick-open-widget .quick-open-tree .monaco-tree-row:hover:not(.highlighted) > .content.has-actions > .results-group, -.monaco-quick-open-widget .quick-open-tree .focused .monaco-tree-row.focused > .content.has-actions > .results-group { - margin-right: 0px; -} - -.monaco-quick-open-widget .quick-open-tree .results-group-separator { - border-top-width: 1px; - border-top-style: solid; - box-sizing: border-box; - margin-left: -11px; - padding-left: 11px; -} - -/* Actions in Quick Open Items */ - -.monaco-tree .monaco-tree-row > .content.actions { - position: relative; - display: flex; -} - -.monaco-tree .monaco-tree-row > .content.actions > .sub-content { - flex: 1; -} - -.monaco-tree .monaco-tree-row > .content.actions .action-item { - margin: 0; -} - -.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar { - line-height: 22px; -} - -.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar { - display: none; - padding: 0 0.8em 0 0.4em; -} - -.monaco-tree .monaco-tree-row.focused > .content.has-actions > .primary-action-bar { - width: 0; /* in order to support a11y with keyboard, we use width: 0 to hide the actions, which still allows to "Tab" into the actions */ - display: block; -} - -.monaco-tree .monaco-tree-row:hover:not(.highlighted) > .content.has-actions > .primary-action-bar, -.monaco-tree.focused .monaco-tree-row.focused > .content.has-actions > .primary-action-bar, -.monaco-tree .monaco-tree-row > .content.has-actions.more > .primary-action-bar { - width: inherit; - display: block; -} - -.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar .action-label { - margin-right: 0.4em; - margin-top: 4px; - background-repeat: no-repeat; - width: 16px; - height: 16px; -} - -.monaco-quick-open-widget .quick-open-tree .monaco-highlighted-label .highlight { - font-weight: bold; -} diff --git a/src/vs/base/parts/quickopen/common/quickOpen.ts b/src/vs/base/parts/quickopen/common/quickOpen.ts deleted file mode 100644 index 582ddf56ee6..00000000000 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ /dev/null @@ -1,95 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; - -export interface IQuickNavigateConfiguration { - keybindings: ResolvedKeybinding[]; -} - -export interface IAutoFocus { - - /** - * The index of the element to focus in the result list. - */ - autoFocusIndex?: number; - - /** - * If set to true, will automatically select the first entry from the result list. - */ - autoFocusFirstEntry?: boolean; - - /** - * If set to true, will automatically select the second entry from the result list. - */ - autoFocusSecondEntry?: boolean; - - /** - * If set to true, will automatically select the last entry from the result list. - */ - autoFocusLastEntry?: boolean; - - /** - * If set to true, will automatically select any entry whose label starts with the search - * value. Since some entries to the top might match the query but not on the prefix, this - * allows to select the most accurate match (matching the prefix) while still showing other - * elements. - */ - autoFocusPrefixMatch?: string; -} - -export const enum Mode { - PREVIEW, - OPEN, - OPEN_IN_BACKGROUND -} - -export interface IEntryRunContext { - event: any; - keymods: IKeyMods; - quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; -} - -export interface IKeyMods { - ctrlCmd: boolean; - alt: boolean; -} - -export interface IDataSource { - getId(entry: T): string; - getLabel(entry: T): string | null; -} - -/** - * See vs/base/parts/tree/browser/tree.ts - IRenderer - */ -export interface IRenderer { - getHeight(entry: T): number; - getTemplateId(entry: T): string; - renderTemplate(templateId: string, container: any /* HTMLElement */, styles: any): any; - renderElement(entry: T, templateId: string, templateData: any, styles: any): void; - disposeTemplate(templateId: string, templateData: any): void; -} - -export interface IFilter { - isVisible(entry: T): boolean; -} - -export interface IAccessiblityProvider { - getAriaLabel(entry: T): string; -} - -export interface IRunner { - run(entry: T, mode: Mode, context: IEntryRunContext): boolean; -} - -export interface IModel { - entries: T[]; - dataSource: IDataSource; - renderer: IRenderer; - runner: IRunner; - filter?: IFilter; - accessibilityProvider?: IAccessiblityProvider; -} diff --git a/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts b/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts deleted file mode 100644 index f857c0b64a7..00000000000 --- a/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel'; -import { DataSource } from 'vs/base/parts/quickopen/browser/quickOpenViewer'; - -suite('QuickOpen', () => { - test('QuickOpenModel', () => { - const model = new QuickOpenModel(); - - const entry1 = new QuickOpenEntry(); - const entry2 = new QuickOpenEntry(); - const entry3 = new QuickOpenEntryGroup(); - - assert.notEqual(entry1.getId(), entry2.getId()); - assert.notEqual(entry2.getId(), entry3.getId()); - - model.addEntries([entry1, entry2, entry3]); - assert.equal(3, model.getEntries().length); - - model.setEntries([entry1, entry2]); - assert.equal(2, model.getEntries().length); - - entry1.setHidden(true); - assert.equal(1, model.getEntries(true).length); - assert.equal(entry2, model.getEntries(true)[0]); - }); - - test('QuickOpenDataSource', async () => { - const model = new QuickOpenModel(); - - const entry1 = new QuickOpenEntry(); - const entry2 = new QuickOpenEntry(); - const entry3 = new QuickOpenEntryGroup(); - - model.addEntries([entry1, entry2, entry3]); - - const ds = new DataSource(model); - assert.equal(entry1.getId(), ds.getId(null!, entry1)); - assert.equal(true, ds.hasChildren(null!, model)); - assert.equal(false, ds.hasChildren(null!, entry1)); - - const children = await ds.getChildren(null!, model); - assert.equal(3, children.length); - }); -}); diff --git a/src/vs/base/parts/request/browser/request.ts b/src/vs/base/parts/request/browser/request.ts index f8532bbe608..1c2499ba404 100644 --- a/src/vs/base/parts/request/browser/request.ts +++ b/src/vs/base/parts/request/browser/request.ts @@ -5,13 +5,15 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; -import { assign } from 'vs/base/common/objects'; import { VSBuffer, bufferToStream } from 'vs/base/common/buffer'; import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; export function request(options: IRequestOptions, token: CancellationToken): Promise { if (options.proxyAuthorization) { - options.headers = assign(options.headers || {}, { 'Proxy-Authorization': options.proxyAuthorization }); + options.headers = { + ...(options.headers || {}), + 'Proxy-Authorization': options.proxyAuthorization + }; } const xhr = new XMLHttpRequest(); @@ -21,7 +23,7 @@ export function request(options: IRequestOptions, token: CancellationToken): Pro setRequestHeaders(xhr, options); xhr.responseType = 'arraybuffer'; - xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText))); + xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText) || 'XHR failed')); xhr.onload = (e) => { resolve({ res: { diff --git a/src/vs/base/parts/sandbox/common/electronTypes.ts b/src/vs/base/parts/sandbox/common/electronTypes.ts new file mode 100644 index 00000000000..cff3caccfaf --- /dev/null +++ b/src/vs/base/parts/sandbox/common/electronTypes.ts @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +// ####################################################################### +// ### ### +// ### electron.d.ts types we need in a common layer for reuse ### +// ### (copied from Electron 9.x) ### +// ### ### +// ####################################################################### + + +export interface MessageBoxOptions { + /** + * Can be `"none"`, `"info"`, `"error"`, `"question"` or `"warning"`. On Windows, + * `"question"` displays the same icon as `"info"`, unless you set an icon using + * the `"icon"` option. On macOS, both `"warning"` and `"error"` display the same + * warning icon. + */ + type?: string; + /** + * Array of texts for buttons. On Windows, an empty array will result in one button + * labeled "OK". + */ + buttons?: string[]; + /** + * Index of the button in the buttons array which will be selected by default when + * the message box opens. + */ + defaultId?: number; + /** + * Title of the message box, some platforms will not show it. + */ + title?: string; + /** + * Content of the message box. + */ + message: string; + /** + * Extra information of the message. + */ + detail?: string; + /** + * If provided, the message box will include a checkbox with the given label. + */ + checkboxLabel?: string; + /** + * Initial checked state of the checkbox. `false` by default. + */ + checkboxChecked?: boolean; + // icon?: NativeImage; + /** + * The index of the button to be used to cancel the dialog, via the `Esc` key. By + * default this is assigned to the first button with "cancel" or "no" as the label. + * If no such labeled buttons exist and this option is not set, `0` will be used as + * the return value. + */ + cancelId?: number; + /** + * On Windows Electron will try to figure out which one of the `buttons` are common + * buttons (like "Cancel" or "Yes"), and show the others as command links in the + * dialog. This can make the dialog appear in the style of modern Windows apps. If + * you don't like this behavior, you can set `noLink` to `true`. + */ + noLink?: boolean; + /** + * Normalize the keyboard access keys across platforms. Default is `false`. + * Enabling this assumes `&` is used in the button labels for the placement of the + * keyboard shortcut access key and labels will be converted so they work correctly + * on each platform, `&` characters are removed on macOS, converted to `_` on + * Linux, and left untouched on Windows. For example, a button label of `Vie&w` + * will be converted to `Vie_w` on Linux and `View` on macOS and can be selected + * via `Alt-W` on Windows and Linux. + */ + normalizeAccessKeys?: boolean; +} + +export interface MessageBoxReturnValue { + /** + * The index of the clicked button. + */ + response: number; + /** + * The checked state of the checkbox if `checkboxLabel` was set. Otherwise `false`. + */ + checkboxChecked: boolean; +} + +export interface OpenDevToolsOptions { + /** + * Opens the devtools with specified dock state, can be `right`, `bottom`, + * `undocked`, `detach`. Defaults to last used dock state. In `undocked` mode it's + * possible to dock back. In `detach` mode it's not. + */ + mode: ('right' | 'bottom' | 'undocked' | 'detach'); + /** + * Whether to bring the opened devtools window to the foreground. The default is + * `true`. + */ + activate?: boolean; +} + +export interface SaveDialogOptions { + title?: string; + /** + * Absolute directory path, absolute file path, or file name to use by default. + */ + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will + * be used. + */ + buttonLabel?: string; + filters?: FileFilter[]; + /** + * Message to display above text fields. + * + * @platform darwin + */ + message?: string; + /** + * Custom label for the text displayed in front of the filename text field. + * + * @platform darwin + */ + nameFieldLabel?: string; + /** + * Show the tags input box, defaults to `true`. + * + * @platform darwin + */ + showsTagField?: boolean; + properties?: Array<'showHiddenFiles' | 'createDirectory' | 'treatPackageAsDirectory' | 'showOverwriteConfirmation' | 'dontAddToRecent'>; + /** + * Create a security scoped bookmark when packaged for the Mac App Store. If this + * option is enabled and the file doesn't already exist a blank file will be + * created at the chosen path. + * + * @platform darwin,mas + */ + securityScopedBookmarks?: boolean; +} + +export interface OpenDialogOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will + * be used. + */ + buttonLabel?: string; + filters?: FileFilter[]; + /** + * Contains which features the dialog should use. The following values are + * supported: + */ + properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory' | 'dontAddToRecent'>; + /** + * Message to display above input boxes. + * + * @platform darwin + */ + message?: string; + /** + * Create security scoped bookmarks when packaged for the Mac App Store. + * + * @platform darwin,mas + */ + securityScopedBookmarks?: boolean; +} + +export interface OpenDialogReturnValue { + /** + * whether or not the dialog was canceled. + */ + canceled: boolean; + /** + * An array of file paths chosen by the user. If the dialog is cancelled this will + * be an empty array. + */ + filePaths: string[]; + /** + * An array matching the `filePaths` array of base64 encoded strings which contains + * security scoped bookmark data. `securityScopedBookmarks` must be enabled for + * this to be populated. (For return values, see table here.) + * + * @platform darwin,mas + */ + bookmarks?: string[]; +} + +export interface SaveDialogReturnValue { + /** + * whether or not the dialog was canceled. + */ + canceled: boolean; + /** + * If the dialog is canceled, this will be `undefined`. + */ + filePath?: string; + /** + * Base64 encoded string which contains the security scoped bookmark data for the + * saved file. `securityScopedBookmarks` must be enabled for this to be present. + * (For return values, see table here.) + * + * @platform darwin,mas + */ + bookmark?: string; +} + +export interface FileFilter { + + // Docs: http://electronjs.org/docs/api/structures/file-filter + + extensions: string[]; + name: string; +} + +export interface InputEvent { + + // Docs: http://electronjs.org/docs/api/structures/input-event + + /** + * An array of modifiers of the event, can be `shift`, `control`, `ctrl`, `alt`, + * `meta`, `command`, `cmd`, `isKeypad`, `isAutoRepeat`, `leftButtonDown`, + * `middleButtonDown`, `rightButtonDown`, `capsLock`, `numLock`, `left`, `right`. + */ + modifiers?: Array<'shift' | 'control' | 'ctrl' | 'alt' | 'meta' | 'command' | 'cmd' | 'isKeypad' | 'isAutoRepeat' | 'leftButtonDown' | 'middleButtonDown' | 'rightButtonDown' | 'capsLock' | 'numLock' | 'left' | 'right'>; +} + +export interface MouseInputEvent extends InputEvent { + + // Docs: http://electronjs.org/docs/api/structures/mouse-input-event + + /** + * The button pressed, can be `left`, `middle`, `right`. + */ + button?: ('left' | 'middle' | 'right'); + clickCount?: number; + globalX?: number; + globalY?: number; + movementX?: number; + movementY?: number; + /** + * The type of the event, can be `mouseDown`, `mouseUp`, `mouseEnter`, + * `mouseLeave`, `contextMenu`, `mouseWheel` or `mouseMove`. + */ + type: ('mouseDown' | 'mouseUp' | 'mouseEnter' | 'mouseLeave' | 'contextMenu' | 'mouseWheel' | 'mouseMove'); + x: number; + y: number; +} diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js new file mode 100644 index 00000000000..d0cb6d4286b --- /dev/null +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// @ts-check +(function () { + 'use strict'; + + const { ipcRenderer, webFrame, crashReporter, contextBridge } = require('electron'); + + // ####################################################################### + // ### ### + // ### !!! DO NOT USE GET/SET PROPERTIES ANYWHERE HERE !!! ### + // ### !!! UNLESS THE ACCESS IS WITHOUT SIDE EFFECTS !!! ### + // ### (https://github.com/electron/electron/issues/25516) ### + // ### ### + // ####################################################################### + + const globals = { + + /** + * A minimal set of methods exposed from Electron's `ipcRenderer` + * to support communication to main process. + */ + ipcRenderer: { + + /** + * @param {string} channel + * @param {any[]} args + */ + send(channel, ...args) { + if (validateIPC(channel)) { + ipcRenderer.send(channel, ...args); + } + }, + + /** + * @param {string} channel + * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener + */ + on(channel, listener) { + if (validateIPC(channel)) { + ipcRenderer.on(channel, listener); + } + }, + + /** + * @param {string} channel + * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener + */ + once(channel, listener) { + if (validateIPC(channel)) { + ipcRenderer.once(channel, listener); + } + }, + + /** + * @param {string} channel + * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener + */ + removeListener(channel, listener) { + if (validateIPC(channel)) { + ipcRenderer.removeListener(channel, listener); + } + } + }, + + /** + * Support for subset of methods of Electron's `webFrame` type. + */ + webFrame: { + + /** + * @param {number} level + */ + setZoomLevel(level) { + if (typeof level === 'number') { + webFrame.setZoomLevel(level); + } + } + }, + + /** + * Support for subset of methods of Electron's `crashReporter` type. + */ + crashReporter: { + + /** + * @param {string} key + * @param {string} value + */ + addExtraParameter(key, value) { + crashReporter.addExtraParameter(key, value); + } + }, + + /** + * Support for a subset of access to node.js global `process`. + */ + process: { + get platform() { return process.platform; }, + get env() { return process.env; }, + get versions() { return process.versions; }, + get type() { return 'renderer'; }, + + _whenEnvResolved: undefined, + whenEnvResolved: + /** + * @returns when the shell environment has been resolved. + */ + function () { + if (!this._whenEnvResolved) { + this._whenEnvResolved = resolveEnv(); + } + + return this._whenEnvResolved; + }, + + nextTick: + /** + * Adds callback to the "next tick queue". This queue is fully drained + * after the current operation on the JavaScript stack runs to completion + * and before the event loop is allowed to continue. + * + * @param {Function} callback + * @param {any[]} args + */ + function nextTick(callback, ...args) { + return process.nextTick(callback, ...args); + }, + + cwd: + /** + * @returns the current working directory. + */ + function () { + return process.cwd(); + }, + + getuid: + /** + * @returns the numeric user identity of the process + */ + function () { + return process.getuid(); + }, + + getProcessMemoryInfo: + /** + * @returns {Promise} + */ + function () { + return process.getProcessMemoryInfo(); + }, + + on: + /** + * @param {string} type + * @param {() => void} callback + */ + function (type, callback) { + if (validateProcessEventType(type)) { + process.on(type, callback); + } + } + }, + + /** + * Some information about the context we are running in. + */ + context: { + get sandbox() { return process.argv.includes('--enable-sandbox'); } + } + }; + + // Use `contextBridge` APIs to expose globals to VSCode + // only if context isolation is enabled, otherwise just + // add to the DOM global. + let useContextBridge = process.argv.includes('--context-isolation'); + if (useContextBridge) { + try { + contextBridge.exposeInMainWorld('vscode', globals); + } catch (error) { + console.error(error); + + useContextBridge = false; + } + } + + if (!useContextBridge) { + // @ts-ignore + window.vscode = globals; + } + + //#region Utilities + + /** + * @param {string} channel + */ + function validateIPC(channel) { + if (!channel || !channel.startsWith('vscode:')) { + throw new Error(`Unsupported event IPC channel '${channel}'`); + } + + return true; + } + + /** + * @param {string} type + * @returns {type is 'uncaughtException'} + */ + function validateProcessEventType(type) { + if (type !== 'uncaughtException') { + throw new Error(`Unsupported process event '${type}'`); + } + + return true; + } + + /** + * If VSCode is not run from a terminal, we should resolve additional + * shell specific environment from the OS shell to ensure we are seeing + * all development related environment variables. We do this from the + * main process because it may involve spawning a shell. + */ + function resolveEnv() { + return new Promise(function (resolve) { + const handle = setTimeout(function () { + console.warn('Preload: Unable to resolve shell environment in a reasonable time'); + + // It took too long to fetch the shell environment, return + resolve(); + }, 3000); + + ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { + clearTimeout(handle); + + // Assign all keys of the shell environment to our process environment + Object.assign(process.env, shellEnv); + + resolve(); + }); + + ipcRenderer.send('vscode:fetchShellEnv'); + }); + } + + //#endregion +}()); diff --git a/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts b/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts new file mode 100644 index 00000000000..ef8fd5642ea --- /dev/null +++ b/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +// ####################################################################### +// ### ### +// ### electron.d.ts types we expose from electron-sandbox ### +// ### (copied from Electron 9.x) ### +// ### ### +// ####################################################################### + + +export interface IpcRenderer { + /** + * Listens to `channel`, when a new message arrives `listener` would be called with + * `listener(event, args...)`. + */ + on(channel: string, listener: (event: unknown, ...args: any[]) => void): void; + + /** + * Adds a one time `listener` function for the event. This `listener` is invoked + * only the next time a message is sent to `channel`, after which it is removed. + */ + once(channel: string, listener: (event: unknown, ...args: any[]) => void): void; + + /** + * Removes the specified `listener` from the listener array for the specified + * `channel`. + */ + removeListener(channel: string, listener: (event: unknown, ...args: any[]) => void): void; + + /** + * Send an asynchronous message to the main process via `channel`, along with + * arguments. Arguments will be serialized with the Structured Clone Algorithm, + * just like `postMessage`, so prototype chains will not be included. Sending + * Functions, Promises, Symbols, WeakMaps, or WeakSets will throw an exception. + * + * > **NOTE**: Sending non-standard JavaScript types such as DOM objects or special + * Electron objects is deprecated, and will begin throwing an exception starting + * with Electron 9. + * + * The main process handles it by listening for `channel` with the `ipcMain` + * module. + */ + send(channel: string, ...args: any[]): void; +} + +export interface WebFrame { + /** + * Changes the zoom level to the specified level. The original size is 0 and each + * increment above or below represents zooming 20% larger or smaller to default + * limits of 300% and 50% of original size, respectively. + */ + setZoomLevel(level: number): void; +} + +export interface CrashReporter { + /** + * Set an extra parameter to be sent with the crash report. The values specified + * here will be sent in addition to any values set via the `extra` option when + * `start` was called. + * + * Parameters added in this fashion (or via the `extra` parameter to + * `crashReporter.start`) are specific to the calling process. Adding extra + * parameters in the main process will not cause those parameters to be sent along + * with crashes from renderer or other child processes. Similarly, adding extra + * parameters in a renderer process will not result in those parameters being sent + * with crashes that occur in other renderer processes or in the main process. + * + * **Note:** Parameters have limits on the length of the keys and values. Key names + * must be no longer than 39 bytes, and values must be no longer than 127 bytes. + * Keys with names longer than the maximum will be silently ignored. Key values + * longer than the maximum length will be truncated. + */ + addExtraParameter(key: string, value: string): void; +} + +export interface ProcessMemoryInfo { + + // Docs: http://electronjs.org/docs/api/structures/process-memory-info + + /** + * The amount of memory not shared by other processes, such as JS heap or HTML + * content in Kilobytes. + */ + private: number; + /** + * The amount of memory currently pinned to actual physical RAM in Kilobytes. + * + * @platform linux,win32 + */ + residentSet: number; + /** + * The amount of memory shared between processes, typically memory consumed by the + * Electron code itself in Kilobytes. + */ + shared: number; +} + +export interface CrashReporterStartOptions { + /** + * URL that crash reports will be sent to as POST. + */ + submitURL: string; + /** + * Defaults to `app.name`. + */ + productName?: string; + /** + * Deprecated alias for `{ globalExtra: { _companyName: ... } }`. + * + * @deprecated + */ + companyName?: string; + /** + * Whether crash reports should be sent to the server. If false, crash reports will + * be collected and stored in the crashes directory, but not uploaded. Default is + * `true`. + */ + uploadToServer?: boolean; + /** + * If true, crashes generated in the main process will not be forwarded to the + * system crash handler. Default is `false`. + */ + ignoreSystemCrashHandler?: boolean; + /** + * If true, limit the number of crashes uploaded to 1/hour. Default is `false`. + * + * @platform darwin,win32 + */ + rateLimit?: boolean; + /** + * If true, crash reports will be compressed and uploaded with `Content-Encoding: + * gzip`. Not all collection servers support compressed payloads. Default is + * `false`. + * + * @platform darwin,win32 + */ + compress?: boolean; + /** + * Extra string key/value annotations that will be sent along with crash reports + * that are generated in the main process. Only string values are supported. + * Crashes generated in child processes will not contain these extra parameters to + * crash reports generated from child processes, call `addExtraParameter` from the + * child process. + */ + extra?: Record; + /** + * Extra string key/value annotations that will be sent along with any crash + * reports generated in any process. These annotations cannot be changed once the + * crash reporter has been started. If a key is present in both the global extra + * parameters and the process-specific extra parameters, then the global one will + * take precedence. By default, `productName` and the app version are included, as + * well as the Electron version. + */ + globalExtra?: Record; +} + +/** + * Additional information around a `app.on('login')` event. + */ +export interface AuthInfo { + isProxy: boolean; + scheme: string; + host: string; + port: number; + realm: string; +} diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts new file mode 100644 index 00000000000..923383b48db --- /dev/null +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { globals, INodeProcess, IProcessEnvironment } from 'vs/base/common/platform'; +import { ProcessMemoryInfo, CrashReporter, IpcRenderer, WebFrame } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes'; + +export interface ISandboxNodeProcess extends INodeProcess { + + /** + * The process.platform property returns a string identifying the operating system platform + * on which the Node.js process is running. + */ + platform: 'win32' | 'linux' | 'darwin'; + + /** + * The type will always be Electron renderer. + */ + type: 'renderer'; + + /** + * A list of versions for the current node.js/electron configuration. + */ + versions: { [key: string]: string | undefined }; + + /** + * The process.env property returns an object containing the user environment. + */ + env: IProcessEnvironment; + + /** + * The current working directory. + */ + cwd(): string; + + /** + * Returns the numeric user identity of the process. + */ + getuid(): number; + + /** + * Allows to await resolving the full process environment by checking for the shell environment + * of the OS in certain cases (e.g. when the app is started from the Dock on macOS). + */ + whenEnvResolved(): Promise; + + /** + * Adds callback to the "next tick queue". This queue is fully drained + * after the current operation on the JavaScript stack runs to completion + * and before the event loop is allowed to continue. + */ + nextTick(callback: (...args: any[]) => void, ...args: any[]): void; + + /** + * A listener on the process. Only a small subset of listener types are allowed. + */ + on: (type: string, callback: Function) => void; + + /** + * Resolves with a ProcessMemoryInfo + * + * Returns an object giving memory usage statistics about the current process. Note + * that all statistics are reported in Kilobytes. This api should be called after + * app ready. + * + * Chromium does not provide `residentSet` value for macOS. This is because macOS + * performs in-memory compression of pages that haven't been recently used. As a + * result the resident set size value is not what one would expect. `private` + * memory is more representative of the actual pre-compression memory usage of the + * process on macOS. + */ + getProcessMemoryInfo: () => Promise; +} + +export interface ISandboxContext { + + /** + * Wether the renderer runs with `sandbox` enabled or not. + */ + sandbox: boolean; +} + +export const ipcRenderer: IpcRenderer = globals.vscode.ipcRenderer; +export const webFrame: WebFrame = globals.vscode.webFrame; +export const crashReporter: CrashReporter = globals.vscode.crashReporter; +export const process: ISandboxNodeProcess = globals.vscode.process; +export const context: ISandboxContext = globals.vscode.context; diff --git a/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts new file mode 100644 index 00000000000..ac3e6511710 --- /dev/null +++ b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ipcRenderer, crashReporter, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals'; + +suite('Sandbox', () => { + test('globals', () => { + assert.ok(ipcRenderer); + assert.ok(crashReporter); + assert.ok(webFrame); + }); +}); diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index 03dedeca57f..8b55819cc54 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -18,16 +18,17 @@ export enum StorageHint { } export interface IStorageOptions { - hint?: StorageHint; + readonly hint?: StorageHint; } export interface IUpdateRequest { - insert?: Map; - delete?: Set; + readonly insert?: Map; + readonly delete?: Set; } export interface IStorageItemsChangeEvent { - items: Map; + readonly changed?: Map; + readonly deleted?: Set; } export interface IStorageDatabase { @@ -42,9 +43,10 @@ export interface IStorageDatabase { export interface IStorage extends IDisposable { + readonly onDidChangeStorage: Event; + readonly items: Map; readonly size: number; - readonly onDidChangeStorage: Event; init(): Promise; @@ -60,6 +62,8 @@ export interface IStorage extends IDisposable { set(key: string, value: string | boolean | number | undefined | null): Promise; delete(key: string): Promise; + whenFlushed(): Promise; + close(): Promise; } @@ -73,26 +77,26 @@ export class Storage extends Disposable implements IStorage { private static readonly DEFAULT_FLUSH_DELAY = 100; - private readonly _onDidChangeStorage: Emitter = this._register(new Emitter()); - readonly onDidChangeStorage: Event = this._onDidChangeStorage.event; + private readonly _onDidChangeStorage = this._register(new Emitter()); + readonly onDidChangeStorage = this._onDidChangeStorage.event; private state = StorageState.None; - private cache: Map = new Map(); + private cache = new Map(); - private flushDelayer: ThrottledDelayer; + private readonly flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); - private pendingDeletes: Set = new Set(); - private pendingInserts: Map = new Map(); + private pendingDeletes = new Set(); + private pendingInserts = new Map(); + + private readonly whenFlushedCallbacks: Function[] = []; constructor( - protected database: IStorageDatabase, - private options: IStorageOptions = Object.create(null) + protected readonly database: IStorageDatabase, + private readonly options: IStorageOptions = Object.create(null) ) { super(); - this.flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); - this.registerListeners(); } @@ -104,10 +108,11 @@ export class Storage extends Disposable implements IStorage { // items that change external require us to update our // caches with the values. we just accept the value and // emit an event if there is a change. - e.items.forEach((value, key) => this.accept(key, value)); + e.changed?.forEach((value, key) => this.accept(key, value)); + e.deleted?.forEach(key => this.accept(key, undefined)); } - private accept(key: string, value: string): void { + private accept(key: string, value: string | undefined): void { if (this.state === StorageState.Closed) { return; // Return early if we are already closed } @@ -144,7 +149,7 @@ export class Storage extends Disposable implements IStorage { async init(): Promise { if (this.state !== StorageState.None) { - return Promise.resolve(); // either closed or already initialized + return; // either closed or already initialized } this.state = StorageState.Initialized; @@ -153,7 +158,7 @@ export class Storage extends Disposable implements IStorage { // return early if we know the storage file does not exist. this is a performance // optimization to not load all items of the underlying storage if we know that // there can be no items because the storage does not exist. - return Promise.resolve(); + return; } this.cache = await this.database.getItems(); @@ -273,8 +278,12 @@ export class Storage extends Disposable implements IStorage { await this.database.close(() => this.cache); } + private get hasPending() { + return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0; + } + private flushPending(): Promise { - if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { + if (!this.hasPending) { return Promise.resolve(); // return early if nothing to do } @@ -285,8 +294,23 @@ export class Storage extends Disposable implements IStorage { this.pendingDeletes = new Set(); this.pendingInserts = new Map(); - // Update in storage - return this.database.updateItems(updateRequest); + // Update in storage and release any + // waiters we have once done + return this.database.updateItems(updateRequest).finally(() => { + if (!this.hasPending) { + while (this.whenFlushedCallbacks.length) { + this.whenFlushedCallbacks.pop()?.(); + } + } + }); + } + + whenFlushed(): Promise { + if (!this.hasPending) { + return Promise.resolve(); // return early if nothing to do + } + + return new Promise(resolve => this.whenFlushedCallbacks.push(resolve)); } } @@ -294,13 +318,13 @@ export class InMemoryStorageDatabase implements IStorageDatabase { readonly onDidChangeItemsExternal = Event.None; - private items = new Map(); + private readonly items = new Map(); - getItems(): Promise> { - return Promise.resolve(this.items); + async getItems(): Promise> { + return this.items; } - updateItems(request: IUpdateRequest): Promise { + async updateItems(request: IUpdateRequest): Promise { if (request.insert) { request.insert.forEach((value, key) => this.items.set(key, value)); } @@ -308,11 +332,7 @@ export class InMemoryStorageDatabase implements IStorageDatabase { if (request.delete) { request.delete.forEach(key => this.items.delete(key)); } - - return Promise.resolve(); } - close(): Promise { - return Promise.resolve(); - } -} \ No newline at end of file + async close(): Promise { } +} diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index e73aafe6ad4..d6eca3ac03f 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -3,26 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Database, Statement } from 'vscode-sqlite3'; +import type { Database, Statement } from 'vscode-sqlite3'; import { Event } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'vs/base/common/path'; import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; -import { fill } from 'vs/base/common/arrays'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; interface IDatabaseConnection { - db: Database; - - isInMemory: boolean; + readonly db: Database; + readonly isInMemory: boolean; isErroneous?: boolean; lastError?: string; } export interface ISQLiteStorageDatabaseOptions { - logging?: ISQLiteStorageDatabaseLoggingOptions; + readonly logging?: ISQLiteStorageDatabaseLoggingOptions; } export interface ISQLiteStorageDatabaseLoggingOptions { @@ -39,21 +37,13 @@ export class SQLiteStorageDatabase implements IStorageDatabase { private static readonly BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY private static readonly MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement - private path: string; - private name: string; + private readonly name = basename(this.path); - private logger: SQLiteStorageDatabaseLogger; + private readonly logger = new SQLiteStorageDatabaseLogger(this.options.logging); - private whenConnected: Promise; + private readonly whenConnected = this.connect(this.path); - constructor(path: string, options: ISQLiteStorageDatabaseOptions = Object.create(null)) { - this.path = path; - this.name = basename(path); - - this.logger = new SQLiteStorageDatabaseLogger(options.logging); - - this.whenConnected = this.connect(path); - } + constructor(private readonly path: string, private readonly options: ISQLiteStorageDatabaseOptions = Object.create(null)) { } async getItems(): Promise> { const connection = await this.whenConnected; @@ -106,7 +96,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); keysValuesChunks.forEach(keysValuesChunk => { - this.prepare(connection, `INSERT INTO ItemTable VALUES ${fill(keysValuesChunk.length / 2, '(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => { + this.prepare(connection, `INSERT INTO ItemTable VALUES ${new Array(keysValuesChunk.length / 2).fill('(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => { const keys: string[] = []; let length = 0; toInsert.forEach((value, key) => { @@ -141,7 +131,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); keysChunks.forEach(keysChunk => { - this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${fill(keysChunk.length, '?').join(',')})`, stmt => stmt.run(keysChunk), () => { + this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${new Array(keysChunk.length).fill('?').join(',')})`, stmt => stmt.run(keysChunk), () => { const keys: string[] = []; toDelete.forEach(key => { keys.push(key); @@ -166,7 +156,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { connection.db.close(closeError => { if (closeError) { - this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`); + this.handleSQLiteError(connection, `[storage ${this.name}] close(): ${closeError}`); } // Return early if this storage was created only in-memory @@ -296,7 +286,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } } - private handleSQLiteError(connection: IDatabaseConnection, error: Error & { code?: string }, msg: string): void { + private handleSQLiteError(connection: IDatabaseConnection, msg: string): void { connection.isErroneous = true; connection.lastError = msg; @@ -328,7 +318,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }; // Errors - connection.db.on('error', error => this.handleSQLiteError(connection, error, `[storage ${this.name}] Error (event): ${error}`)); + connection.db.on('error', error => this.handleSQLiteError(connection, `[storage ${this.name}] Error (event): ${error}`)); // Tracing if (this.logger.isTracing) { @@ -342,7 +332,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { connection.db.exec(sql, error => { if (error) { - this.handleSQLiteError(connection, error, `[storage ${this.name}] exec(): ${error}`); + this.handleSQLiteError(connection, `[storage ${this.name}] exec(): ${error}`); return reject(error); } @@ -356,7 +346,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { connection.db.get(sql, (error, row) => { if (error) { - this.handleSQLiteError(connection, error, `[storage ${this.name}] get(): ${error}`); + this.handleSQLiteError(connection, `[storage ${this.name}] get(): ${error}`); return reject(error); } @@ -370,7 +360,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { connection.db.all(sql, (error, rows) => { if (error) { - this.handleSQLiteError(connection, error, `[storage ${this.name}] all(): ${error}`); + this.handleSQLiteError(connection, `[storage ${this.name}] all(): ${error}`); return reject(error); } @@ -389,7 +379,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { connection.db.run('END TRANSACTION', error => { if (error) { - this.handleSQLiteError(connection, error, `[storage ${this.name}] transaction(): ${error}`); + this.handleSQLiteError(connection, `[storage ${this.name}] transaction(): ${error}`); return reject(error); } @@ -404,7 +394,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { const stmt = connection.db.prepare(sql); const statementErrorListener = (error: Error) => { - this.handleSQLiteError(connection, error, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`); + this.handleSQLiteError(connection, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`); }; stmt.on('error', statementErrorListener); diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 9c966c0868b..e735634c2ec 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -14,7 +14,12 @@ import { timeout } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { isWindows } from 'vs/base/common/platform'; -suite('Storage Library', () => { +suite('Storage Library', function () { + + // Given issues such as https://github.com/microsoft/vscode/issues/108113 + // we see random test failures when accessing the native file system. + this.retries(3); + this.timeout(1000 * 20); function uniqueStorageDir(): string { const id = generateUuid(); @@ -40,11 +45,16 @@ suite('Storage Library', () => { changes.add(key); }); + await storage.whenFlushed(); // returns immediately when no pending updates + // Simple updates const set1Promise = storage.set('bar', 'foo'); const set2Promise = storage.set('barNumber', 55); const set3Promise = storage.set('barBoolean', true); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('bar'), 'foo'); equal(storage.getNumber('barNumber'), 55); equal(storage.getBoolean('barBoolean'), true); @@ -57,6 +67,7 @@ suite('Storage Library', () => { let setPromiseResolved = false; await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true); equal(setPromiseResolved, true); + equal(flushPromiseResolved, true); changes = new Set(); @@ -124,28 +135,27 @@ suite('Storage Library', () => { changes.clear(); // Nothing happens if changing to same value - const change = new Map(); - change.set('foo', 'bar'); - database.fireDidChangeItemsExternal({ items: change }); + const changed = new Map(); + changed.set('foo', 'bar'); + database.fireDidChangeItemsExternal({ changed }); equal(changes.size, 0); // Change is accepted if valid - change.set('foo', 'bar1'); - database.fireDidChangeItemsExternal({ items: change }); + changed.set('foo', 'bar1'); + database.fireDidChangeItemsExternal({ changed }); ok(changes.has('foo')); equal(storage.get('foo'), 'bar1'); changes.clear(); // Delete is accepted - change.set('foo', undefined!); - database.fireDidChangeItemsExternal({ items: change }); + const deleted = new Set(['foo']); + database.fireDidChangeItemsExternal({ deleted }); ok(changes.has('foo')); - equal(storage.get('foo', null!), null); + equal(storage.get('foo', undefined), undefined); changes.clear(); // Nothing happens if changing to same value - change.set('foo', undefined!); - database.fireDidChangeItemsExternal({ items: change }); + database.fireDidChangeItemsExternal({ deleted }); equal(changes.size, 0); await storage.close(); @@ -162,6 +172,9 @@ suite('Storage Library', () => { const set1Promise = storage.set('foo', 'bar'); const set2Promise = storage.set('bar', 'foo'); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('foo'), 'bar'); equal(storage.get('bar'), 'foo'); @@ -171,6 +184,7 @@ suite('Storage Library', () => { await storage.close(); equal(setPromiseResolved, true); + equal(flushPromiseResolved, true); storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); @@ -222,6 +236,9 @@ suite('Storage Library', () => { const set2Promise = storage.set('foo', 'bar2'); const set3Promise = storage.set('foo', 'bar3'); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('foo'), 'bar3'); equal(changes.size, 1); ok(changes.has('foo')); @@ -229,6 +246,7 @@ suite('Storage Library', () => { let setPromiseResolved = false; await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true); ok(setPromiseResolved); + ok(flushPromiseResolved); changes = new Set(); @@ -279,7 +297,12 @@ suite('Storage Library', () => { }); }); -suite('SQLite Storage Library', () => { +suite('SQLite Storage Library', function () { + + // Given issues such as https://github.com/microsoft/vscode/issues/108113 + // we see random test failures when accessing the native file system. + this.retries(3); + this.timeout(1000 * 20); function uniqueStorageDir(): string { const id = generateUuid(); @@ -541,8 +564,6 @@ suite('SQLite Storage Library', () => { }); test('real world example', async function () { - this.timeout(20000); - const storageDir = uniqueStorageDir(); await mkdirp(storageDir); diff --git a/src/vs/base/parts/tree/browser/tree.css b/src/vs/base/parts/tree/browser/tree.css deleted file mode 100644 index 3e709334db6..00000000000 --- a/src/vs/base/parts/tree/browser/tree.css +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -.monaco-tree { - height: 100%; - width: 100%; - white-space: nowrap; - user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - position: relative; -} - -.monaco-tree > .monaco-scrollable-element { - height: 100%; -} - -.monaco-tree > .monaco-scrollable-element > .monaco-tree-wrapper { - height: 100%; - width: 100%; - position: relative; -} - -.monaco-tree .monaco-tree-rows { - position: absolute; - width: 100%; - height: 100%; -} - -.monaco-tree .monaco-tree-rows > .monaco-tree-row { - box-sizing: border-box; - cursor: pointer; - overflow: hidden; - width: 100%; - touch-action: none; -} - -.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content { - position: relative; - height: 100%; -} - -.monaco-tree-drag-image { - display: inline-block; - padding: 1px 7px; - border-radius: 10px; - font-size: 12px; - position: absolute; -} - -/* for OS X ballistic scrolling */ -.monaco-tree .monaco-tree-rows > .monaco-tree-row.scrolling { - display: none; -} - -/* Highlighted */ - -.monaco-tree.highlighted .monaco-tree-rows > .monaco-tree-row:not(.highlighted) { - opacity: 0.3; -} diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts deleted file mode 100644 index 82b4015e6dd..00000000000 --- a/src/vs/base/parts/tree/browser/tree.ts +++ /dev/null @@ -1,582 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as Touch from 'vs/base/browser/touch'; -import * as Mouse from 'vs/base/browser/mouseEvent'; -import * as Keyboard from 'vs/base/browser/keyboardEvent'; -import { INavigator } from 'vs/base/common/iterator'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Event } from 'vs/base/common/event'; -import { IAction } from 'vs/base/common/actions'; -import { Color } from 'vs/base/common/color'; -import { IItemCollapseEvent, IItemExpandEvent } from 'vs/base/parts/tree/browser/treeModel'; -import { IDragAndDropData } from 'vs/base/browser/dnd'; - -export interface ITree { - - onDidFocus: Event; - onDidBlur: Event; - onDidChangeFocus: Event; - onDidChangeSelection: Event; - onDidChangeHighlight: Event; - onDidExpandItem: Event; - onDidCollapseItem: Event; - onDidDispose: Event; - onDidScroll: Event; - - /** - * Returns the tree's DOM element. - */ - getHTMLElement(): HTMLElement; - - /** - * Lays out the tree. - * Provide a specific height to save an (expensive) height computation. - */ - layout(height?: number): void; - - /** - * Notifies the tree that is has become visible. - */ - onVisible(): void; - - /** - * Notifies the tree that is has become hidden. - */ - onHidden(): void; - - /** - * Sets the input of the tree. - */ - setInput(element: any): Promise; - - /** - * Returns the tree's input. - */ - getInput(): any; - - /** - * Sets DOM focus on the tree. - */ - domFocus(): void; - - /** - * Returns whether the tree has DOM focus. - */ - isDOMFocused(): boolean; - - /** - * Removes DOM focus from the tree. - */ - domBlur(): void; - - /** - * Refreshes an element. - * Provide no arguments and it will refresh the input element. - */ - refresh(element?: any, recursive?: boolean): Promise; - - /** - * Expands an element. - * The returned promise returns a boolean for whether the element was expanded or not. - */ - expand(element: any): Promise; - - /** - * Expands several elements. - * The returned promise returns a boolean array for whether the elements were expanded or not. - */ - expandAll(elements?: any[]): Promise; - - /** - * Collapses an element. - * The returned promise returns a boolean for whether the element was collapsed or not. - */ - collapse(element: any, recursive?: boolean): Promise; - - /** - * Collapses several elements. - * Provide no arguments and it will recursively collapse all elements in the tree - * The returned promise returns a boolean for whether the elements were collapsed or not. - */ - collapseAll(elements?: any[], recursive?: boolean): Promise; - - /** - * Toggles an element's expansion state. - */ - toggleExpansion(element: any, recursive?: boolean): Promise; - - /** - * Returns whether an element is expanded or not. - */ - isExpanded(element: any): boolean; - - /** - * Reveals an element in the tree. The relativeTop is a value between 0 and 1. The closer to 0 the more the - * element will scroll up to the top. - */ - reveal(element: any, relativeTop?: number): Promise; - - /** - * Returns the currently highlighted element. - */ - getHighlight(includeHidden?: boolean): any; - - /** - * Clears the highlight. - */ - clearHighlight(eventPayload?: any): void; - - /** - * Replaces the current selection with the given elements. - */ - setSelection(elements: any[], eventPayload?: any): void; - - /** - * Returns the currently selected elements. - */ - getSelection(includeHidden?: boolean): any[]; - - /** - * Clears the selection. - */ - clearSelection(eventPayload?: any): void; - - /** - * Sets the focused element. - */ - setFocus(element?: any, eventPayload?: any): void; - - /** - * Returns focused element. - */ - getFocus(includeHidden?: boolean): any; - - /** - * Focuses the next `count`-nth element, in visible order. - */ - focusNext(count?: number, eventPayload?: any): void; - - /** - * Focuses the previous `count`-nth element, in visible order. - */ - focusPrevious(count?: number, eventPayload?: any): void; - - /** - * Focuses the currently focused element's parent. - */ - focusParent(eventPayload?: any): void; - - /** - * Focuses the first child of the currently focused element. - */ - focusFirstChild(eventPayload?: any): void; - - /** - * Focuses the second element, in visible order. Will focus the first - * child from the provided element's parent if any. - */ - focusFirst(eventPayload?: any, from?: any): void; - - /** - * Focuses the nth element, in visible order. - */ - focusNth(index: number, eventPayload?: any): void; - - /** - * Focuses the last element, in visible order. Will focus the last - * child from the provided element's parent if any. - */ - focusLast(eventPayload?: any, from?: any): void; - - /** - * Focuses the element at the end of the next page, in visible order. - */ - focusNextPage(eventPayload?: any): void; - - /** - * Focuses the element at the beginning of the previous page, in visible order. - */ - focusPreviousPage(eventPayload?: any): void; - - /** - * Clears the focus. - */ - clearFocus(eventPayload?: any): void; - - /** - * Returns a navigator which allows to discover the visible and - * expanded elements in the tree. - */ - getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator; - - /** - * Apply styles to the tree. - */ - style(styles: ITreeStyles): void; - - /** - * Disposes the tree - */ - dispose(): void; -} - -export interface IDataSource { - - /** - * Returns the unique identifier of the given element. - * No more than one element may use a given identifier. - * - * You should not attempt to "move" an element to a different - * parent by keeping its ID. The idea here is to have tree location - * related IDs (e.g. full file path, in the Explorer example). - */ - getId(tree: ITree, element: any): string; - - /** - * Returns a boolean value indicating whether the element has children. - */ - hasChildren(tree: ITree, element: any): boolean; - - /** - * Returns the element's children as an array in a promise. - */ - getChildren(tree: ITree, element: any): Promise; - - /** - * Returns the element's parent in a promise. - */ - getParent(tree: ITree, element: any): Promise; - - /** - * Returns whether an element should be expanded when first added to the tree. - */ - shouldAutoexpand?(tree: ITree, element: any): boolean; -} - -export interface IRenderer { - - /** - * Returns the element's height in the tree, in pixels. - */ - getHeight(tree: ITree, element: any): number; - - /** - * Returns a template ID for a given element. This will be used as an identifier - * for the next 3 methods. - */ - getTemplateId(tree: ITree, element: any): string; - - /** - * Renders the template in a DOM element. This method should render all the DOM - * structure for an hypothetical element leaving its contents blank. It should - * return an object bag which will be passed along to `renderElement` and used - * to fill in those blanks. - * - * You should do all DOM creating and object allocation in this method. It - * will be called only a few times. - */ - renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any; - - /** - * Renders an element, given an object bag returned by `renderTemplate`. - * This method should do as little as possible and ideally it should only fill - * in the blanks left by `renderTemplate`. - * - * Try to make this method do as little possible, since it will be called very - * often. - */ - renderElement(tree: ITree, element: any, templateId: string, templateData: any): void; - - /** - * Disposes a template that was once rendered. - */ - disposeTemplate(tree: ITree, templateId: string, templateData: any): void; -} - -export interface IAccessibilityProvider { - - /** - * Given an element in the tree, return the ARIA label that should be associated with the - * item. This helps screen readers to provide a meaningful label for the currently focused - * tree element. - * - * Returning null will not disable ARIA for the element. Instead it is up to the screen reader - * to compute a meaningful label based on the contents of the element in the DOM - * - * See also: https://www.w3.org/TR/wai-aria/states_and_properties#aria-label - */ - getAriaLabel(tree: ITree, element: any): string | null; - - /** - * Given an element in the tree return its aria-posinset. Should be between 1 and aria-setsize - * https://www.w3.org/TR/wai-aria/states_and_properties#aria-posinset - */ - getPosInSet?(tree: ITree, element: any): string; - - /** - * Return the aria-setsize of the tree. - * https://www.w3.org/TR/wai-aria/states_and_properties#aria-setsize - */ - getSetSize?(): string; -} - -export /* abstract */ class ContextMenuEvent { - - private _posx: number; - private _posy: number; - private _target: HTMLElement; - - constructor(posx: number, posy: number, target: HTMLElement) { - this._posx = posx; - this._posy = posy; - this._target = target; - } - - public preventDefault(): void { - // no-op - } - - public stopPropagation(): void { - // no-op - } - - public get posx(): number { - return this._posx; - } - - public get posy(): number { - return this._posy; - } - - public get target(): HTMLElement { - return this._target; - } -} - -export class MouseContextMenuEvent extends ContextMenuEvent { - - private originalEvent: Mouse.IMouseEvent; - - constructor(originalEvent: Mouse.IMouseEvent) { - super(originalEvent.posx, originalEvent.posy, originalEvent.target); - this.originalEvent = originalEvent; - } - - public preventDefault(): void { - this.originalEvent.preventDefault(); - } - - public stopPropagation(): void { - this.originalEvent.stopPropagation(); - } -} - -export class KeyboardContextMenuEvent extends ContextMenuEvent { - - private originalEvent: Keyboard.IKeyboardEvent; - - constructor(posx: number, posy: number, originalEvent: Keyboard.IKeyboardEvent) { - super(posx, posy, originalEvent.target); - this.originalEvent = originalEvent; - } - - public preventDefault(): void { - this.originalEvent.preventDefault(); - } - - public stopPropagation(): void { - this.originalEvent.stopPropagation(); - } -} - -export interface IController { - - /** - * Called when an element is clicked. - */ - onClick(tree: ITree, element: any, event: Mouse.IMouseEvent): boolean; - - /** - * Called when an element is requested for a context menu. - */ - onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean; - - /** - * Called when an element is tapped. - */ - onTap(tree: ITree, element: any, event: Touch.GestureEvent): boolean; - - /** - * Called when a key is pressed down while selecting elements. - */ - onKeyDown(tree: ITree, event: Keyboard.IKeyboardEvent): boolean; - - /** - * Called when a key is released while selecting elements. - */ - onKeyUp(tree: ITree, event: Keyboard.IKeyboardEvent): boolean; - - /** - * Called when a mouse middle button is pressed down on an element. - */ - onMouseMiddleClick?(tree: ITree, element: any, event: Mouse.IMouseEvent): boolean; - - /** - * Called when a mouse button is pressed down on an element. - */ - onMouseDown?(tree: ITree, element: any, event: Mouse.IMouseEvent): boolean; - - /** - * Called when a mouse button goes up on an element. - */ - onMouseUp?(tree: ITree, element: any, event: Mouse.IMouseEvent): boolean; -} - -export const enum DragOverEffect { - COPY, - MOVE -} - -export const enum DragOverBubble { - BUBBLE_DOWN, - BUBBLE_UP -} - -export interface IDragOverReaction { - accept: boolean; - effect?: DragOverEffect; - bubble?: DragOverBubble; - autoExpand?: boolean; -} - -export interface IDragAndDrop { - - /** - * Returns a uri if the given element should be allowed to drag. - * Returns null, otherwise. - */ - getDragURI(tree: ITree, element: any): string | null; - - /** - * Returns a label to display when dragging the element. - */ - getDragLabel?(tree: ITree, elements: any[]): string; - - /** - * Sent when the drag operation is starting. - */ - onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: Mouse.DragMouseEvent): void; - - /** - * Returns a DragOverReaction indicating whether sources can be - * dropped into target or some parent of the target. - */ - onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: Mouse.DragMouseEvent): IDragOverReaction | null; - - /** - * Handles the action of dropping sources into target. - */ - drop(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: Mouse.DragMouseEvent): void; -} - -export interface IFilter { - - /** - * Returns whether the given element should be visible. - */ - isVisible(tree: ITree, element: any): boolean; -} - -export interface ISorter { - - /** - * Compare two elements in the viewer to define the sorting order. - */ - compare(tree: ITree, element: any, otherElement: any): number; -} - -// Events - -export interface ISelectionEvent { - selection: any[]; - payload?: any; -} - -export interface IFocusEvent { - focus: any; - payload?: any; -} - -export interface IHighlightEvent { - highlight: any; - payload?: any; -} - -// Options - -export interface ITreeConfiguration { - dataSource: IDataSource; - renderer?: IRenderer; - controller?: IController; - dnd?: IDragAndDrop; - filter?: IFilter; - sorter?: ISorter; - accessibilityProvider?: IAccessibilityProvider; - styler?: ITreeStyler; -} - -export interface ITreeOptions extends ITreeStyles { - twistiePixels?: number; - showTwistie?: boolean; - indentPixels?: number; - verticalScrollMode?: ScrollbarVisibility; - horizontalScrollMode?: ScrollbarVisibility; - alwaysFocused?: boolean; - autoExpandSingleChildren?: boolean; - useShadows?: boolean; - paddingOnRow?: boolean; - ariaLabel?: string; - keyboardSupport?: boolean; - preventRootFocus?: boolean; - showLoading?: boolean; -} - -export interface ITreeStyler { - style(styles: ITreeStyles): void; -} - -export interface ITreeStyles { - listFocusBackground?: Color; - listFocusForeground?: Color; - listActiveSelectionBackground?: Color; - listActiveSelectionForeground?: Color; - listFocusAndSelectionBackground?: Color; - listFocusAndSelectionForeground?: Color; - listInactiveSelectionBackground?: Color; - listInactiveSelectionForeground?: Color; - listHoverBackground?: Color; - listHoverForeground?: Color; - listDropBackground?: Color; - listFocusOutline?: Color; -} - -export interface ITreeContext extends ITreeConfiguration { - tree: ITree; - options: ITreeOptions; -} - -export interface IActionProvider { - - /** - * Returns whether or not the element has actions. These show up in place right to the element in the tree. - */ - hasActions(tree: ITree | null, element: any): boolean; - - /** - * Returns an array with the actions of the element that should show up in place right to the element in the tree. - */ - getActions(tree: ITree | null, element: any): ReadonlyArray | null; -} diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts deleted file mode 100644 index f91ca2bcf84..00000000000 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ /dev/null @@ -1,574 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; -import * as platform from 'vs/base/common/platform'; -import * as touch from 'vs/base/browser/touch'; -import * as errors from 'vs/base/common/errors'; -import * as dom from 'vs/base/browser/dom'; -import * as mouse from 'vs/base/browser/mouseEvent'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import * as _ from 'vs/base/parts/tree/browser/tree'; -import { IDragAndDropData } from 'vs/base/browser/dnd'; -import { KeyCode, KeyMod, Keybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; - -export interface IKeyBindingCallback { - (tree: _.ITree, event: IKeyboardEvent): void; -} - -export interface ICancelableEvent { - preventDefault(): void; - stopPropagation(): void; -} - -export const enum ClickBehavior { - - /** - * Handle the click when the mouse button is pressed but not released yet. - */ - ON_MOUSE_DOWN, - - /** - * Handle the click when the mouse button is released. - */ - ON_MOUSE_UP -} - -export const enum OpenMode { - SINGLE_CLICK, - DOUBLE_CLICK -} - -export interface IControllerOptions { - clickBehavior?: ClickBehavior; - openMode?: OpenMode; - keyboardSupport?: boolean; -} - -interface IKeybindingDispatcherItem { - keybinding: Keybinding | null; - callback: IKeyBindingCallback; -} - -export class KeybindingDispatcher { - - private _arr: IKeybindingDispatcherItem[]; - - constructor() { - this._arr = []; - } - - public has(keybinding: KeyCode): boolean { - let target = createKeybinding(keybinding, platform.OS); - if (target !== null) { - for (const a of this._arr) { - if (target.equals(a.keybinding)) { - return true; - } - } - } - return false; - } - - public set(keybinding: number, callback: IKeyBindingCallback) { - this._arr.push({ - keybinding: createKeybinding(keybinding, platform.OS), - callback: callback - }); - } - - public dispatch(keybinding: SimpleKeybinding): IKeyBindingCallback | null { - // Loop from the last to the first to handle overwrites - for (let i = this._arr.length - 1; i >= 0; i--) { - let item = this._arr[i]; - if (keybinding.toChord().equals(item.keybinding)) { - return item.callback; - } - } - return null; - } -} - -export class DefaultController implements _.IController { - - protected downKeyBindingDispatcher: KeybindingDispatcher; - protected upKeyBindingDispatcher: KeybindingDispatcher; - - private options: IControllerOptions; - - constructor(options: IControllerOptions = { clickBehavior: ClickBehavior.ON_MOUSE_DOWN, keyboardSupport: true, openMode: OpenMode.SINGLE_CLICK }) { - this.options = options; - - this.downKeyBindingDispatcher = new KeybindingDispatcher(); - this.upKeyBindingDispatcher = new KeybindingDispatcher(); - - if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) { - this.downKeyBindingDispatcher.set(KeyCode.UpArrow, (t, e) => this.onUp(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.DownArrow, (t, e) => this.onDown(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.LeftArrow, (t, e) => this.onLeft(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.RightArrow, (t, e) => this.onRight(t, e)); - if (platform.isMacintosh) { - this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.UpArrow, (t, e) => this.onLeft(t, e)); - this.downKeyBindingDispatcher.set(KeyMod.WinCtrl | KeyCode.KEY_N, (t, e) => this.onDown(t, e)); - this.downKeyBindingDispatcher.set(KeyMod.WinCtrl | KeyCode.KEY_P, (t, e) => this.onUp(t, e)); - } - this.downKeyBindingDispatcher.set(KeyCode.PageUp, (t, e) => this.onPageUp(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.PageDown, (t, e) => this.onPageDown(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.Home, (t, e) => this.onHome(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.End, (t, e) => this.onEnd(t, e)); - - this.downKeyBindingDispatcher.set(KeyCode.Space, (t, e) => this.onSpace(t, e)); - this.downKeyBindingDispatcher.set(KeyCode.Escape, (t, e) => this.onEscape(t, e)); - - this.upKeyBindingDispatcher.set(KeyCode.Enter, this.onEnter.bind(this)); - this.upKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Enter, this.onEnter.bind(this)); - } - } - - public onMouseDown(tree: _.ITree, element: any, event: mouse.IMouseEvent, origin: string = 'mouse'): boolean { - if (this.options.clickBehavior === ClickBehavior.ON_MOUSE_DOWN && (event.leftButton || event.middleButton)) { - if (event.target) { - if (event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return false; // Ignore event if target is a form input field (avoids browser specific issues) - } - - if (dom.findParentWithClass(event.target, 'scrollbar', 'monaco-tree')) { - return false; - } - - if (dom.findParentWithClass(event.target, 'monaco-action-bar', 'row')) { // TODO@Joao not very nice way of checking for the action bar (implicit knowledge) - return false; // Ignore event if target is over an action bar of the row - } - } - - // Propagate to onLeftClick now - return this.onLeftClick(tree, element, event, origin); - } - - return false; - } - - public onClick(tree: _.ITree, element: any, event: mouse.IMouseEvent): boolean { - const isMac = platform.isMacintosh; - - // A Ctrl click on the Mac is a context menu event - if (isMac && event.ctrlKey) { - event.preventDefault(); - event.stopPropagation(); - return false; - } - - if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return false; // Ignore event if target is a form input field (avoids browser specific issues) - } - - if (this.options.clickBehavior === ClickBehavior.ON_MOUSE_DOWN && (event.leftButton || event.middleButton)) { - return false; // Already handled by onMouseDown - } - - return this.onLeftClick(tree, element, event); - } - - protected onLeftClick(tree: _.ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean { - const event = eventish; - const payload = { origin: origin, originalEvent: eventish, didClickOnTwistie: this.isClickOnTwistie(event) }; - - if (tree.getInput() === element) { - tree.clearFocus(payload); - tree.clearSelection(payload); - } else { - const isSingleMouseDown = eventish && event.browserEvent && event.browserEvent.type === 'mousedown' && event.browserEvent.detail === 1; - if (!isSingleMouseDown) { - eventish.preventDefault(); // we cannot preventDefault onMouseDown with single click because this would break DND otherwise - } - eventish.stopPropagation(); - - tree.domFocus(); - tree.setSelection([element], payload); - tree.setFocus(element, payload); - - if (this.shouldToggleExpansion(element, event, origin)) { - if (tree.isExpanded(element)) { - tree.collapse(element).then(undefined, errors.onUnexpectedError); - } else { - tree.expand(element).then(undefined, errors.onUnexpectedError); - } - } - } - - return true; - } - - protected shouldToggleExpansion(element: any, event: mouse.IMouseEvent, origin: string): boolean { - const isDoubleClick = (origin === 'mouse' && event.detail === 2); - return this.openOnSingleClick || isDoubleClick || this.isClickOnTwistie(event); - } - - protected setOpenMode(openMode: OpenMode) { - this.options.openMode = openMode; - } - - protected get openOnSingleClick(): boolean { - return this.options.openMode === OpenMode.SINGLE_CLICK; - } - - protected isClickOnTwistie(event: mouse.IMouseEvent): boolean { - let element = event.target as HTMLElement; - - if (!dom.hasClass(element, 'content')) { - return false; - } - - const twistieStyle = window.getComputedStyle(element, ':before'); - - if (twistieStyle.backgroundImage === 'none' || twistieStyle.display === 'none') { - return false; - } - - const twistieWidth = parseInt(twistieStyle.width!) + parseInt(twistieStyle.paddingRight!); - return event.browserEvent.offsetX <= twistieWidth; - } - - public onContextMenu(tree: _.ITree, element: any, event: _.ContextMenuEvent): boolean { - if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return false; // allow context menu on input fields - } - - // Prevent native context menu from showing up - if (event) { - event.preventDefault(); - event.stopPropagation(); - } - - return false; - } - - public onTap(tree: _.ITree, element: any, event: touch.GestureEvent): boolean { - const target = event.initialTarget; - - if (target && target.tagName && target.tagName.toLowerCase() === 'input') { - return false; // Ignore event if target is a form input field (avoids browser specific issues) - } - - return this.onLeftClick(tree, element, event, 'touch'); - } - - public onKeyDown(tree: _.ITree, event: IKeyboardEvent): boolean { - return this.onKey(this.downKeyBindingDispatcher, tree, event); - } - - public onKeyUp(tree: _.ITree, event: IKeyboardEvent): boolean { - return this.onKey(this.upKeyBindingDispatcher, tree, event); - } - - private onKey(bindings: KeybindingDispatcher, tree: _.ITree, event: IKeyboardEvent): boolean { - const handler: any = bindings.dispatch(event.toKeybinding()); - if (handler) { - // TODO: TS 3.1 upgrade. Why are we checking against void? - if (handler(tree, event)) { - event.preventDefault(); - event.stopPropagation(); - return true; - } - } - return false; - } - - protected onUp(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - tree.focusPrevious(1, payload); - tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onPageUp(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - tree.focusPreviousPage(payload); - tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onDown(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - tree.focusNext(1, payload); - tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onPageDown(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - tree.focusNextPage(payload); - tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onHome(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - tree.focusFirst(payload); - tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onEnd(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - tree.focusLast(payload); - tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onLeft(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - const focus = tree.getFocus(); - tree.collapse(focus).then(didCollapse => { - if (focus && !didCollapse) { - tree.focusParent(payload); - return tree.reveal(tree.getFocus()); - } - return undefined; - }).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onRight(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - } else { - const focus = tree.getFocus(); - tree.expand(focus).then(didExpand => { - if (focus && !didExpand) { - tree.focusFirstChild(payload); - return tree.reveal(tree.getFocus()); - } - return undefined; - }).then(undefined, errors.onUnexpectedError); - } - return true; - } - - protected onEnter(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - return false; - } - const focus = tree.getFocus(); - if (focus) { - tree.setSelection([focus], payload); - } - return true; - } - - protected onSpace(tree: _.ITree, event: IKeyboardEvent): boolean { - if (tree.getHighlight()) { - return false; - } - const focus = tree.getFocus(); - if (focus) { - tree.toggleExpansion(focus); - } - return true; - } - - protected onEscape(tree: _.ITree, event: IKeyboardEvent): boolean { - const payload = { origin: 'keyboard', originalEvent: event }; - - if (tree.getHighlight()) { - tree.clearHighlight(payload); - return true; - } - - if (tree.getSelection().length) { - tree.clearSelection(payload); - return true; - } - - if (tree.getFocus()) { - tree.clearFocus(payload); - return true; - } - - return false; - } -} - -export class DefaultDragAndDrop implements _.IDragAndDrop { - - public getDragURI(tree: _.ITree, element: any): string | null { - return null; - } - - public onDragStart(tree: _.ITree, data: IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { - return; - } - - public onDragOver(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction | null { - return null; - } - - public drop(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { - return; - } -} - -export class DefaultFilter implements _.IFilter { - - public isVisible(tree: _.ITree, element: any): boolean { - return true; - } -} - -export class DefaultSorter implements _.ISorter { - - public compare(tree: _.ITree, element: any, otherElement: any): number { - return 0; - } -} - -export class DefaultAccessibilityProvider implements _.IAccessibilityProvider { - - getAriaLabel(tree: _.ITree, element: any): string | null { - return null; - } -} - -export class DefaultTreestyler implements _.ITreeStyler { - - constructor(private styleElement: HTMLStyleElement, private selectorSuffix?: string) { } - - style(styles: _.ITreeStyles): void { - const suffix = this.selectorSuffix ? `.${this.selectorSuffix}` : ''; - const content: string[] = []; - - if (styles.listFocusBackground) { - content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { background-color: ${styles.listFocusBackground}; }`); - } - - if (styles.listFocusForeground) { - content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { color: ${styles.listFocusForeground}; }`); - } - - if (styles.listActiveSelectionBackground) { - content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${styles.listActiveSelectionBackground}; }`); - } - - if (styles.listActiveSelectionForeground) { - content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${styles.listActiveSelectionForeground}; }`); - } - - if (styles.listFocusAndSelectionBackground) { - content.push(` - .monaco-tree-drag-image, - .monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { background-color: ${styles.listFocusAndSelectionBackground}; } - `); - } - - if (styles.listFocusAndSelectionForeground) { - content.push(` - .monaco-tree-drag-image, - .monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { color: ${styles.listFocusAndSelectionForeground}; } - `); - } - - if (styles.listInactiveSelectionBackground) { - content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${styles.listInactiveSelectionBackground}; }`); - } - - if (styles.listInactiveSelectionForeground) { - content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${styles.listInactiveSelectionForeground}; }`); - } - - if (styles.listHoverBackground) { - content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); - } - - if (styles.listHoverForeground) { - content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); - } - - if (styles.listDropBackground) { - content.push(` - .monaco-tree${suffix} .monaco-tree-wrapper.drop-target, - .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } - `); - } - - if (styles.listFocusOutline) { - content.push(` - .monaco-tree-drag-image { border: 1px solid ${styles.listFocusOutline}; background: #000; } - .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row { border: 1px solid transparent; } - .monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { border: 1px dotted ${styles.listFocusOutline}; } - .monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid ${styles.listFocusOutline}; } - .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid ${styles.listFocusOutline}; } - .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { border: 1px dashed ${styles.listFocusOutline}; } - .monaco-tree${suffix} .monaco-tree-wrapper.drop-target, - .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { border: 1px dashed ${styles.listFocusOutline}; } - `); - } - - const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; - } - } -} - -export class CollapseAllAction extends Action { - - constructor(private viewer: _.ITree, enabled: boolean) { - super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled); - } - - public run(context?: any): Promise { - if (this.viewer.getHighlight()) { - return Promise.resolve(); // Global action disabled if user is in edit mode from another action - } - - this.viewer.collapseAll(); - this.viewer.clearSelection(); - this.viewer.clearFocus(); - this.viewer.domFocus(); - this.viewer.focusFirst(); - - return Promise.resolve(); - } -} diff --git a/src/vs/base/parts/tree/browser/treeDnd.ts b/src/vs/base/parts/tree/browser/treeDnd.ts deleted file mode 100644 index 810e6ce98ee..00000000000 --- a/src/vs/base/parts/tree/browser/treeDnd.ts +++ /dev/null @@ -1,73 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as _ from 'vs/base/parts/tree/browser/tree'; -import { IDragAndDropData } from 'vs/base/browser/dnd'; - -export class ElementsDragAndDropData implements IDragAndDropData { - - private elements: any[]; - - constructor(elements: any[]) { - this.elements = elements; - } - - public update(dataTransfer: DataTransfer): void { - // no-op - } - - public getData(): any { - return this.elements; - } -} - -export class ExternalElementsDragAndDropData implements IDragAndDropData { - - private elements: any[]; - - constructor(elements: any[]) { - this.elements = elements; - } - - public update(dataTransfer: DataTransfer): void { - // no-op - } - - public getData(): any { - return this.elements; - } -} - -export class DesktopDragAndDropData implements IDragAndDropData { - - private types: any[]; - private files: any[]; - - constructor() { - this.types = []; - this.files = []; - } - - public update(dataTransfer: DataTransfer): void { - if (dataTransfer.types) { - this.types = []; - Array.prototype.push.apply(this.types, dataTransfer.types as any); - } - - if (dataTransfer.files) { - this.files = []; - Array.prototype.push.apply(this.files, dataTransfer.files as any); - - this.files = this.files.filter(f => f.size || f.type); - } - } - - public getData(): any { - return { - types: this.types, - files: this.files - }; - } -} \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts deleted file mode 100644 index 8b8f7b28078..00000000000 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ /dev/null @@ -1,275 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./tree'; -import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; -import * as Model from 'vs/base/parts/tree/browser/treeModel'; -import * as View from './treeView'; -import * as _ from 'vs/base/parts/tree/browser/tree'; -import { INavigator, MappedNavigator } from 'vs/base/common/iterator'; -import { Event, Emitter, Relay } from 'vs/base/common/event'; -import { Color } from 'vs/base/common/color'; -import { mixin } from 'vs/base/common/objects'; - -export class TreeContext implements _.ITreeContext { - - public tree: _.ITree; - public configuration: _.ITreeConfiguration; - public options: _.ITreeOptions; - - public dataSource: _.IDataSource; - public renderer?: _.IRenderer; - public controller: _.IController; - public dnd: _.IDragAndDrop; - public filter: _.IFilter; - public sorter?: _.ISorter; - public accessibilityProvider: _.IAccessibilityProvider; - public styler?: _.ITreeStyler; - - constructor(tree: _.ITree, configuration: _.ITreeConfiguration, options: _.ITreeOptions = {}) { - this.tree = tree; - this.configuration = configuration; - this.options = options; - - if (!configuration.dataSource) { - throw new Error('You must provide a Data Source to the tree.'); - } - - this.dataSource = configuration.dataSource; - this.renderer = configuration.renderer; - this.controller = configuration.controller || new TreeDefaults.DefaultController({ clickBehavior: TreeDefaults.ClickBehavior.ON_MOUSE_UP, keyboardSupport: typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport }); - this.dnd = configuration.dnd || new TreeDefaults.DefaultDragAndDrop(); - this.filter = configuration.filter || new TreeDefaults.DefaultFilter(); - this.sorter = configuration.sorter; - this.accessibilityProvider = configuration.accessibilityProvider || new TreeDefaults.DefaultAccessibilityProvider(); - this.styler = configuration.styler; - } -} - -const defaultStyles: _.ITreeStyles = { - listFocusBackground: Color.fromHex('#073655'), - listActiveSelectionBackground: Color.fromHex('#0E639C'), - listActiveSelectionForeground: Color.fromHex('#FFFFFF'), - listFocusAndSelectionBackground: Color.fromHex('#094771'), - listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'), - listInactiveSelectionBackground: Color.fromHex('#3F3F46'), - listHoverBackground: Color.fromHex('#2A2D2E'), - listDropBackground: Color.fromHex('#383B3D') -}; - -export class Tree implements _.ITree { - - private container: HTMLElement; - - private context: _.ITreeContext; - private model: Model.TreeModel; - private view: View.TreeView; - - private _onDidChangeFocus = new Relay<_.IFocusEvent>(); - readonly onDidChangeFocus: Event<_.IFocusEvent> = this._onDidChangeFocus.event; - private _onDidChangeSelection = new Relay<_.ISelectionEvent>(); - readonly onDidChangeSelection: Event<_.ISelectionEvent> = this._onDidChangeSelection.event; - private _onHighlightChange = new Relay<_.IHighlightEvent>(); - readonly onDidChangeHighlight: Event<_.IHighlightEvent> = this._onHighlightChange.event; - private _onDidExpandItem = new Relay(); - readonly onDidExpandItem: Event = this._onDidExpandItem.event; - private _onDidCollapseItem = new Relay(); - readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; - private readonly _onDispose = new Emitter(); - readonly onDidDispose: Event = this._onDispose.event; - - constructor(container: HTMLElement, configuration: _.ITreeConfiguration, options: _.ITreeOptions = {}) { - this.container = container; - mixin(options, defaultStyles, false); - - options.twistiePixels = typeof options.twistiePixels === 'number' ? options.twistiePixels : 32; - options.showTwistie = options.showTwistie === false ? false : true; - options.indentPixels = typeof options.indentPixels === 'number' ? options.indentPixels : 12; - options.alwaysFocused = options.alwaysFocused === true ? true : false; - options.useShadows = options.useShadows === false ? false : true; - options.paddingOnRow = options.paddingOnRow === false ? false : true; - options.showLoading = options.showLoading === false ? false : true; - - this.context = new TreeContext(this, configuration, options); - this.model = new Model.TreeModel(this.context); - this.view = new View.TreeView(this.context, this.container); - - this.view.setModel(this.model); - - this._onDidChangeFocus.input = this.model.onDidFocus; - this._onDidChangeSelection.input = this.model.onDidSelect; - this._onHighlightChange.input = this.model.onDidHighlight; - this._onDidExpandItem.input = this.model.onDidExpandItem; - this._onDidCollapseItem.input = this.model.onDidCollapseItem; - } - - public style(styles: _.ITreeStyles): void { - this.view.applyStyles(styles); - } - - get onDidFocus(): Event { - return this.view.onDOMFocus; - } - - get onDidBlur(): Event { - return this.view.onDOMBlur; - } - - get onDidScroll(): Event { - return this.view.onDidScroll; - } - - public getHTMLElement(): HTMLElement { - return this.view.getHTMLElement(); - } - - public layout(height?: number, width?: number): void { - this.view.layout(height, width); - } - - public domFocus(): void { - this.view.focus(); - } - - public isDOMFocused(): boolean { - return this.view.isFocused(); - } - - public domBlur(): void { - this.view.blur(); - } - - public onVisible(): void { - this.view.onVisible(); - } - - public onHidden(): void { - this.view.onHidden(); - } - - public setInput(element: any): Promise { - return this.model.setInput(element); - } - - public getInput(): any { - return this.model.getInput(); - } - - public refresh(element: any = null, recursive = true): Promise { - return this.model.refresh(element, recursive); - } - - public expand(element: any): Promise { - return this.model.expand(element); - } - - public expandAll(elements: any[]): Promise { - return this.model.expandAll(elements); - } - - public collapse(element: any, recursive: boolean = false): Promise { - return this.model.collapse(element, recursive); - } - - public collapseAll(elements: any[] | null = null, recursive: boolean = false): Promise { - return this.model.collapseAll(elements, recursive); - } - - public toggleExpansion(element: any, recursive: boolean = false): Promise { - return this.model.toggleExpansion(element, recursive); - } - - public isExpanded(element: any): boolean { - return this.model.isExpanded(element); - } - - public reveal(element: any, relativeTop: number | null = null): Promise { - return this.model.reveal(element, relativeTop); - } - - public getHighlight(): any { - return this.model.getHighlight(); - } - - public clearHighlight(eventPayload?: any): void { - this.model.setHighlight(null, eventPayload); - } - - public setSelection(elements: any[], eventPayload?: any): void { - this.model.setSelection(elements, eventPayload); - } - - public getSelection(): any[] { - return this.model.getSelection(); - } - - public clearSelection(eventPayload?: any): void { - this.model.setSelection([], eventPayload); - } - - public setFocus(element?: any, eventPayload?: any): void { - this.model.setFocus(element, eventPayload); - } - - public getFocus(): any { - return this.model.getFocus(); - } - - public focusNext(count?: number, eventPayload?: any): void { - this.model.focusNext(count, eventPayload); - } - - public focusPrevious(count?: number, eventPayload?: any): void { - this.model.focusPrevious(count, eventPayload); - } - - public focusParent(eventPayload?: any): void { - this.model.focusParent(eventPayload); - } - - public focusFirstChild(eventPayload?: any): void { - this.model.focusFirstChild(eventPayload); - } - - public focusFirst(eventPayload?: any, from?: any): void { - this.model.focusFirst(eventPayload, from); - } - - public focusNth(index: number, eventPayload?: any): void { - this.model.focusNth(index, eventPayload); - } - - public focusLast(eventPayload?: any, from?: any): void { - this.model.focusLast(eventPayload, from); - } - - public focusNextPage(eventPayload?: any): void { - this.view.focusNextPage(eventPayload); - } - - public focusPreviousPage(eventPayload?: any): void { - this.view.focusPreviousPage(eventPayload); - } - - public clearFocus(eventPayload?: any): void { - this.model.setFocus(null, eventPayload); - } - - getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator { - return new MappedNavigator(this.model.getNavigator(fromElement, subTreeOnly), i => i && i.getElement()); - } - - public dispose(): void { - this._onDispose.fire(); - this.model.dispose(); - this.view.dispose(); - this._onDidChangeFocus.dispose(); - this._onDidChangeSelection.dispose(); - this._onHighlightChange.dispose(); - this._onDidExpandItem.dispose(); - this._onDidCollapseItem.dispose(); - this._onDispose.dispose(); - } -} diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts deleted file mode 100644 index 30df6dc07d7..00000000000 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ /dev/null @@ -1,1494 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as Assert from 'vs/base/common/assert'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { INavigator } from 'vs/base/common/iterator'; -import * as _ from './tree'; -import { Event, Emitter, EventMultiplexer, Relay } from 'vs/base/common/event'; - -interface IMap { [id: string]: T; } -interface IItemMap extends IMap { } -interface ITraitMap extends IMap { } - -export class LockData { - - private _item: Item; - private _onDispose?= new Emitter(); - readonly onDispose: Event = this._onDispose!.event; - - constructor(item: Item) { - this._item = item; - } - - get item(): Item { - return this._item; - } - - dispose(): void { - if (this._onDispose) { - this._onDispose.fire(); - this._onDispose.dispose(); - this._onDispose = undefined; - } - } -} - -export class Lock { - - /* When refreshing tree items, the tree's structured can be altered, by - inserting and removing sub-items. This lock helps to manage several - possibly-structure-changing calls. - - API-wise, there are two possibly-structure-changing: refresh(...), - expand(...) and collapse(...). All these calls must call Lock#run(...). - - Any call to Lock#run(...) needs to provide the affecting item and a - callback to execute when unlocked. It must also return a promise - which fulfills once the operation ends. Once it is called, there - are three possibilities: - - - Nothing is currently running. The affecting item is remembered, and - the callback is executed. - - - Or, there are on-going operations. There are two outcomes: - - - The affecting item intersects with any other affecting items - of on-going run calls. In such a case, the given callback should - be executed only when the on-going one completes. - - - Or, it doesn't. In such a case, both operations can be run in - parallel. - - Note: two items A and B intersect if A is a descendant of B, or - vice-versa. - */ - - private locks: { [id: string]: LockData; }; - - constructor() { - this.locks = Object.create({}); - } - - public isLocked(item: Item): boolean { - return !!this.locks[item.id]; - } - - public run(item: Item, fn: () => Promise): Promise { - const lock = this.getLock(item); - - if (lock) { - return new Promise((c, e) => { - Event.once(lock.onDispose)(() => { - return this.run(item, fn).then(c, e); - }); - }); - } - - let result: Promise; - - return new Promise((c, e) => { - - if (item.isDisposed()) { - return e(new Error('Item is disposed.')); - } - - let lock = this.locks[item.id] = new LockData(item); - - result = fn().then((r) => { - delete this.locks[item.id]; - lock.dispose(); - - return r; - }).then(c, e); - - return result; - }); - } - - private getLock(item: Item): LockData | null { - let key: string; - - for (key in this.locks) { - let lock = this.locks[key]; - - if (item.intersects(lock.item)) { - return lock; - } - } - - return null; - } -} - -export class ItemRegistry { - - private _isDisposed = false; - private items: IMap<{ item: Item; disposable: IDisposable; }>; - - private _onDidRevealItem = new EventMultiplexer(); - readonly onDidRevealItem: Event = this._onDidRevealItem.event; - private _onExpandItem = new EventMultiplexer(); - readonly onExpandItem: Event = this._onExpandItem.event; - private _onDidExpandItem = new EventMultiplexer(); - readonly onDidExpandItem: Event = this._onDidExpandItem.event; - private _onCollapseItem = new EventMultiplexer(); - readonly onCollapseItem: Event = this._onCollapseItem.event; - private _onDidCollapseItem = new EventMultiplexer(); - readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; - private _onDidAddTraitItem = new EventMultiplexer(); - readonly onDidAddTraitItem: Event = this._onDidAddTraitItem.event; - private _onDidRemoveTraitItem = new EventMultiplexer(); - readonly onDidRemoveTraitItem: Event = this._onDidRemoveTraitItem.event; - private _onDidRefreshItem = new EventMultiplexer(); - readonly onDidRefreshItem: Event = this._onDidRefreshItem.event; - private _onRefreshItemChildren = new EventMultiplexer(); - readonly onRefreshItemChildren: Event = this._onRefreshItemChildren.event; - private _onDidRefreshItemChildren = new EventMultiplexer(); - readonly onDidRefreshItemChildren: Event = this._onDidRefreshItemChildren.event; - private _onDidDisposeItem = new EventMultiplexer(); - readonly onDidDisposeItem: Event = this._onDidDisposeItem.event; - - constructor() { - this.items = {}; - } - - public register(item: Item): void { - Assert.ok(!this.isRegistered(item.id), 'item already registered: ' + item.id); - - const disposable = combinedDisposable( - this._onDidRevealItem.add(item.onDidReveal), - this._onExpandItem.add(item.onExpand), - this._onDidExpandItem.add(item.onDidExpand), - this._onCollapseItem.add(item.onCollapse), - this._onDidCollapseItem.add(item.onDidCollapse), - this._onDidAddTraitItem.add(item.onDidAddTrait), - this._onDidRemoveTraitItem.add(item.onDidRemoveTrait), - this._onDidRefreshItem.add(item.onDidRefresh), - this._onRefreshItemChildren.add(item.onRefreshChildren), - this._onDidRefreshItemChildren.add(item.onDidRefreshChildren), - this._onDidDisposeItem.add(item.onDidDispose) - ); - - this.items[item.id] = { item, disposable }; - } - - public deregister(item: Item): void { - Assert.ok(this.isRegistered(item.id), 'item not registered: ' + item.id); - this.items[item.id].disposable.dispose(); - delete this.items[item.id]; - } - - public isRegistered(id: string): boolean { - return this.items.hasOwnProperty(id); - } - - public getItem(id: string): Item | null { - const result = this.items[id]; - return result ? result.item : null; - } - - public dispose(): void { - this.items = {}; - - this._onDidRevealItem.dispose(); - this._onExpandItem.dispose(); - this._onDidExpandItem.dispose(); - this._onCollapseItem.dispose(); - this._onDidCollapseItem.dispose(); - this._onDidAddTraitItem.dispose(); - this._onDidRemoveTraitItem.dispose(); - this._onDidRefreshItem.dispose(); - this._onRefreshItemChildren.dispose(); - this._onDidRefreshItemChildren.dispose(); - - this._isDisposed = true; - } - - public isDisposed(): boolean { - return this._isDisposed; - } -} - -export interface IBaseItemEvent { - item: Item; -} - -export interface IItemRefreshEvent extends IBaseItemEvent { } -export interface IItemExpandEvent extends IBaseItemEvent { } -export interface IItemCollapseEvent extends IBaseItemEvent { } - -export interface IItemTraitEvent extends IBaseItemEvent { - trait: string; -} - -export interface IItemRevealEvent extends IBaseItemEvent { - relativeTop: number | null; -} - -export interface IItemChildrenRefreshEvent extends IBaseItemEvent { - isNested: boolean; -} - -export class Item { - - private registry: ItemRegistry; - private context: _.ITreeContext; - private element: any; - private lock: Lock; - - public id: string; - - private needsChildrenRefresh: boolean; - private doesHaveChildren: boolean; - - public parent: Item | null; - public previous: Item | null; - public next: Item | null; - public firstChild: Item | null; - public lastChild: Item | null; - - private height: number; - private depth: number; - - private visible: boolean; - private expanded: boolean; - - private traits: { [trait: string]: boolean; }; - - private readonly _onDidCreate = new Emitter(); - readonly onDidCreate: Event = this._onDidCreate.event; - private readonly _onDidReveal = new Emitter(); - readonly onDidReveal: Event = this._onDidReveal.event; - private readonly _onExpand = new Emitter(); - readonly onExpand: Event = this._onExpand.event; - private readonly _onDidExpand = new Emitter(); - readonly onDidExpand: Event = this._onDidExpand.event; - private readonly _onCollapse = new Emitter(); - readonly onCollapse: Event = this._onCollapse.event; - private readonly _onDidCollapse = new Emitter(); - readonly onDidCollapse: Event = this._onDidCollapse.event; - private readonly _onDidAddTrait = new Emitter(); - readonly onDidAddTrait: Event = this._onDidAddTrait.event; - private readonly _onDidRemoveTrait = new Emitter(); - readonly onDidRemoveTrait: Event = this._onDidRemoveTrait.event; - private readonly _onDidRefresh = new Emitter(); - readonly onDidRefresh: Event = this._onDidRefresh.event; - private readonly _onRefreshChildren = new Emitter(); - readonly onRefreshChildren: Event = this._onRefreshChildren.event; - private readonly _onDidRefreshChildren = new Emitter(); - readonly onDidRefreshChildren: Event = this._onDidRefreshChildren.event; - private readonly _onDidDispose = new Emitter(); - readonly onDidDispose: Event = this._onDidDispose.event; - - private _isDisposed: boolean; - - constructor(id: string, registry: ItemRegistry, context: _.ITreeContext, lock: Lock, element: any) { - this.registry = registry; - this.context = context; - this.lock = lock; - this.element = element; - - this.id = id; - this.registry.register(this); - - this.doesHaveChildren = this.context.dataSource.hasChildren(this.context.tree, this.element); - this.needsChildrenRefresh = true; - - this.parent = null; - this.previous = null; - this.next = null; - this.firstChild = null; - this.lastChild = null; - - this.traits = {}; - this.depth = 0; - this.expanded = !!(this.context.dataSource.shouldAutoexpand && this.context.dataSource.shouldAutoexpand(this.context.tree, element)); - - this._onDidCreate.fire(this); - - this.visible = this._isVisible(); - this.height = this._getHeight(); - - this._isDisposed = false; - } - - public getElement(): any { - return this.element; - } - - public hasChildren(): boolean { - return this.doesHaveChildren; - } - - public getDepth(): number { - return this.depth; - } - - public isVisible(): boolean { - return this.visible; - } - - public setVisible(value: boolean): void { - this.visible = value; - } - - public isExpanded(): boolean { - return this.expanded; - } - - /* protected */ public _setExpanded(value: boolean): void { - this.expanded = value; - } - - public reveal(relativeTop: number | null = null): void { - let eventData: IItemRevealEvent = { item: this, relativeTop: relativeTop }; - this._onDidReveal.fire(eventData); - } - - public expand(): Promise { - if (this.isExpanded() || !this.doesHaveChildren || this.lock.isLocked(this)) { - return Promise.resolve(false); - } - - let result = this.lock.run(this, () => { - if (this.isExpanded() || !this.doesHaveChildren) { - return Promise.resolve(false); - } - - let eventData: IItemExpandEvent = { item: this }; - let result: Promise; - this._onExpand.fire(eventData); - - if (this.needsChildrenRefresh) { - result = this.refreshChildren(false, true, true); - } else { - result = Promise.resolve(null); - } - - return result.then(() => { - this._setExpanded(true); - this._onDidExpand.fire(eventData); - return true; - }); - }); - - return result.then((r) => { - if (this.isDisposed()) { - return false; - } - - // Auto expand single child folders - if (this.context.options.autoExpandSingleChildren && r && this.firstChild !== null && this.firstChild === this.lastChild && this.firstChild.isVisible()) { - return this.firstChild.expand().then(() => { return true; }); - } - - return r; - }); - } - - public collapse(recursive: boolean = false): Promise { - if (recursive) { - let collapseChildrenPromise = Promise.resolve(null); - this.forEachChild((child) => { - collapseChildrenPromise = collapseChildrenPromise.then(() => child.collapse(true)); - }); - return collapseChildrenPromise.then(() => { - return this.collapse(false); - }); - } else { - if (!this.isExpanded() || this.lock.isLocked(this)) { - return Promise.resolve(false); - } - - return this.lock.run(this, () => { - let eventData: IItemCollapseEvent = { item: this }; - this._onCollapse.fire(eventData); - this._setExpanded(false); - this._onDidCollapse.fire(eventData); - - return Promise.resolve(true); - }); - } - } - - public addTrait(trait: string): void { - let eventData: IItemTraitEvent = { item: this, trait: trait }; - this.traits[trait] = true; - this._onDidAddTrait.fire(eventData); - } - - public removeTrait(trait: string): void { - let eventData: IItemTraitEvent = { item: this, trait: trait }; - delete this.traits[trait]; - this._onDidRemoveTrait.fire(eventData); - } - - public hasTrait(trait: string): boolean { - return this.traits[trait] || false; - } - - public getAllTraits(): string[] { - let result: string[] = []; - let trait: string; - for (trait in this.traits) { - if (this.traits.hasOwnProperty(trait) && this.traits[trait]) { - result.push(trait); - } - } - return result; - } - - public getHeight(): number { - return this.height; - } - - private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): Promise { - if (!force && !this.isExpanded()) { - const setNeedsChildrenRefresh = (item: Item) => { - item.needsChildrenRefresh = true; - item.forEachChild(setNeedsChildrenRefresh); - }; - - setNeedsChildrenRefresh(this); - - return Promise.resolve(this); - } - - this.needsChildrenRefresh = false; - - let doRefresh = () => { - let eventData: IItemChildrenRefreshEvent = { item: this, isNested: safe }; - this._onRefreshChildren.fire(eventData); - - let childrenPromise: Promise; - if (this.doesHaveChildren) { - childrenPromise = this.context.dataSource.getChildren(this.context.tree, this.element); - } else { - childrenPromise = Promise.resolve([]); - } - - const result = childrenPromise.then((elements: any[]) => { - if (this.isDisposed() || this.registry.isDisposed()) { - return Promise.resolve(null); - } - - if (!Array.isArray(elements)) { - return Promise.reject(new Error('Please return an array of children.')); - } - - elements = !elements ? [] : elements.slice(0); - elements = this.sort(elements); - - let staleItems: IItemMap = {}; - while (this.firstChild !== null) { - staleItems[this.firstChild.id] = this.firstChild; - this.removeChild(this.firstChild); - } - - for (let i = 0, len = elements.length; i < len; i++) { - let element = elements[i]; - let id = this.context.dataSource.getId(this.context.tree, element); - let item = staleItems[id] || new Item(id, this.registry, this.context, this.lock, element); - item.element = element; - if (recursive) { - item.needsChildrenRefresh = recursive; - } - delete staleItems[id]; - this.addChild(item); - } - - for (let staleItemId in staleItems) { - if (staleItems.hasOwnProperty(staleItemId)) { - staleItems[staleItemId].dispose(); - } - } - - if (recursive) { - return Promise.all(this.mapEachChild((child) => { - return child.doRefresh(recursive, true); - })); - } else { - return Promise.all(this.mapEachChild((child) => { - if (child.isExpanded() && child.needsChildrenRefresh) { - return child.doRefresh(recursive, true); - } else { - child.updateVisibility(); - return Promise.resolve(null); - } - })); - } - }); - - return result - .then(undefined, onUnexpectedError) - .then(() => this._onDidRefreshChildren.fire(eventData)); - }; - - return safe ? doRefresh() : this.lock.run(this, doRefresh); - } - - private doRefresh(recursive: boolean, safe: boolean = false): Promise { - this.doesHaveChildren = this.context.dataSource.hasChildren(this.context.tree, this.element); - this.height = this._getHeight(); - this.updateVisibility(); - - this._onDidRefresh.fire(this); - - return this.refreshChildren(recursive, safe); - } - - private updateVisibility(): void { - this.setVisible(this._isVisible()); - } - - public refresh(recursive: boolean): Promise { - return this.doRefresh(recursive); - } - - public getNavigator(): INavigator { - return new TreeNavigator(this); - } - - public intersects(other: Item): boolean { - return this.isAncestorOf(other) || other.isAncestorOf(this); - } - - private isAncestorOf(startItem: Item): boolean { - let item: Item | null = startItem; - while (item) { - if (item.id === this.id) { - return true; - } - item = item.parent; - } - return false; - } - - private addChild(item: Item, afterItem: Item | null = this.lastChild): void { - let isEmpty = this.firstChild === null; - let atHead = afterItem === null; - let atTail = afterItem === this.lastChild; - - if (isEmpty) { - this.firstChild = this.lastChild = item; - item.next = item.previous = null; - } else if (atHead) { - if (!this.firstChild) { - throw new Error('Invalid tree state'); - } - this.firstChild.previous = item; - item.next = this.firstChild; - item.previous = null; - this.firstChild = item; - } else if (atTail) { - if (!this.lastChild) { - throw new Error('Invalid tree state'); - } - this.lastChild.next = item; - item.next = null; - item.previous = this.lastChild; - this.lastChild = item; - } else { - item.previous = afterItem; - if (!afterItem) { - throw new Error('Invalid tree state'); - } - item.next = afterItem.next; - if (!afterItem.next) { - throw new Error('Invalid tree state'); - } - afterItem.next.previous = item; - afterItem.next = item; - } - - item.parent = this; - item.depth = this.depth + 1; - } - - private removeChild(item: Item): void { - let isFirstChild = this.firstChild === item; - let isLastChild = this.lastChild === item; - - if (isFirstChild && isLastChild) { - this.firstChild = this.lastChild = null; - } else if (isFirstChild) { - if (!item.next) { - throw new Error('Invalid tree state'); - } - item.next.previous = null; - this.firstChild = item.next; - } else if (isLastChild) { - if (!item.previous) { - throw new Error('Invalid tree state'); - } - item.previous.next = null; - this.lastChild = item.previous; - } else { - if (!item.next) { - throw new Error('Invalid tree state'); - } - item.next.previous = item.previous; - if (!item.previous) { - throw new Error('Invalid tree state'); - } - item.previous.next = item.next; - } - - item.parent = null; - item.depth = NaN; - } - - private forEachChild(fn: (child: Item) => void): void { - let child = this.firstChild; - let next: Item | null; - while (child) { - next = child.next; - fn(child); - child = next; - } - } - - private mapEachChild(fn: (child: Item) => T): T[] { - let result: T[] = []; - this.forEachChild((child) => { - result.push(fn(child)); - }); - return result; - } - - private sort(elements: any[]): any[] { - const sorter = this.context.sorter; - if (sorter) { - return elements.sort((element, otherElement) => { - return sorter.compare(this.context.tree, element, otherElement); - }); - } - - return elements; - } - - /* protected */ public _getHeight(): number { - if (!this.context.renderer) { - return 0; - } - return this.context.renderer.getHeight(this.context.tree, this.element); - } - - /* protected */ public _isVisible(): boolean { - if (!this.context.filter) { - return false; - } - return this.context.filter.isVisible(this.context.tree, this.element); - } - - public isDisposed(): boolean { - return this._isDisposed; - } - - public dispose(): void { - this.forEachChild((child) => child.dispose()); - - this.parent = null; - this.previous = null; - this.next = null; - this.firstChild = null; - this.lastChild = null; - - this._onDidDispose.fire(this); - - this.registry.deregister(this); - - this._onDidCreate.dispose(); - this._onDidReveal.dispose(); - this._onExpand.dispose(); - this._onDidExpand.dispose(); - this._onCollapse.dispose(); - this._onDidCollapse.dispose(); - this._onDidAddTrait.dispose(); - this._onDidRemoveTrait.dispose(); - this._onDidRefresh.dispose(); - this._onRefreshChildren.dispose(); - this._onDidRefreshChildren.dispose(); - this._onDidDispose.dispose(); - - this._isDisposed = true; - } -} - -class RootItem extends Item { - - constructor(id: string, registry: ItemRegistry, context: _.ITreeContext, lock: Lock, element: any) { - super(id, registry, context, lock, element); - } - - public isVisible(): boolean { - return false; - } - - public setVisible(value: boolean): void { - // no-op - } - - public isExpanded(): boolean { - return true; - } - - /* protected */ public _setExpanded(value: boolean): void { - // no-op - } - - public render(): void { - // no-op - } - - /* protected */ public _getHeight(): number { - return 0; - } - - /* protected */ public _isVisible(): boolean { - return false; - } -} - -export class TreeNavigator implements INavigator { - - private start: Item | null; - private item: Item | null; - - static lastDescendantOf(item: Item | null): Item | null { - if (!item) { - return null; - } - - if (item instanceof RootItem) { - return TreeNavigator.lastDescendantOf(item.lastChild); - } - - if (!item.isVisible()) { - return TreeNavigator.lastDescendantOf(item.previous); - } - - if (!item.isExpanded() || item.lastChild === null) { - return item; - } - - return TreeNavigator.lastDescendantOf(item.lastChild); - } - - constructor(item: Item | null, subTreeOnly: boolean = true) { - this.item = item; - this.start = subTreeOnly ? item : null; - } - - public current(): Item | null { - return this.item || null; - } - - public next(): Item | null { - if (this.item) { - do { - if ((this.item instanceof RootItem || (this.item.isVisible() && this.item.isExpanded())) && this.item.firstChild) { - this.item = this.item.firstChild; - } else if (this.item === this.start) { - this.item = null; - } else { - // select next brother, next uncle, next great-uncle, etc... - while (this.item && this.item !== this.start && !this.item.next) { - this.item = this.item.parent; - } - if (this.item === this.start) { - this.item = null; - } - this.item = !this.item ? null : this.item.next; - } - } while (this.item && !this.item.isVisible()); - } - return this.item || null; - } - - public previous(): Item | null { - if (this.item) { - do { - let previous = TreeNavigator.lastDescendantOf(this.item.previous); - if (previous) { - this.item = previous; - } else if (this.item.parent && this.item.parent !== this.start && this.item.parent.isVisible()) { - this.item = this.item.parent; - } else { - this.item = null; - } - } while (this.item && !this.item.isVisible()); - } - return this.item || null; - } - - public parent(): Item | null { - if (this.item) { - let parent = this.item.parent; - if (parent && parent !== this.start && parent.isVisible()) { - this.item = parent; - } else { - this.item = null; - } - } - return this.item || null; - } - - public first(): Item | null { - this.item = this.start; - this.next(); - return this.item || null; - } - - public last(): Item | null { - return TreeNavigator.lastDescendantOf(this.start); - } -} - -export interface IBaseEvent { - item: Item | null; -} - -export interface IInputEvent extends IBaseEvent { } - -export interface IRefreshEvent extends IBaseEvent { - recursive: boolean; -} - -export class TreeModel { - - private context: _.ITreeContext; - private lock!: Lock; - private input: Item | null; - private registry: ItemRegistry = new ItemRegistry(); - private registryDisposable: IDisposable = Disposable.None; - private traitsToItems: ITraitMap; - - private readonly _onSetInput = new Emitter(); - readonly onSetInput: Event = this._onSetInput.event; - private readonly _onDidSetInput = new Emitter(); - readonly onDidSetInput: Event = this._onDidSetInput.event; - private readonly _onRefresh = new Emitter(); - readonly onRefresh: Event = this._onRefresh.event; - private readonly _onDidRefresh = new Emitter(); - readonly onDidRefresh: Event = this._onDidRefresh.event; - private readonly _onDidHighlight = new Emitter<_.IHighlightEvent>(); - readonly onDidHighlight: Event<_.IHighlightEvent> = this._onDidHighlight.event; - private readonly _onDidSelect = new Emitter<_.ISelectionEvent>(); - readonly onDidSelect: Event<_.ISelectionEvent> = this._onDidSelect.event; - private readonly _onDidFocus = new Emitter<_.IFocusEvent>(); - readonly onDidFocus: Event<_.IFocusEvent> = this._onDidFocus.event; - - private _onDidRevealItem = new Relay(); - readonly onDidRevealItem: Event = this._onDidRevealItem.event; - private _onExpandItem = new Relay(); - readonly onExpandItem: Event = this._onExpandItem.event; - private _onDidExpandItem = new Relay(); - readonly onDidExpandItem: Event = this._onDidExpandItem.event; - private _onCollapseItem = new Relay(); - readonly onCollapseItem: Event = this._onCollapseItem.event; - private _onDidCollapseItem = new Relay(); - readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; - private _onDidAddTraitItem = new Relay(); - readonly onDidAddTraitItem: Event = this._onDidAddTraitItem.event; - private _onDidRemoveTraitItem = new Relay(); - readonly onDidRemoveTraitItem: Event = this._onDidRemoveTraitItem.event; - private _onDidRefreshItem = new Relay(); - readonly onDidRefreshItem: Event = this._onDidRefreshItem.event; - private _onRefreshItemChildren = new Relay(); - readonly onRefreshItemChildren: Event = this._onRefreshItemChildren.event; - private _onDidRefreshItemChildren = new Relay(); - readonly onDidRefreshItemChildren: Event = this._onDidRefreshItemChildren.event; - private _onDidDisposeItem = new Relay(); - readonly onDidDisposeItem: Event = this._onDidDisposeItem.event; - - constructor(context: _.ITreeContext) { - this.context = context; - this.input = null; - this.traitsToItems = {}; - } - - public setInput(element: any): Promise { - let eventData: IInputEvent = { item: this.input }; - this._onSetInput.fire(eventData); - - this.setSelection([]); - this.setFocus(); - this.setHighlight(); - - this.lock = new Lock(); - - if (this.input) { - this.input.dispose(); - } - - if (this.registry) { - this.registry.dispose(); - this.registryDisposable.dispose(); - } - - this.registry = new ItemRegistry(); - - this._onDidRevealItem.input = this.registry.onDidRevealItem; - this._onExpandItem.input = this.registry.onExpandItem; - this._onDidExpandItem.input = this.registry.onDidExpandItem; - this._onCollapseItem.input = this.registry.onCollapseItem; - this._onDidCollapseItem.input = this.registry.onDidCollapseItem; - this._onDidAddTraitItem.input = this.registry.onDidAddTraitItem; - this._onDidRemoveTraitItem.input = this.registry.onDidRemoveTraitItem; - this._onDidRefreshItem.input = this.registry.onDidRefreshItem; - this._onRefreshItemChildren.input = this.registry.onRefreshItemChildren; - this._onDidRefreshItemChildren.input = this.registry.onDidRefreshItemChildren; - this._onDidDisposeItem.input = this.registry.onDidDisposeItem; - - this.registryDisposable = this.registry - .onDidDisposeItem(item => item.getAllTraits().forEach(trait => delete this.traitsToItems[trait][item.id])); - - let id = this.context.dataSource.getId(this.context.tree, element); - this.input = new RootItem(id, this.registry, this.context, this.lock, element); - eventData = { item: this.input }; - this._onDidSetInput.fire(eventData); - return this.refresh(this.input); - } - - public getInput(): any { - return this.input ? this.input.getElement() : null; - } - - public refresh(element: any = null, recursive: boolean = true): Promise { - let item = this.getItem(element); - - if (!item) { - return Promise.resolve(null); - } - - let eventData: IRefreshEvent = { item: item, recursive: recursive }; - this._onRefresh.fire(eventData); - return item.refresh(recursive).then(() => { - this._onDidRefresh.fire(eventData); - }); - } - - public expand(element: any): Promise { - let item = this.getItem(element); - - if (!item) { - return Promise.resolve(false); - } - - return item.expand(); - } - - public expandAll(elements?: any[]): Promise { - if (!elements) { - elements = []; - - let item: Item | null; - let nav = this.getNavigator(); - - while (item = nav.next()) { - elements.push(item); - } - } - - return this._expandAll(elements); - } - - private _expandAll(elements: any[]): Promise { - if (elements.length === 0) { - return Promise.resolve(null); - } - - const elementsToExpand: any[] = []; - const elementsToDelay: any[] = []; - - for (const element of elements) { - let item = this.getItem(element); - - if (item) { - elementsToExpand.push(element); - } else { - elementsToDelay.push(element); - } - } - - if (elementsToExpand.length === 0) { - return Promise.resolve(null); - } - - return this.__expandAll(elementsToExpand) - .then(() => this._expandAll(elementsToDelay)); - } - - private __expandAll(elements: any[]): Promise { - const promises: Array> = []; - for (let i = 0, len = elements.length; i < len; i++) { - promises.push(this.expand(elements[i])); - } - return Promise.all(promises); - } - - public collapse(element: any, recursive: boolean = false): Promise { - const item = this.getItem(element); - - if (!item) { - return Promise.resolve(false); - } - - return item.collapse(recursive); - } - - public collapseAll(elements: any[] | null = null, recursive: boolean = false): Promise { - if (!elements) { - elements = [this.input]; - recursive = true; - } - let promises: Array> = []; - for (let i = 0, len = elements.length; i < len; i++) { - promises.push(this.collapse(elements[i], recursive)); - } - return Promise.all(promises); - } - - public toggleExpansion(element: any, recursive: boolean = false): Promise { - return this.isExpanded(element) ? this.collapse(element, recursive) : this.expand(element); - } - - public toggleExpansionAll(elements: any[]): Promise { - let promises: Array> = []; - for (let i = 0, len = elements.length; i < len; i++) { - promises.push(this.toggleExpansion(elements[i])); - } - return Promise.all(promises); - } - - public isExpanded(element: any): boolean { - let item = this.getItem(element); - - if (!item) { - return false; - } - - return item.isExpanded(); - } - - public getExpandedElements(): any[] { - let result: any[] = []; - let item: Item | null; - let nav = this.getNavigator(); - - while (item = nav.next()) { - if (item.isExpanded()) { - result.push(item.getElement()); - } - } - - return result; - } - - public reveal(element: any, relativeTop: number | null = null): Promise { - return this.resolveUnknownParentChain(element).then((chain: any[]) => { - let result = Promise.resolve(null); - - chain.forEach((e) => { - result = result.then(() => this.expand(e)); - }); - - return result; - }).then(() => { - let item = this.getItem(element); - - if (item) { - return item.reveal(relativeTop); - } - }); - } - - private resolveUnknownParentChain(element: any): Promise { - return this.context.dataSource.getParent(this.context.tree, element).then((parent) => { - if (!parent) { - return Promise.resolve([]); - } - - return this.resolveUnknownParentChain(parent).then((result) => { - result.push(parent); - return result; - }); - }); - } - - public setHighlight(element?: any, eventPayload?: any): void { - this.setTraits('highlighted', element ? [element] : []); - let eventData: _.IHighlightEvent = { highlight: this.getHighlight(), payload: eventPayload }; - this._onDidHighlight.fire(eventData); - } - - public getHighlight(includeHidden: boolean = false): any { - let result = this.getElementsWithTrait('highlighted', includeHidden); - return result.length === 0 ? null : result[0]; - } - - public isHighlighted(element: any): boolean { - let item = this.getItem(element); - - if (!item) { - return false; - } - - return item.hasTrait('highlighted'); - } - - public select(element: any, eventPayload?: any): void { - this.selectAll([element], eventPayload); - } - - public selectAll(elements: any[], eventPayload?: any): void { - this.addTraits('selected', elements); - let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; - this._onDidSelect.fire(eventData); - } - - public deselect(element: any, eventPayload?: any): void { - this.deselectAll([element], eventPayload); - } - - public deselectAll(elements: any[], eventPayload?: any): void { - this.removeTraits('selected', elements); - let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; - this._onDidSelect.fire(eventData); - } - - public setSelection(elements: any[], eventPayload?: any): void { - this.setTraits('selected', elements); - let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; - this._onDidSelect.fire(eventData); - } - - public isSelected(element: any): boolean { - let item = this.getItem(element); - - if (!item) { - return false; - } - - return item.hasTrait('selected'); - } - - public getSelection(includeHidden: boolean = false): any[] { - return this.getElementsWithTrait('selected', includeHidden); - } - - public selectNext(count: number = 1, clearSelection: boolean = true, eventPayload?: any): void { - let selection = this.getSelection(); - let item: Item = selection.length > 0 ? selection[0] : this.input; - let nextItem: Item | null; - let nav = this.getNavigator(item, false); - - for (let i = 0; i < count; i++) { - nextItem = nav.next(); - if (!nextItem) { - break; - } - item = nextItem; - } - - if (clearSelection) { - this.setSelection([item], eventPayload); - } else { - this.select(item, eventPayload); - } - } - - public selectPrevious(count: number = 1, clearSelection: boolean = true, eventPayload?: any): void { - let selection = this.getSelection(), - item: Item | null = null, - previousItem: Item | null = null; - - if (selection.length === 0) { - let nav = this.getNavigator(this.input); - - while (item = nav.next()) { - previousItem = item; - } - - item = previousItem; - - } else { - item = selection[0]; - let nav = this.getNavigator(item, false); - - for (let i = 0; i < count; i++) { - previousItem = nav.previous(); - if (!previousItem) { - break; - } - item = previousItem; - } - } - - if (clearSelection) { - this.setSelection([item], eventPayload); - } else { - this.select(item, eventPayload); - } - } - - public setFocus(element?: any, eventPayload?: any): void { - this.setTraits('focused', element ? [element] : []); - let eventData: _.IFocusEvent = { focus: this.getFocus(), payload: eventPayload }; - this._onDidFocus.fire(eventData); - } - - public isFocused(element: any): boolean { - let item = this.getItem(element); - - if (!item) { - return false; - } - - return item.hasTrait('focused'); - } - - public getFocus(includeHidden: boolean = false): any { - let result = this.getElementsWithTrait('focused', includeHidden); - return result.length === 0 ? null : result[0]; - } - - public focusNext(count: number = 1, eventPayload?: any): void { - let item: Item = this.getFocus() || this.input; - let nextItem: Item | null; - let nav = this.getNavigator(item, false); - - for (let i = 0; i < count; i++) { - nextItem = nav.next(); - if (!nextItem) { - break; - } - item = nextItem; - } - - this.setFocus(item, eventPayload); - } - - public focusPrevious(count: number = 1, eventPayload?: any): void { - let item: Item = this.getFocus() || this.input; - let previousItem: Item | null; - let nav = this.getNavigator(item, false); - - for (let i = 0; i < count; i++) { - previousItem = nav.previous(); - if (!previousItem) { - break; - } - item = previousItem; - } - - this.setFocus(item, eventPayload); - } - - public focusParent(eventPayload?: any): void { - let item: Item = this.getFocus() || this.input; - let nav = this.getNavigator(item, false); - let parent = nav.parent(); - - if (parent) { - this.setFocus(parent, eventPayload); - } - } - - public focusFirstChild(eventPayload?: any): void { - const item = this.getItem(this.getFocus() || this.input); - const nav = this.getNavigator(item, false); - const next = nav.next(); - const parent = nav.parent(); - - if (parent === item) { - this.setFocus(next, eventPayload); - } - } - - public focusFirst(eventPayload?: any, from?: any): void { - this.focusNth(0, eventPayload, from); - } - - public focusNth(index: number, eventPayload?: any, from?: any): void { - let navItem = this.getParent(from); - let nav = this.getNavigator(navItem); - let item = nav.first(); - for (let i = 0; i < index; i++) { - item = nav.next(); - } - - if (item) { - this.setFocus(item, eventPayload); - } - } - - public focusLast(eventPayload?: any, from?: any): void { - const navItem = this.getParent(from); - let item: Item | null; - if (from && navItem) { - item = navItem.lastChild; - } else { - const nav = this.getNavigator(navItem); - item = nav.last(); - } - - if (item) { - this.setFocus(item, eventPayload); - } - } - - private getParent(from?: any): Item | null { - if (from) { - const fromItem = this.getItem(from); - if (fromItem && fromItem.parent) { - return fromItem.parent; - } - } - - return this.getItem(this.input); - } - - public getNavigator(element: any = null, subTreeOnly: boolean = true): INavigator { - return new TreeNavigator(this.getItem(element), subTreeOnly); - } - - public getItem(element: any = null): Item | null { - if (element === null) { - return this.input; - } else if (element instanceof Item) { - return element; - } else if (typeof element === 'string') { - return this.registry.getItem(element); - } else { - return this.registry.getItem(this.context.dataSource.getId(this.context.tree, element)); - } - } - - public addTraits(trait: string, elements: any[]): void { - let items: IItemMap = this.traitsToItems[trait] || {}; - let item: Item | null; - for (let i = 0, len = elements.length; i < len; i++) { - item = this.getItem(elements[i]); - - if (item) { - item.addTrait(trait); - items[item.id] = item; - } - } - this.traitsToItems[trait] = items; - } - - public removeTraits(trait: string, elements: any[]): void { - let items: IItemMap = this.traitsToItems[trait] || {}; - let item: Item | null; - let id: string; - - if (elements.length === 0) { - for (id in items) { - if (items.hasOwnProperty(id)) { - item = items[id]; - item.removeTrait(trait); - } - } - - delete this.traitsToItems[trait]; - - } else { - for (let i = 0, len = elements.length; i < len; i++) { - item = this.getItem(elements[i]); - - if (item) { - item.removeTrait(trait); - delete items[item.id]; - } - } - } - } - - private setTraits(trait: string, elements: any[]): void { - if (elements.length === 0) { - this.removeTraits(trait, elements); - } else { - let items: { [id: string]: Item; } = {}; - let item: Item | null; - - for (let i = 0, len = elements.length; i < len; i++) { - item = this.getItem(elements[i]); - - if (item) { - items[item.id] = item; - } - } - - let traitItems: IItemMap = this.traitsToItems[trait] || {}; - let itemsToRemoveTrait: Item[] = []; - let id: string; - - for (id in traitItems) { - if (traitItems.hasOwnProperty(id)) { - if (items.hasOwnProperty(id)) { - delete items[id]; - } else { - itemsToRemoveTrait.push(traitItems[id]); - } - } - } - - for (let i = 0, len = itemsToRemoveTrait.length; i < len; i++) { - item = itemsToRemoveTrait[i]; - item.removeTrait(trait); - delete traitItems[item.id]; - } - - for (id in items) { - if (items.hasOwnProperty(id)) { - item = items[id]; - item.addTrait(trait); - traitItems[id] = item; - } - } - - this.traitsToItems[trait] = traitItems; - } - } - - private getElementsWithTrait(trait: string, includeHidden: boolean): any[] { - let elements: any[] = []; - let items = this.traitsToItems[trait] || {}; - let id: string; - for (id in items) { - if (items.hasOwnProperty(id) && (items[id].isVisible() || includeHidden)) { - elements.push(items[id].getElement()); - } - } - return elements; - } - - public dispose(): void { - this.registry.dispose(); - this._onSetInput.dispose(); - this._onDidSetInput.dispose(); - this._onRefresh.dispose(); - this._onDidRefresh.dispose(); - this._onDidHighlight.dispose(); - this._onDidSelect.dispose(); - this._onDidFocus.dispose(); - this._onDidRevealItem.dispose(); - this._onExpandItem.dispose(); - this._onDidExpandItem.dispose(); - this._onCollapseItem.dispose(); - this._onDidCollapseItem.dispose(); - this._onDidAddTraitItem.dispose(); - this._onDidRemoveTraitItem.dispose(); - this._onDidRefreshItem.dispose(); - this._onRefreshItemChildren.dispose(); - this._onDidRefreshItemChildren.dispose(); - this._onDidDisposeItem.dispose(); - } -} diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts deleted file mode 100644 index 64e5cfdb245..00000000000 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ /dev/null @@ -1,1682 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as Platform from 'vs/base/common/platform'; -import * as Browser from 'vs/base/browser/browser'; -import * as Lifecycle from 'vs/base/common/lifecycle'; -import * as DOM from 'vs/base/browser/dom'; -import * as Diff from 'vs/base/common/diff/diff'; -import * as Touch from 'vs/base/browser/touch'; -import * as strings from 'vs/base/common/strings'; -import * as Mouse from 'vs/base/browser/mouseEvent'; -import * as Keyboard from 'vs/base/browser/keyboardEvent'; -import * as Model from 'vs/base/parts/tree/browser/treeModel'; -import * as dnd from './treeDnd'; -import { ArrayIterator, MappedIterator } from 'vs/base/common/iterator'; -import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; -import * as _ from 'vs/base/parts/tree/browser/tree'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { Event, Emitter } from 'vs/base/common/event'; -import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; -import { DefaultTreestyler } from './treeDefaults'; -import { Delayer, timeout } from 'vs/base/common/async'; - -export interface IRow { - element: HTMLElement | null; - templateId: string; - templateData: any; -} - -function removeFromParent(element: HTMLElement): void { - try { - element.parentElement!.removeChild(element); - } catch (e) { - // this will throw if this happens due to a blur event, nasty business - } -} - -export class RowCache implements Lifecycle.IDisposable { - - private _cache: { [templateId: string]: IRow[]; } | null; - - constructor(private context: _.ITreeContext) { - this._cache = { '': [] }; - } - - public alloc(templateId: string): IRow { - let result = this.cache(templateId).pop(); - - if (!result) { - let content = document.createElement('div'); - content.className = 'content'; - - let row = document.createElement('div'); - row.appendChild(content); - - let templateData: any = null; - - try { - templateData = this.context.renderer!.renderTemplate(this.context.tree, templateId, content); - } catch (err) { - console.error('Tree usage error: exception while rendering template'); - console.error(err); - } - - result = { - element: row, - templateId: templateId, - templateData - }; - } - - return result; - } - - public release(templateId: string, row: IRow): void { - removeFromParent(row.element!); - this.cache(templateId).push(row); - } - - private cache(templateId: string): IRow[] { - return this._cache![templateId] || (this._cache![templateId] = []); - } - - public garbageCollect(): void { - if (this._cache) { - Object.keys(this._cache).forEach(templateId => { - this._cache![templateId].forEach(cachedRow => { - this.context.renderer!.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); - cachedRow.element = null; - cachedRow.templateData = null; - }); - - delete this._cache![templateId]; - }); - } - } - - public dispose(): void { - this.garbageCollect(); - this._cache = null; - } -} - -export interface IViewContext extends _.ITreeContext { - cache: RowCache; - horizontalScrolling: boolean; -} - -export class ViewItem implements IViewItem { - - private context: IViewContext; - - public model: Model.Item; - public id: string; - protected row: IRow | null; - - public top: number; - public height: number; - public width: number = 0; - public onDragStart!: (e: DragEvent) => void; - - public needsRender: boolean = false; - public uri: string | null = null; - public unbindDragStart: Lifecycle.IDisposable = Lifecycle.Disposable.None; - public loadingTimer: any; - - public _styles: any; - private _draggable: boolean = false; - - constructor(context: IViewContext, model: Model.Item) { - this.context = context; - this.model = model; - - this.id = this.model.id; - this.row = null; - - this.top = 0; - this.height = model.getHeight(); - - this._styles = {}; - model.getAllTraits().forEach(t => this._styles[t] = true); - - if (model.isExpanded()) { - this.addClass('expanded'); - } - } - - set expanded(value: boolean) { - value ? this.addClass('expanded') : this.removeClass('expanded'); - } - - set loading(value: boolean) { - value ? this.addClass('codicon-loading') : this.removeClass('codicon-loading'); - } - - set draggable(value: boolean) { - this._draggable = value; - this.render(true); - } - - get draggable() { - return this._draggable; - } - - set dropTarget(value: boolean) { - value ? this.addClass('drop-target') : this.removeClass('drop-target'); - } - - public get element(): HTMLElement { - return (this.row && this.row.element)!; - } - - private _templateId: string | undefined; - private get templateId(): string { - return this._templateId || (this._templateId = (this.context.renderer!.getTemplateId && this.context.renderer!.getTemplateId(this.context.tree, this.model.getElement()))); - } - - public addClass(name: string): void { - this._styles[name] = true; - this.render(true); - } - - public removeClass(name: string): void { - delete this._styles[name]; // is this slow? - this.render(true); - } - - public render(skipUserRender = false): void { - if (!this.model || !this.element) { - return; - } - - let classes = ['monaco-tree-row']; - classes.push.apply(classes, Object.keys(this._styles)); - - if (this.model.hasChildren()) { - classes.push('has-children'); - } - - this.element.className = classes.join(' '); - this.element.draggable = this.draggable; - this.element.style.height = this.height + 'px'; - - // ARIA - this.element.setAttribute('role', 'treeitem'); - const accessibility = this.context.accessibilityProvider!; - const ariaLabel = accessibility.getAriaLabel(this.context.tree, this.model.getElement()); - if (ariaLabel) { - this.element.setAttribute('aria-label', ariaLabel); - } - if (accessibility.getPosInSet && accessibility.getSetSize) { - this.element.setAttribute('aria-setsize', accessibility.getSetSize()); - this.element.setAttribute('aria-posinset', accessibility.getPosInSet(this.context.tree, this.model.getElement())); - } - if (this.model.hasTrait('focused')) { - const base64Id = strings.safeBtoa(this.model.id); - - this.element.setAttribute('aria-selected', 'true'); - this.element.setAttribute('id', base64Id); - } else { - this.element.setAttribute('aria-selected', 'false'); - this.element.removeAttribute('id'); - } - if (this.model.hasChildren()) { - this.element.setAttribute('aria-expanded', String(!!this._styles['expanded'])); - } else { - this.element.removeAttribute('aria-expanded'); - } - this.element.setAttribute('aria-level', String(this.model.getDepth())); - - if (this.context.options.paddingOnRow) { - this.element.style.paddingLeft = this.context.options.twistiePixels! + ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; - } else { - this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; - (this.row!.element!.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; - } - - let uri = this.context.dnd!.getDragURI(this.context.tree, this.model.getElement()); - - if (uri !== this.uri) { - if (this.unbindDragStart) { - this.unbindDragStart.dispose(); - } - - if (uri) { - this.uri = uri; - this.draggable = true; - this.unbindDragStart = DOM.addDisposableListener(this.element, 'dragstart', (e) => { - this.onDragStart(e); - }); - } else { - this.uri = null; - } - } - - if (!skipUserRender && this.element) { - let paddingLeft: number = 0; - if (this.context.horizontalScrolling) { - const style = window.getComputedStyle(this.element); - paddingLeft = parseFloat(style.paddingLeft!); - } - - if (this.context.horizontalScrolling) { - this.element.style.width = Browser.isFirefox ? '-moz-fit-content' : 'fit-content'; - } - - try { - this.context.renderer!.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row!.templateData); - } catch (err) { - console.error('Tree usage error: exception while rendering element'); - console.error(err); - } - - if (this.context.horizontalScrolling) { - this.width = DOM.getContentWidth(this.element) + paddingLeft; - this.element.style.width = ''; - } - } - } - - updateWidth(): any { - if (!this.context.horizontalScrolling || !this.element) { - return; - } - - const style = window.getComputedStyle(this.element); - const paddingLeft = parseFloat(style.paddingLeft!); - this.element.style.width = Browser.isFirefox ? '-moz-fit-content' : 'fit-content'; - this.width = DOM.getContentWidth(this.element) + paddingLeft; - this.element.style.width = ''; - } - - public insertInDOM(container: HTMLElement, afterElement: HTMLElement | null): void { - if (!this.row) { - this.row = this.context.cache.alloc(this.templateId); - - // used in reverse lookup from HTMLElement to Item - (this.element)[TreeView.BINDING] = this; - } - - if (this.element.parentElement) { - return; - } - - if (afterElement === null) { - container.appendChild(this.element); - } else { - try { - container.insertBefore(this.element, afterElement); - } catch (e) { - console.warn('Failed to locate previous tree element'); - container.appendChild(this.element); - } - } - - this.render(); - } - - public removeFromDOM(): void { - if (!this.row) { - return; - } - - this.unbindDragStart.dispose(); - - this.uri = null; - - (this.element)[TreeView.BINDING] = null; - this.context.cache.release(this.templateId, this.row); - this.row = null; - } - - public dispose(): void { - this.row = null; - } -} - -class RootViewItem extends ViewItem { - - constructor(context: IViewContext, model: Model.Item, wrapper: HTMLElement) { - super(context, model); - - this.row = { - element: wrapper, - templateData: null, - templateId: null! - }; - } - - public render(): void { - if (!this.model || !this.element) { - return; - } - - let classes = ['monaco-tree-wrapper']; - classes.push.apply(classes, Object.keys(this._styles)); - - if (this.model.hasChildren()) { - classes.push('has-children'); - } - - this.element.className = classes.join(' '); - } - - public insertInDOM(container: HTMLElement, afterElement: HTMLElement): void { - // noop - } - - public removeFromDOM(): void { - // noop - } -} - -interface IThrottledGestureEvent { - translationX: number; - translationY: number; -} - -function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction | null): boolean { - if (!one && !other) { - return true; - } else if (!one || !other) { - return false; - } else if (one.accept !== other.accept) { - return false; - } else if (one.bubble !== other.bubble) { - return false; - } else if (one.effect !== other.effect) { - return false; - } else { - return true; - } -} - -export class TreeView extends HeightMap { - - static readonly BINDING = 'monaco-tree-row'; - static readonly LOADING_DECORATION_DELAY = 800; - - private static counter: number = 0; - private instance: number; - - private context: IViewContext; - private modelListeners: Lifecycle.IDisposable[]; - private model: Model.TreeModel | null = null; - - private viewListeners: Lifecycle.IDisposable[]; - private domNode: HTMLElement; - private wrapper: HTMLElement; - private styleElement: HTMLStyleElement; - private treeStyler: _.ITreeStyler; - private rowsContainer: HTMLElement; - private scrollableElement: ScrollableElement; - private msGesture: MSGesture | undefined; - private lastPointerType: string = ''; - private lastClickTimeStamp: number = 0; - - private horizontalScrolling: boolean; - private contentWidthUpdateDelayer = new Delayer(50); - - private lastRenderTop: number; - private lastRenderHeight: number; - - private inputItem!: ViewItem; - private items: { [id: string]: ViewItem; }; - - private isRefreshing = false; - private refreshingPreviousChildrenIds: { [id: string]: string[] } = {}; - private currentDragAndDropData: IDragAndDropData | null = null; - private currentDropElement: any; - private currentDropElementReaction!: _.IDragOverReaction; - private currentDropTarget: ViewItem | null = null; - private shouldInvalidateDropReaction: boolean; - private currentDropTargets: ViewItem[] | null = null; - private currentDropDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None; - private gestureDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None; - private dragAndDropScrollInterval: number | null = null; - private dragAndDropScrollTimeout: number | null = null; - private dragAndDropMouseY: number | null = null; - - private didJustPressContextMenuKey: boolean; - - private highlightedItemWasDraggable: boolean = false; - private onHiddenScrollTop: number | null = null; - - private readonly _onDOMFocus = new Emitter(); - readonly onDOMFocus: Event = this._onDOMFocus.event; - - private readonly _onDOMBlur = new Emitter(); - readonly onDOMBlur: Event = this._onDOMBlur.event; - - private readonly _onDidScroll = new Emitter(); - readonly onDidScroll: Event = this._onDidScroll.event; - - constructor(context: _.ITreeContext, container: HTMLElement) { - super(); - - TreeView.counter++; - this.instance = TreeView.counter; - - const horizontalScrollMode = typeof context.options.horizontalScrollMode === 'undefined' ? ScrollbarVisibility.Hidden : context.options.horizontalScrollMode; - this.horizontalScrolling = horizontalScrollMode !== ScrollbarVisibility.Hidden; - - this.context = { - dataSource: context.dataSource, - renderer: context.renderer, - controller: context.controller, - dnd: context.dnd, - filter: context.filter, - sorter: context.sorter, - tree: context.tree, - accessibilityProvider: context.accessibilityProvider, - options: context.options, - cache: new RowCache(context), - horizontalScrolling: this.horizontalScrolling - }; - - this.modelListeners = []; - this.viewListeners = []; - - this.items = {}; - - this.domNode = document.createElement('div'); - this.domNode.className = `monaco-tree no-focused-item monaco-tree-instance-${this.instance}`; - // to allow direct tabbing into the tree instead of first focusing the tree - this.domNode.tabIndex = context.options.preventRootFocus ? -1 : 0; - - this.styleElement = DOM.createStyleSheet(this.domNode); - - this.treeStyler = context.styler || new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); - - // ARIA - this.domNode.setAttribute('role', 'tree'); - if (this.context.options.ariaLabel) { - this.domNode.setAttribute('aria-label', this.context.options.ariaLabel); - } - - if (this.context.options.alwaysFocused) { - DOM.addClass(this.domNode, 'focused'); - } - - if (!this.context.options.paddingOnRow) { - DOM.addClass(this.domNode, 'no-row-padding'); - } - - this.wrapper = document.createElement('div'); - this.wrapper.className = 'monaco-tree-wrapper'; - this.scrollableElement = new ScrollableElement(this.wrapper, { - alwaysConsumeMouseWheel: true, - horizontal: horizontalScrollMode, - vertical: (typeof context.options.verticalScrollMode !== 'undefined' ? context.options.verticalScrollMode : ScrollbarVisibility.Auto), - useShadows: context.options.useShadows - }); - this.scrollableElement.onScroll((e) => { - this.render(e.scrollTop, e.height, e.scrollLeft, e.width, e.scrollWidth); - this._onDidScroll.fire(); - }); - - if (Browser.isIE) { - this.wrapper.style.msTouchAction = 'none'; - this.wrapper.style.msContentZooming = 'none'; - } else { - this.gestureDisposable = Touch.Gesture.addTarget(this.wrapper); - } - - this.rowsContainer = document.createElement('div'); - this.rowsContainer.className = 'monaco-tree-rows'; - if (context.options.showTwistie) { - this.rowsContainer.className += ' show-twisties'; - } - - let focusTracker = DOM.trackFocus(this.domNode); - this.viewListeners.push(focusTracker.onDidFocus(() => this.onFocus())); - this.viewListeners.push(focusTracker.onDidBlur(() => this.onBlur())); - this.viewListeners.push(focusTracker); - - this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'keydown', (e) => this.onKeyDown(e))); - this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'keyup', (e) => this.onKeyUp(e))); - this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'mousedown', (e) => this.onMouseDown(e))); - this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'mouseup', (e) => this.onMouseUp(e))); - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'auxclick', (e: MouseEvent) => { - if (e && e.button === 1) { - this.onMouseMiddleClick(e); - } - })); - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'click', (e) => this.onClick(e))); - this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'contextmenu', (e) => this.onContextMenu(e))); - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, Touch.EventType.Tap, (e) => this.onTap(e))); - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, Touch.EventType.Change, (e) => this.onTouchChange(e))); - - if (Browser.isIE) { - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'MSPointerDown', (e) => this.onMsPointerDown(e))); - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'MSGestureTap', (e) => this.onMsGestureTap(e))); - - // these events come too fast, we throttle them - this.viewListeners.push(DOM.addDisposableThrottledListener(this.wrapper, 'MSGestureChange', (e) => this.onThrottledMsGestureChange(e), (lastEvent: IThrottledGestureEvent, event: MSGestureEvent): IThrottledGestureEvent => { - event.stopPropagation(); - event.preventDefault(); - - let result = { translationY: event.translationY, translationX: event.translationX }; - - if (lastEvent) { - result.translationY += lastEvent.translationY; - result.translationX += lastEvent.translationX; - } - - return result; - })); - } - - this.viewListeners.push(DOM.addDisposableListener(window, 'dragover', (e) => this.onDragOver(e))); - this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'drop', (e) => this.onDrop(e))); - this.viewListeners.push(DOM.addDisposableListener(window, 'dragend', (e) => this.onDragEnd(e))); - this.viewListeners.push(DOM.addDisposableListener(window, 'dragleave', (e) => this.onDragOver(e))); - - this.wrapper.appendChild(this.rowsContainer); - this.domNode.appendChild(this.scrollableElement.getDomNode()); - container.appendChild(this.domNode); - - this.lastRenderTop = 0; - this.lastRenderHeight = 0; - - this.didJustPressContextMenuKey = false; - - this.currentDropTarget = null; - this.currentDropTargets = []; - this.shouldInvalidateDropReaction = false; - - this.dragAndDropScrollInterval = null; - this.dragAndDropScrollTimeout = null; - - this.onRowsChanged(); - this.layout(); - - this.setupMSGesture(); - - this.applyStyles(context.options); - } - - public applyStyles(styles: _.ITreeStyles): void { - this.treeStyler.style(styles); - } - - protected createViewItem(item: Model.Item): IViewItem { - return new ViewItem(this.context, item); - } - - public getHTMLElement(): HTMLElement { - return this.domNode; - } - - public focus(): void { - this.domNode.focus(); - } - - public isFocused(): boolean { - return document.activeElement === this.domNode; - } - - public blur(): void { - this.domNode.blur(); - } - - public onVisible(): void { - this.scrollTop = this.onHiddenScrollTop!; - this.onHiddenScrollTop = null; - this.setupMSGesture(); - } - - private setupMSGesture(): void { - if ((window).MSGesture) { - this.msGesture = new MSGesture(); - setTimeout(() => this.msGesture!.target = this.wrapper, 100); // TODO@joh, TODO@IETeam - } - } - - public onHidden(): void { - this.onHiddenScrollTop = this.scrollTop; - } - - private isTreeVisible(): boolean { - return this.onHiddenScrollTop === null; - } - - public layout(height?: number, width?: number): void { - if (!this.isTreeVisible()) { - return; - } - - this.viewHeight = height || DOM.getContentHeight(this.wrapper); // render - this.scrollHeight = this.getContentHeight(); - - if (this.horizontalScrolling) { - this.viewWidth = width || DOM.getContentWidth(this.wrapper); - } - } - - private render(scrollTop: number, viewHeight: number, scrollLeft: number, viewWidth: number, scrollWidth: number): void { - let i: number; - let stop: number; - - let renderTop = scrollTop; - let renderBottom = scrollTop + viewHeight; - let thisRenderBottom = this.lastRenderTop + this.lastRenderHeight; - - // when view scrolls down, start rendering from the renderBottom - for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) { - this.insertItemInDOM(this.itemAtIndex(i)); - } - - // when view scrolls up, start rendering from either this.renderTop or renderBottom - for (i = Math.min(this.indexAt(this.lastRenderTop), this.indexAfter(renderBottom)) - 1, stop = this.indexAt(renderTop); i >= stop; i--) { - this.insertItemInDOM(this.itemAtIndex(i)); - } - - // when view scrolls down, start unrendering from renderTop - for (i = this.indexAt(this.lastRenderTop), stop = Math.min(this.indexAt(renderTop), this.indexAfter(thisRenderBottom)); i < stop; i++) { - this.removeItemFromDOM(this.itemAtIndex(i)); - } - - // when view scrolls up, start unrendering from either renderBottom this.renderTop - for (i = Math.max(this.indexAfter(renderBottom), this.indexAt(this.lastRenderTop)), stop = this.indexAfter(thisRenderBottom); i < stop; i++) { - this.removeItemFromDOM(this.itemAtIndex(i)); - } - - let topItem = this.itemAtIndex(this.indexAt(renderTop)); - - if (topItem) { - this.rowsContainer.style.top = (topItem.top - renderTop) + 'px'; - } - - if (this.horizontalScrolling) { - this.rowsContainer.style.left = -scrollLeft + 'px'; - this.rowsContainer.style.width = `${Math.max(scrollWidth, viewWidth)}px`; - } - - this.lastRenderTop = renderTop; - this.lastRenderHeight = renderBottom - renderTop; - } - - public setModel(newModel: Model.TreeModel): void { - this.releaseModel(); - this.model = newModel; - - this.model.onRefresh(this.onRefreshing, this, this.modelListeners); - this.model.onDidRefresh(this.onRefreshed, this, this.modelListeners); - this.model.onSetInput(this.onClearingInput, this, this.modelListeners); - this.model.onDidSetInput(this.onSetInput, this, this.modelListeners); - this.model.onDidFocus(this.onModelFocusChange, this, this.modelListeners); - - this.model.onRefreshItemChildren(this.onItemChildrenRefreshing, this, this.modelListeners); - this.model.onDidRefreshItemChildren(this.onItemChildrenRefreshed, this, this.modelListeners); - this.model.onDidRefreshItem(this.onItemRefresh, this, this.modelListeners); - this.model.onExpandItem(this.onItemExpanding, this, this.modelListeners); - this.model.onDidExpandItem(this.onItemExpanded, this, this.modelListeners); - this.model.onCollapseItem(this.onItemCollapsing, this, this.modelListeners); - this.model.onDidRevealItem(this.onItemReveal, this, this.modelListeners); - this.model.onDidAddTraitItem(this.onItemAddTrait, this, this.modelListeners); - this.model.onDidRemoveTraitItem(this.onItemRemoveTrait, this, this.modelListeners); - } - - private onRefreshing(): void { - this.isRefreshing = true; - } - - private onRefreshed(): void { - this.isRefreshing = false; - this.onRowsChanged(); - } - - private onRowsChanged(scrollTop: number = this.scrollTop): void { - if (this.isRefreshing) { - return; - } - - this.scrollTop = scrollTop; - this.updateScrollWidth(); - } - - private updateScrollWidth(): void { - if (!this.horizontalScrolling) { - return; - } - - this.contentWidthUpdateDelayer.trigger(() => { - const keys = Object.keys(this.items); - let scrollWidth = 0; - - for (const key of keys) { - scrollWidth = Math.max(scrollWidth, this.items[key].width); - } - - this.scrollWidth = scrollWidth + 10 /* scrollbar */; - }); - } - - public focusNextPage(eventPayload?: any): void { - let lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); - lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; - let lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement(); - let currentlyFocusedElement = this.model!.getFocus(); - - if (currentlyFocusedElement !== lastPageElement) { - this.model!.setFocus(lastPageElement, eventPayload); - } else { - let previousScrollTop = this.scrollTop; - this.scrollTop += this.viewHeight; - - if (this.scrollTop !== previousScrollTop) { - - // Let the scroll event listener run - setTimeout(() => { - this.focusNextPage(eventPayload); - }, 0); - } - } - } - - public focusPreviousPage(eventPayload?: any): void { - let firstPageIndex: number; - - if (this.scrollTop === 0) { - firstPageIndex = this.indexAt(this.scrollTop); - } else { - firstPageIndex = this.indexAfter(this.scrollTop - 1); - } - - let firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement(); - let currentlyFocusedElement = this.model!.getFocus(); - - if (currentlyFocusedElement !== firstPageElement) { - this.model!.setFocus(firstPageElement, eventPayload); - } else { - let previousScrollTop = this.scrollTop; - this.scrollTop -= this.viewHeight; - - if (this.scrollTop !== previousScrollTop) { - - // Let the scroll event listener run - setTimeout(() => { - this.focusPreviousPage(eventPayload); - }, 0); - } - } - } - - public get viewHeight() { - const scrollDimensions = this.scrollableElement.getScrollDimensions(); - return scrollDimensions.height; - } - - public set viewHeight(height: number) { - this.scrollableElement.setScrollDimensions({ height }); - } - - private set scrollHeight(scrollHeight: number) { - scrollHeight = scrollHeight + (this.horizontalScrolling ? 10 : 0); - this.scrollableElement.setScrollDimensions({ scrollHeight }); - } - - public get viewWidth(): number { - const scrollDimensions = this.scrollableElement.getScrollDimensions(); - return scrollDimensions.width; - } - - public set viewWidth(viewWidth: number) { - this.scrollableElement.setScrollDimensions({ width: viewWidth }); - } - - private set scrollWidth(scrollWidth: number) { - this.scrollableElement.setScrollDimensions({ scrollWidth }); - } - - public get scrollTop(): number { - const scrollPosition = this.scrollableElement.getScrollPosition(); - return scrollPosition.scrollTop; - } - - public set scrollTop(scrollTop: number) { - const scrollHeight = this.getContentHeight() + (this.horizontalScrolling ? 10 : 0); - this.scrollableElement.setScrollDimensions({ scrollHeight }); - this.scrollableElement.setScrollPosition({ scrollTop }); - } - - public getScrollPosition(): number { - const height = this.getContentHeight() - this.viewHeight; - return height <= 0 ? 1 : this.scrollTop / height; - } - - public setScrollPosition(pos: number): void { - const height = this.getContentHeight() - this.viewHeight; - this.scrollTop = height * pos; - } - - // Events - - private onClearingInput(e: Model.IInputEvent): void { - let item = e.item; - if (item) { - this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); - this.onRowsChanged(); - } - } - - private onSetInput(e: Model.IInputEvent): void { - this.context.cache.garbageCollect(); - this.inputItem = new RootViewItem(this.context, e.item, this.wrapper); - } - - private onItemChildrenRefreshing(e: Model.IItemChildrenRefreshEvent): void { - let item = e.item; - let viewItem = this.items[item.id]; - - if (viewItem && this.context.options.showLoading) { - viewItem.loadingTimer = setTimeout(() => { - viewItem.loadingTimer = 0; - viewItem.loading = true; - }, TreeView.LOADING_DECORATION_DELAY); - } - - if (!e.isNested) { - let childrenIds: string[] = []; - let navigator = item.getNavigator(); - let childItem: Model.Item | null; - - while (childItem = navigator.next()) { - childrenIds.push(childItem.id); - } - - this.refreshingPreviousChildrenIds[item.id] = childrenIds; - } - } - - private onItemChildrenRefreshed(e: Model.IItemChildrenRefreshEvent): void { - let item = e.item; - let viewItem = this.items[item.id]; - - if (viewItem) { - if (viewItem.loadingTimer) { - clearTimeout(viewItem.loadingTimer); - viewItem.loadingTimer = 0; - } - - viewItem.loading = false; - } - - if (!e.isNested) { - let previousChildrenIds = this.refreshingPreviousChildrenIds[item.id]; - let afterModelItems: Model.Item[] = []; - let navigator = item.getNavigator(); - let childItem: Model.Item | null; - - while (childItem = navigator.next()) { - afterModelItems.push(childItem); - } - - let skipDiff = Math.abs(previousChildrenIds.length - afterModelItems.length) > 1000; - let diff: Diff.IDiffChange[] = []; - let doToInsertItemsAlreadyExist: boolean = false; - - if (!skipDiff) { - const lcs = new Diff.LcsDiff( - { - getElements: () => previousChildrenIds - }, - { - getElements: () => afterModelItems.map(item => item.id) - }, - null - ); - - diff = lcs.ComputeDiff(false).changes; - - // this means that the result of the diff algorithm would result - // in inserting items that were already registered. this can only - // happen if the data provider returns bad ids OR if the sorting - // of the elements has changed - doToInsertItemsAlreadyExist = diff.some(d => { - if (d.modifiedLength > 0) { - for (let i = d.modifiedStart, len = d.modifiedStart + d.modifiedLength; i < len; i++) { - if (this.items.hasOwnProperty(afterModelItems[i].id)) { - return true; - } - } - } - return false; - }); - } - - // 50 is an optimization number, at some point we're better off - // just replacing everything - if (!skipDiff && !doToInsertItemsAlreadyExist && diff.length < 50) { - for (const diffChange of diff) { - - if (diffChange.originalLength > 0) { - this.onRemoveItems(new ArrayIterator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength)); - } - - if (diffChange.modifiedLength > 0) { - let beforeItem: Model.Item | null = afterModelItems[diffChange.modifiedStart - 1] || item; - beforeItem = beforeItem.getDepth() > 0 ? beforeItem : null; - - this.onInsertItems(new ArrayIterator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null); - } - } - - } else if (skipDiff || diff.length) { - this.onRemoveItems(new ArrayIterator(previousChildrenIds)); - this.onInsertItems(new ArrayIterator(afterModelItems), item.getDepth() > 0 ? item.id : null); - } - - if (skipDiff || diff.length) { - this.onRowsChanged(); - } - } - } - - private onItemRefresh(item: Model.Item): void { - this.onItemsRefresh([item]); - } - - private onItemsRefresh(items: Model.Item[]): void { - this.onRefreshItemSet(items.filter(item => this.items.hasOwnProperty(item.id))); - this.onRowsChanged(); - } - - private onItemExpanding(e: Model.IItemExpandEvent): void { - let viewItem = this.items[e.item.id]; - if (viewItem) { - viewItem.expanded = true; - } - } - - private onItemExpanded(e: Model.IItemExpandEvent): void { - let item = e.item; - let viewItem = this.items[item.id]; - if (viewItem) { - viewItem.expanded = true; - - let height = this.onInsertItems(item.getNavigator(), item.id) || 0; - let scrollTop = this.scrollTop; - - if (viewItem.top + viewItem.height <= this.scrollTop) { - scrollTop += height; - } - - this.onRowsChanged(scrollTop); - } - } - - private onItemCollapsing(e: Model.IItemCollapseEvent): void { - let item = e.item; - let viewItem = this.items[item.id]; - if (viewItem) { - viewItem.expanded = false; - this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); - this.onRowsChanged(); - } - } - - private onItemReveal(e: Model.IItemRevealEvent): void { - let item = e.item; - let relativeTop = e.relativeTop; - let viewItem = this.items[item.id]; - if (viewItem) { - if (relativeTop !== null) { - relativeTop = relativeTop < 0 ? 0 : relativeTop; - relativeTop = relativeTop > 1 ? 1 : relativeTop; - - // y = mx + b - let m = viewItem.height - this.viewHeight; - this.scrollTop = m * relativeTop + viewItem.top; - } else { - let viewItemBottom = viewItem.top + viewItem.height; - let wrapperBottom = this.scrollTop + this.viewHeight; - - if (viewItem.top < this.scrollTop) { - this.scrollTop = viewItem.top; - } else if (viewItemBottom >= wrapperBottom) { - this.scrollTop = viewItemBottom - this.viewHeight; - } - } - } - } - - private onItemAddTrait(e: Model.IItemTraitEvent): void { - let item = e.item; - let trait = e.trait; - let viewItem = this.items[item.id]; - if (viewItem) { - viewItem.addClass(trait); - } - if (trait === 'highlighted') { - DOM.addClass(this.domNode, trait); - - // Ugly Firefox fix: input fields can't be selected if parent nodes are draggable - if (viewItem) { - this.highlightedItemWasDraggable = !!viewItem.draggable; - if (viewItem.draggable) { - viewItem.draggable = false; - } - } - } - } - - private onItemRemoveTrait(e: Model.IItemTraitEvent): void { - let item = e.item; - let trait = e.trait; - let viewItem = this.items[item.id]; - if (viewItem) { - viewItem.removeClass(trait); - } - if (trait === 'highlighted') { - DOM.removeClass(this.domNode, trait); - - // Ugly Firefox fix: input fields can't be selected if parent nodes are draggable - if (this.highlightedItemWasDraggable) { - viewItem.draggable = true; - } - this.highlightedItemWasDraggable = false; - } - } - - private onModelFocusChange(): void { - const focus = this.model && this.model.getFocus(); - - DOM.toggleClass(this.domNode, 'no-focused-item', !focus); - - // ARIA - if (focus) { - this.domNode.setAttribute('aria-activedescendant', strings.safeBtoa(this.context.dataSource.getId(this.context.tree, focus))); - } else { - this.domNode.removeAttribute('aria-activedescendant'); - } - } - - // HeightMap "events" - - public onInsertItem(item: ViewItem): void { - item.onDragStart = (e) => { this.onDragStart(item, e); }; - item.needsRender = true; - this.refreshViewItem(item); - this.items[item.id] = item; - } - - public onRefreshItem(item: ViewItem, needsRender = false): void { - item.needsRender = item.needsRender || needsRender; - this.refreshViewItem(item); - } - - public onRemoveItem(item: ViewItem): void { - this.removeItemFromDOM(item); - item.dispose(); - delete this.items[item.id]; - } - - // ViewItem refresh - - private refreshViewItem(item: ViewItem): void { - item.render(); - - if (this.shouldBeRendered(item)) { - this.insertItemInDOM(item); - } else { - this.removeItemFromDOM(item); - } - } - - // DOM Events - - private onClick(e: MouseEvent): void { - if (this.lastPointerType && this.lastPointerType !== 'mouse') { - return; - } - - let event = new Mouse.StandardMouseEvent(e); - let item = this.getItemAround(event.target); - - if (!item) { - return; - } - - if (Browser.isIE && Date.now() - this.lastClickTimeStamp < 300) { - // IE10+ doesn't set the detail property correctly. While IE10 simply - // counts the number of clicks, IE11 reports always 1. To align with - // other browser, we set the value to 2 if clicks events come in a 300ms - // sequence. - event.detail = 2; - } - this.lastClickTimeStamp = Date.now(); - - this.context.controller!.onClick(this.context.tree, item.model.getElement(), event); - } - - private onMouseMiddleClick(e: MouseEvent): void { - if (!this.context.controller!.onMouseMiddleClick!) { - return; - } - - let event = new Mouse.StandardMouseEvent(e); - let item = this.getItemAround(event.target); - - if (!item) { - return; - } - this.context.controller!.onMouseMiddleClick!(this.context.tree, item.model.getElement(), event); - } - - private onMouseDown(e: MouseEvent): void { - this.didJustPressContextMenuKey = false; - - if (!this.context.controller!.onMouseDown!) { - return; - } - - if (this.lastPointerType && this.lastPointerType !== 'mouse') { - return; - } - - let event = new Mouse.StandardMouseEvent(e); - - if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) { - return; - } - - let item = this.getItemAround(event.target); - - if (!item) { - return; - } - - this.context.controller!.onMouseDown!(this.context.tree, item.model.getElement(), event); - } - - private onMouseUp(e: MouseEvent): void { - if (!this.context.controller!.onMouseUp!) { - return; - } - - if (this.lastPointerType && this.lastPointerType !== 'mouse') { - return; - } - - let event = new Mouse.StandardMouseEvent(e); - - if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) { - return; - } - - let item = this.getItemAround(event.target); - - if (!item) { - return; - } - - this.context.controller!.onMouseUp!(this.context.tree, item.model.getElement(), event); - } - - private onTap(e: Touch.GestureEvent): void { - let item = this.getItemAround(e.initialTarget); - - if (!item) { - return; - } - - this.context.controller!.onTap(this.context.tree, item.model.getElement(), e); - } - - private onTouchChange(event: Touch.GestureEvent): void { - event.preventDefault(); - event.stopPropagation(); - - this.scrollTop -= event.translationY; - } - - private onContextMenu(keyboardEvent: KeyboardEvent): void; - private onContextMenu(mouseEvent: MouseEvent): void; - private onContextMenu(event: KeyboardEvent | MouseEvent): void { - let resultEvent: _.ContextMenuEvent; - let element: any; - - if (event instanceof KeyboardEvent || this.didJustPressContextMenuKey) { - this.didJustPressContextMenuKey = false; - - let keyboardEvent = new Keyboard.StandardKeyboardEvent(event); - element = this.model!.getFocus(); - - let position: DOM.IDomNodePagePosition; - - if (!element) { - element = this.model!.getInput(); - position = DOM.getDomNodePagePosition(this.inputItem.element); - } else { - const id = this.context.dataSource.getId(this.context.tree, element); - const viewItem = this.items[id!]; - position = DOM.getDomNodePagePosition(viewItem.element); - } - - resultEvent = new _.KeyboardContextMenuEvent(position.left + position.width, position.top, keyboardEvent); - - } else { - let mouseEvent = new Mouse.StandardMouseEvent(event); - let item = this.getItemAround(mouseEvent.target); - - if (!item) { - return; - } - - element = item.model.getElement(); - resultEvent = new _.MouseContextMenuEvent(mouseEvent); - } - - this.context.controller!.onContextMenu(this.context.tree, element, resultEvent); - } - - private onKeyDown(e: KeyboardEvent): void { - let event = new Keyboard.StandardKeyboardEvent(e); - - this.didJustPressContextMenuKey = event.keyCode === KeyCode.ContextMenu || (event.shiftKey && event.keyCode === KeyCode.F10); - - if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return; // Ignore event if target is a form input field (avoids browser specific issues) - } - - if (this.didJustPressContextMenuKey) { - event.preventDefault(); - event.stopPropagation(); - } - - this.context.controller!.onKeyDown(this.context.tree, event); - } - - private onKeyUp(e: KeyboardEvent): void { - if (this.didJustPressContextMenuKey) { - this.onContextMenu(e); - } - - this.didJustPressContextMenuKey = false; - this.context.controller!.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); - } - - private onDragStart(item: ViewItem, e: any): void { - if (this.model!.getHighlight()) { - return; - } - - let element = item.model.getElement(); - let selection = this.model!.getSelection(); - let elements: any[]; - - if (selection.indexOf(element) > -1) { - elements = selection; - } else { - elements = [element]; - } - - e.dataTransfer.effectAllowed = 'copyMove'; - e.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([item.uri])); - if (e.dataTransfer.setDragImage) { - let label: string; - - if (this.context.dnd!.getDragLabel) { - label = this.context.dnd!.getDragLabel!(this.context.tree, elements); - } else { - label = String(elements.length); - } - - const dragImage = document.createElement('div'); - dragImage.className = 'monaco-tree-drag-image'; - dragImage.textContent = label; - document.body.appendChild(dragImage); - e.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => document.body.removeChild(dragImage), 0); - } - - this.currentDragAndDropData = new dnd.ElementsDragAndDropData(elements); - StaticDND.CurrentDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); - - this.context.dnd!.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); - } - - private setupDragAndDropScrollInterval(): void { - let viewTop = DOM.getTopLeftOffset(this.wrapper).top; - - if (!this.dragAndDropScrollInterval) { - this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === null) { - return; - } - - let diff = this.dragAndDropMouseY - viewTop; - let scrollDiff = 0; - let upperLimit = this.viewHeight - 35; - - if (diff < 35) { - scrollDiff = Math.max(-14, 0.2 * (diff - 35)); - } else if (diff > upperLimit) { - scrollDiff = Math.min(14, 0.2 * (diff - upperLimit)); - } - - this.scrollTop += scrollDiff; - }, 10); - - this.cancelDragAndDropScrollTimeout(); - - this.dragAndDropScrollTimeout = window.setTimeout(() => { - this.cancelDragAndDropScrollInterval(); - this.dragAndDropScrollTimeout = null; - }, 1000); - } - } - - private cancelDragAndDropScrollInterval(): void { - if (this.dragAndDropScrollInterval) { - window.clearInterval(this.dragAndDropScrollInterval); - this.dragAndDropScrollInterval = null; - } - - this.cancelDragAndDropScrollTimeout(); - } - - private cancelDragAndDropScrollTimeout(): void { - if (this.dragAndDropScrollTimeout) { - window.clearTimeout(this.dragAndDropScrollTimeout); - this.dragAndDropScrollTimeout = null; - } - } - - private onDragOver(e: DragEvent): boolean { - e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - - let event = new Mouse.DragMouseEvent(e); - - let viewItem = this.getItemAround(event.target); - - if (!viewItem || (event.posx === 0 && event.posy === 0 && event.browserEvent.type === DOM.EventType.DRAG_LEAVE)) { - // dragging outside of tree - - if (this.currentDropTarget) { - // clear previously hovered element feedback - - this.currentDropTargets!.forEach(i => i.dropTarget = false); - this.currentDropTargets = []; - this.currentDropDisposable.dispose(); - } - - this.cancelDragAndDropScrollInterval(); - this.currentDropTarget = null; - this.currentDropElement = null; - this.dragAndDropMouseY = null; - - return false; - } - - // dragging inside the tree - this.setupDragAndDropScrollInterval(); - this.dragAndDropMouseY = event.posy; - - if (!this.currentDragAndDropData) { - // just started dragging - - if (StaticDND.CurrentDragAndDropData) { - this.currentDragAndDropData = StaticDND.CurrentDragAndDropData; - } else { - if (!event.dataTransfer.types) { - return false; - } - - this.currentDragAndDropData = new dnd.DesktopDragAndDropData(); - } - } - - this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer!); - - let element: any; - let item: Model.Item | null = viewItem.model; - let reaction: _.IDragOverReaction | null; - - // check the bubble up behavior - do { - element = item ? item.getElement() : this.model!.getInput(); - reaction = this.context.dnd!.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); - - if (!reaction || reaction.bubble !== _.DragOverBubble.BUBBLE_UP) { - break; - } - - item = item && item.parent; - } while (item); - - if (!item) { - this.currentDropElement = null; - return false; - } - - let canDrop = reaction && reaction.accept; - - if (canDrop) { - this.currentDropElement = item.getElement(); - event.preventDefault(); - event.dataTransfer.dropEffect = reaction!.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; - } else { - this.currentDropElement = null; - } - - // item is the model item where drop() should be called - - // can be null - let currentDropTarget = item.id === this.inputItem.id ? this.inputItem : this.items[item.id]; - - if (this.shouldInvalidateDropReaction || this.currentDropTarget !== currentDropTarget || !reactionEquals(this.currentDropElementReaction, reaction)) { - this.shouldInvalidateDropReaction = false; - - if (this.currentDropTarget) { - this.currentDropTargets!.forEach(i => i.dropTarget = false); - this.currentDropTargets = []; - this.currentDropDisposable.dispose(); - } - - this.currentDropTarget = currentDropTarget; - this.currentDropElementReaction = reaction!; - - if (canDrop) { - // setup hover feedback for drop target - - if (this.currentDropTarget) { - this.currentDropTarget.dropTarget = true; - this.currentDropTargets!.push(this.currentDropTarget); - } - - if (reaction!.bubble === _.DragOverBubble.BUBBLE_DOWN) { - let nav = item.getNavigator(); - let child: Model.Item | null; - while (child = nav.next()) { - viewItem = this.items[child.id]; - if (viewItem) { - viewItem.dropTarget = true; - this.currentDropTargets!.push(viewItem); - } - } - } - - if (reaction!.autoExpand) { - const timeoutPromise = timeout(500); - this.currentDropDisposable = Lifecycle.toDisposable(() => timeoutPromise.cancel()); - - timeoutPromise - .then(() => this.context.tree.expand(this.currentDropElement)) - .then(() => this.shouldInvalidateDropReaction = true); - } - } - } - - return true; - } - - private onDrop(e: DragEvent): void { - if (this.currentDropElement) { - let event = new Mouse.DragMouseEvent(e); - event.preventDefault(); - this.currentDragAndDropData!.update((event.browserEvent as DragEvent).dataTransfer!); - this.context.dnd!.drop(this.context.tree, this.currentDragAndDropData!, this.currentDropElement, event); - this.onDragEnd(e); - } - this.cancelDragAndDropScrollInterval(); - } - - private onDragEnd(e: DragEvent): void { - if (this.currentDropTarget) { - this.currentDropTargets!.forEach(i => i.dropTarget = false); - this.currentDropTargets = []; - } - - this.currentDropDisposable.dispose(); - - this.cancelDragAndDropScrollInterval(); - this.currentDragAndDropData = null; - StaticDND.CurrentDragAndDropData = undefined; - this.currentDropElement = null; - this.currentDropTarget = null; - this.dragAndDropMouseY = null; - } - - private onFocus(): void { - if (!this.context.options.alwaysFocused) { - DOM.addClass(this.domNode, 'focused'); - } - - this._onDOMFocus.fire(); - } - - private onBlur(): void { - if (!this.context.options.alwaysFocused) { - DOM.removeClass(this.domNode, 'focused'); - } - - this.domNode.removeAttribute('aria-activedescendant'); // ARIA - - this._onDOMBlur.fire(); - } - - // MS specific DOM Events - - private onMsPointerDown(event: MSPointerEvent): void { - if (!this.msGesture) { - return; - } - - // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions - let pointerType = event.pointerType; - if (pointerType === ((event).MSPOINTER_TYPE_MOUSE || 'mouse')) { - this.lastPointerType = 'mouse'; - return; - } else if (pointerType === ((event).MSPOINTER_TYPE_TOUCH || 'touch')) { - this.lastPointerType = 'touch'; - } else { - return; - } - - event.stopPropagation(); - event.preventDefault(); - - this.msGesture.addPointer(event.pointerId); - } - - private onThrottledMsGestureChange(event: IThrottledGestureEvent): void { - this.scrollTop -= event.translationY; - } - - private onMsGestureTap(event: MSGestureEvent): void { - (event).initialTarget = document.elementFromPoint(event.clientX, event.clientY); - this.onTap(event); - } - - // DOM changes - - private insertItemInDOM(item: ViewItem): void { - let elementAfter: HTMLElement | null = null; - let itemAfter = this.itemAfter(item); - - if (itemAfter && itemAfter.element) { - elementAfter = itemAfter.element; - } - - item.insertInDOM(this.rowsContainer, elementAfter); - } - - private removeItemFromDOM(item: ViewItem): void { - if (!item) { - return; - } - - item.removeFromDOM(); - } - - // Helpers - - private shouldBeRendered(item: ViewItem): boolean { - return item.top < this.lastRenderTop + this.lastRenderHeight && item.top + item.height > this.lastRenderTop; - } - - private getItemAround(element: HTMLElement): ViewItem | undefined { - let candidate: ViewItem = this.inputItem; - let el: HTMLElement | null = element; - - do { - if ((el)[TreeView.BINDING]) { - candidate = (el)[TreeView.BINDING]; - } - - if (el === this.wrapper || el === this.domNode) { - return candidate; - } - - if (el === this.scrollableElement.getDomNode() || el === document.body) { - return undefined; - } - } while (el = el.parentElement); - - return undefined; - } - - // Cleanup - - private releaseModel(): void { - if (this.model) { - this.modelListeners = Lifecycle.dispose(this.modelListeners); - this.model = null; - } - } - - public dispose(): void { - // TODO@joao: improve - this.scrollableElement.dispose(); - - this.releaseModel(); - - this.viewListeners = Lifecycle.dispose(this.viewListeners); - - this._onDOMFocus.dispose(); - this._onDOMBlur.dispose(); - - if (this.domNode.parentNode) { - this.domNode.parentNode.removeChild(this.domNode); - } - - if (this.items) { - Object.keys(this.items).forEach(key => this.items[key].removeFromDOM()); - } - - if (this.context.cache) { - this.context.cache.dispose(); - } - this.gestureDisposable.dispose(); - - super.dispose(); - } -} diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts deleted file mode 100644 index 823f0bb0a9d..00000000000 --- a/src/vs/base/parts/tree/browser/treeViewModel.ts +++ /dev/null @@ -1,235 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { INextIterator, ArrayIterator } from 'vs/base/common/iterator'; -import { Item } from './treeModel'; - -export interface IViewItem { - model: Item; - top: number; - height: number; - width: number; -} - -export class HeightMap { - - private heightMap: IViewItem[] = []; - private indexes: { [item: string]: number; } = {}; - - getContentHeight(): number { - let last = this.heightMap[this.heightMap.length - 1]; - return !last ? 0 : last.top + last.height; - } - - onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number | undefined { - let item: Item | null = null; - let viewItem: IViewItem; - let i: number, j: number; - let totalSize: number; - let sizeDiff = 0; - - if (afterItemId === null) { - i = 0; - totalSize = 0; - } else { - i = this.indexes[afterItemId] + 1; - viewItem = this.heightMap[i - 1]; - - if (!viewItem) { - console.error('view item doesnt exist'); - return undefined; - } - - totalSize = viewItem.top + viewItem.height; - } - - let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0); - - let itemsToInsert: IViewItem[] = []; - - while (item = iterator.next()) { - viewItem = this.createViewItem(item); - viewItem.top = totalSize + sizeDiff; - - this.indexes[item.id] = i++; - itemsToInsert.push(viewItem); - sizeDiff += viewItem.height; - } - - boundSplice.apply(this.heightMap, itemsToInsert); - - for (j = i; j < this.heightMap.length; j++) { - viewItem = this.heightMap[j]; - viewItem.top += sizeDiff; - this.indexes[viewItem.model.id] = j; - } - - for (j = itemsToInsert.length - 1; j >= 0; j--) { - this.onInsertItem(itemsToInsert[j]); - } - - for (j = this.heightMap.length - 1; j >= i; j--) { - this.onRefreshItem(this.heightMap[j]); - } - - return sizeDiff; - } - - onInsertItem(item: IViewItem): void { - // noop - } - - // Contiguous items - onRemoveItems(iterator: INextIterator): void { - let itemId: string | null = null; - let viewItem: IViewItem; - let startIndex: number | null = null; - let i = 0; - let sizeDiff = 0; - - while (itemId = iterator.next()) { - i = this.indexes[itemId]; - viewItem = this.heightMap[i]; - - if (!viewItem) { - console.error('view item doesnt exist'); - return; - } - - sizeDiff -= viewItem.height; - delete this.indexes[itemId]; - this.onRemoveItem(viewItem); - - if (startIndex === null) { - startIndex = i; - } - } - - if (sizeDiff === 0 || startIndex === null) { - return; - } - - this.heightMap.splice(startIndex, i - startIndex + 1); - - for (i = startIndex; i < this.heightMap.length; i++) { - viewItem = this.heightMap[i]; - viewItem.top += sizeDiff; - this.indexes[viewItem.model.id] = i; - this.onRefreshItem(viewItem); - } - } - - onRemoveItem(item: IViewItem): void { - // noop - } - - onRefreshItemSet(items: Item[]): void { - let sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); - this.onRefreshItems(new ArrayIterator(sortedItems)); - } - - // Ordered, but not necessarily contiguous items - onRefreshItems(iterator: INextIterator): void { - let item: Item | null = null; - let viewItem: IViewItem; - let newHeight: number; - let i: number, j: number | null = null; - let cummDiff = 0; - - while (item = iterator.next()) { - i = this.indexes[item.id]; - - for (; cummDiff !== 0 && j !== null && j < i; j++) { - viewItem = this.heightMap[j]; - viewItem.top += cummDiff; - this.onRefreshItem(viewItem); - } - - viewItem = this.heightMap[i]; - newHeight = item.getHeight(); - viewItem.top += cummDiff; - cummDiff += newHeight - viewItem.height; - viewItem.height = newHeight; - this.onRefreshItem(viewItem, true); - - j = i + 1; - } - - if (cummDiff !== 0 && j !== null) { - for (; j < this.heightMap.length; j++) { - viewItem = this.heightMap[j]; - viewItem.top += cummDiff; - this.onRefreshItem(viewItem); - } - } - } - - onRefreshItem(item: IViewItem, needsRender: boolean = false): void { - // noop - } - - itemsCount(): number { - return this.heightMap.length; - } - - itemAt(position: number): string { - return this.heightMap[this.indexAt(position)].model.id; - } - - withItemsInRange(start: number, end: number, fn: (item: string) => void): void { - start = this.indexAt(start); - end = this.indexAt(end); - for (let i = start; i <= end; i++) { - fn(this.heightMap[i].model.id); - } - } - - indexAt(position: number): number { - let left = 0; - let right = this.heightMap.length; - let center: number; - let item: IViewItem; - - // Binary search - while (left < right) { - center = Math.floor((left + right) / 2); - item = this.heightMap[center]; - - if (position < item.top) { - right = center; - } else if (position >= item.top + item.height) { - if (left === center) { - break; - } - left = center; - } else { - return center; - } - } - - return this.heightMap.length; - } - - indexAfter(position: number): number { - return Math.min(this.indexAt(position) + 1, this.heightMap.length); - } - - itemAtIndex(index: number): IViewItem { - return this.heightMap[index]; - } - - itemAfter(item: IViewItem): IViewItem { - return this.heightMap[this.indexes[item.model.id] + 1] || null; - } - - protected createViewItem(item: Item): IViewItem { - throw new Error('not implemented'); - } - - dispose(): void { - this.heightMap = []; - this.indexes = {}; - } -} diff --git a/src/vs/base/parts/tree/test/browser/treeModel.test.ts b/src/vs/base/parts/tree/test/browser/treeModel.test.ts deleted file mode 100644 index 18d27eefada..00000000000 --- a/src/vs/base/parts/tree/test/browser/treeModel.test.ts +++ /dev/null @@ -1,1662 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as lifecycle from 'vs/base/common/lifecycle'; -import * as _ from 'vs/base/parts/tree/browser/tree'; -import * as model from 'vs/base/parts/tree/browser/treeModel'; -import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; -import { Event, Emitter } from 'vs/base/common/event'; -import { timeout } from 'vs/base/common/async'; - -export class FakeRenderer { - - public getHeight(tree: _.ITree, element: any): number { - return 20; - } - - public getTemplateId(tree: _.ITree, element: any): string { - return 'fake'; - } - - public renderTemplate(tree: _.ITree, templateId: string, container: any): any { - return null; - } - - public renderElement(tree: _.ITree, element: any, templateId: string, templateData: any): void { - // noop - } - - public disposeTemplate(tree: _.ITree, templateId: string, templateData: any): void { - // noop - } -} - -class TreeContext implements _.ITreeContext { - - public tree: _.ITree = null!; - public options: _.ITreeOptions = { autoExpandSingleChildren: true }; - public dataSource: _.IDataSource; - public renderer: _.IRenderer; - public controller?: _.IController; - public dnd?: _.IDragAndDrop; - public filter: _.IFilter; - public sorter: _.ISorter; - - constructor(public configuration: _.ITreeConfiguration) { - this.dataSource = configuration.dataSource; - this.renderer = configuration.renderer || new FakeRenderer(); - this.controller = configuration.controller; - this.dnd = configuration.dnd; - this.filter = configuration.filter || new TreeDefaults.DefaultFilter(); - this.sorter = configuration.sorter || new TreeDefaults.DefaultSorter(); - } -} - -class TreeModel extends model.TreeModel { - - constructor(configuration: _.ITreeConfiguration) { - super(new TreeContext(configuration)); - } -} - -class EventCounter { - - private listeners: lifecycle.IDisposable[]; - private _count: number; - - constructor() { - this.listeners = []; - this._count = 0; - } - - public listen(event: Event, fn: ((e: T) => void) | null = null): () => void { - let r = event(data => { - this._count++; - if (fn) { - fn(data); - } - }); - - this.listeners.push(r); - - return () => { - let idx = this.listeners.indexOf(r); - if (idx > -1) { - this.listeners.splice(idx, 1); - r.dispose(); - } - }; - } - - public up(): void { - this._count++; - } - - public get count(): number { - return this._count; - } - - public dispose(): void { - this.listeners = lifecycle.dispose(this.listeners); - this._count = -1; - } -} - -const SAMPLE: any = { - ONE: { id: 'one' }, - - AB: { - id: 'ROOT', children: [ - { - id: 'a', children: [ - { id: 'aa' }, - { id: 'ab' } - ] - }, - { id: 'b' }, - { - id: 'c', children: [ - { id: 'ca' }, - { id: 'cb' } - ] - } - ] - }, - - DEEP: { - id: 'ROOT', children: [ - { - id: 'a', children: [ - { - id: 'x', children: [ - { id: 'xa' }, - { id: 'xb' }, - ] - } - ] - }, - { id: 'b' } - ] - }, - - DEEP2: { - id: 'ROOT', children: [ - { - id: 'a', children: [ - { - id: 'x', children: [ - { id: 'xa' }, - { id: 'xb' }, - ] - }, - { id: 'y' } - ] - }, - { id: 'b' } - ] - } -}; - -class TestDataSource implements _.IDataSource { - public getId(tree: _.ITree, element: any): string { - return element.id; - } - - public hasChildren(tree: _.ITree, element: any): boolean { - return !!element.children; - } - - public getChildren(tree: _.ITree, element: any): Promise { - return Promise.resolve(element.children); - } - - public getParent(tree: _.ITree, element: any): Promise { - throw new Error('Not implemented'); - } -} - -suite('TreeModel', () => { - let model: model.TreeModel; - let counter: EventCounter; - - setup(() => { - counter = new EventCounter(); - model = new TreeModel({ - dataSource: new TestDataSource() - }); - }); - - teardown(() => { - counter.dispose(); - model.dispose(); - }); - - test('setInput, getInput', () => { - model.setInput(SAMPLE.ONE); - assert.equal(model.getInput(), SAMPLE.ONE); - }); - - test('refresh() refreshes all', () => { - return model.setInput(SAMPLE.AB).then(() => { - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem); // 4 - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(null); - }).then(() => { - assert.equal(counter.count, 8); - }); - }); - - test('refresh(root) refreshes all', () => { - return model.setInput(SAMPLE.AB).then(() => { - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem); // 4 - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(SAMPLE.AB); - }).then(() => { - assert.equal(counter.count, 8); - }); - }); - - test('refresh(root, false) refreshes the root', () => { - return model.setInput(SAMPLE.AB).then(() => { - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem); // 1 - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(SAMPLE.AB, false); - }).then(() => { - assert.equal(counter.count, 5); - }); - }); - - test('refresh(collapsed element) does not refresh descendants', () => { - return model.setInput(SAMPLE.AB).then(() => { - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem); // 1 - counter.listen(model.onRefreshItemChildren); // 0 - counter.listen(model.onDidRefreshItemChildren); // 0 - return model.refresh(SAMPLE.AB.children[0]); - }).then(() => { - assert.equal(counter.count, 3); - }); - }); - - test('refresh(expanded element) refreshes the element and descendants', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expand(SAMPLE.AB.children[0]).then(() => { - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem); // 3 - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(SAMPLE.AB.children[0]); - }); - }).then(() => { - assert.equal(counter.count, 7); - }); - }); - - test('refresh(element, false) refreshes the element', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expand(SAMPLE.AB.children[0]).then(() => { - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem, item => { // 1 - assert.equal(item.id, 'a'); - counter.up(); - }); - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(SAMPLE.AB.children[0], false); - }); - }).then(() => { - assert.equal(counter.count, 6); - }); - }); - - test('depths', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expandAll(['a', 'c']).then(() => { - counter.listen(model.onDidRefreshItem, item => { - switch (item.id) { - case 'ROOT': assert.equal(item.getDepth(), 0); break; - case 'a': assert.equal(item.getDepth(), 1); break; - case 'aa': assert.equal(item.getDepth(), 2); break; - case 'ab': assert.equal(item.getDepth(), 2); break; - case 'b': assert.equal(item.getDepth(), 1); break; - case 'c': assert.equal(item.getDepth(), 1); break; - case 'ca': assert.equal(item.getDepth(), 2); break; - case 'cb': assert.equal(item.getDepth(), 2); break; - default: return; - } - counter.up(); - }); - - return model.refresh(); - }); - }).then(() => { - assert.equal(counter.count, 16); - }); - }); - - test('intersections', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expandAll(['a', 'c']).then(() => { - // going internals - const r = (model).registry; - - assert(r.getItem('a').intersects(r.getItem('a'))); - assert(r.getItem('a').intersects(r.getItem('aa'))); - assert(r.getItem('a').intersects(r.getItem('ab'))); - assert(r.getItem('aa').intersects(r.getItem('a'))); - assert(r.getItem('ab').intersects(r.getItem('a'))); - assert(!r.getItem('aa').intersects(r.getItem('ab'))); - assert(!r.getItem('a').intersects(r.getItem('b'))); - assert(!r.getItem('a').intersects(r.getItem('c'))); - assert(!r.getItem('a').intersects(r.getItem('ca'))); - assert(!r.getItem('aa').intersects(r.getItem('ca'))); - }); - }); - }); -}); - -suite('TreeModel - TreeNavigator', () => { - let model: model.TreeModel; - let counter: EventCounter; - - setup(() => { - counter = new EventCounter(); - model = new TreeModel({ - dataSource: new TestDataSource() - }); - }); - - teardown(() => { - counter.dispose(); - model.dispose(); - }); - - test('next()', () => { - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - }); - }); - - test('previous()', () => { - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator(); - - nav.next(); - nav.next(); - - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.previous()!.id, 'b'); - assert.equal(nav.previous()!.id, 'a'); - assert.equal(nav.previous() && false, null); - }); - }); - - test('parent()', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - const nav = model.getNavigator(); - - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.parent()!.id, 'a'); - - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.parent()!.id, 'a'); - - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next()!.id, 'ca'); - assert.equal(nav.parent()!.id, 'c'); - - assert.equal(nav.parent() && false, null); - }); - }); - }); - - test('next() - scoped', () => { - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator(SAMPLE.AB.children[0]); - return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next() && false, null); - }); - }); - }); - - test('previous() - scoped', () => { - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator(SAMPLE.AB.children[0]); - return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.previous()!.id, 'aa'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('parent() - scoped', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - const nav = model.getNavigator(SAMPLE.AB.children[0]); - - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.parent() && false, null); - }); - }); - }); - - test('next() - non sub tree only', () => { - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator(SAMPLE.AB.children[0], false); - return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - }); - }); - }); - - test('previous() - non sub tree only', () => { - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator(SAMPLE.AB.children[0], false); - return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.previous()!.id, 'b'); - assert.equal(nav.previous()!.id, 'ab'); - assert.equal(nav.previous()!.id, 'aa'); - assert.equal(nav.previous()!.id, 'a'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('parent() - non sub tree only', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - const nav = model.getNavigator(SAMPLE.AB.children[0], false); - - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.parent()!.id, 'a'); - assert.equal(nav.parent() && false, null); - }); - }); - }); - - test('deep next() - scoped', () => { - return model.setInput(SAMPLE.DEEP).then(() => { - return model.expand(SAMPLE.DEEP.children[0]).then(() => { - return model.expand(SAMPLE.DEEP.children[0].children[0]).then(() => { - const nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); - assert.equal(nav.next()!.id, 'xa'); - assert.equal(nav.next()!.id, 'xb'); - assert.equal(nav.next() && false, null); - }); - }); - }); - }); - - test('deep previous() - scoped', () => { - return model.setInput(SAMPLE.DEEP).then(() => { - return model.expand(SAMPLE.DEEP.children[0]).then(() => { - return model.expand(SAMPLE.DEEP.children[0].children[0]).then(() => { - const nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); - assert.equal(nav.next()!.id, 'xa'); - assert.equal(nav.next()!.id, 'xb'); - assert.equal(nav.previous()!.id, 'xa'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - }); - - test('last()', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - const nav = model.getNavigator(); - assert.equal(nav.last()!.id, 'cb'); - }); - }); - }); -}); - -suite('TreeModel - Expansion', () => { - let model: model.TreeModel; - let counter: EventCounter; - - setup(() => { - counter = new EventCounter(); - model = new TreeModel({ - dataSource: new TestDataSource() - }); - }); - - teardown(() => { - counter.dispose(); - model.dispose(); - }); - - test('collapse, expand', () => { - return model.setInput(SAMPLE.AB).then(() => { - counter.listen(model.onExpandItem, (e) => { - assert.equal(e.item.id, 'a'); - const nav = model.getNavigator(e.item); - assert.equal(nav.next() && false, null); - }); - - counter.listen(model.onDidExpandItem, (e) => { - assert.equal(e.item.id, 'a'); - const nav = model.getNavigator(e.item); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next() && false, null); - }); - - assert(!model.isExpanded(SAMPLE.AB.children[0])); - - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - - assert.equal(model.getExpandedElements().length, 0); - - return model.expand(SAMPLE.AB.children[0]).then(() => { - assert(model.isExpanded(SAMPLE.AB.children[0])); - - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - - const expandedElements = model.getExpandedElements(); - assert.equal(expandedElements.length, 1); - assert.equal(expandedElements[0].id, 'a'); - - assert.equal(counter.count, 2); - }); - }); - }); - - test('toggleExpansion', () => { - return model.setInput(SAMPLE.AB).then(() => { - assert(!model.isExpanded(SAMPLE.AB.children[0])); - - return model.toggleExpansion(SAMPLE.AB.children[0]).then(() => { - assert(model.isExpanded(SAMPLE.AB.children[0])); - assert(!model.isExpanded(SAMPLE.AB.children[0].children[0])); - - return model.toggleExpansion(SAMPLE.AB.children[0].children[0]).then(() => { - assert(!model.isExpanded(SAMPLE.AB.children[0].children[0])); - - return model.toggleExpansion(SAMPLE.AB.children[0]).then(() => { - assert(!model.isExpanded(SAMPLE.AB.children[0])); - }); - }); - }); - }); - }); - - test('collapseAll', () => { - return model.setInput(SAMPLE.DEEP2).then(() => { - return model.expand(SAMPLE.DEEP2.children[0]).then(() => { - return model.expand(SAMPLE.DEEP2.children[0].children[0]).then(() => { - - assert(model.isExpanded(SAMPLE.DEEP2.children[0])); - assert(model.isExpanded(SAMPLE.DEEP2.children[0].children[0])); - - return model.collapseAll().then(() => { - assert(!model.isExpanded(SAMPLE.DEEP2.children[0])); - - return model.expand(SAMPLE.DEEP2.children[0]).then(() => { - assert(!model.isExpanded(SAMPLE.DEEP2.children[0].children[0])); - }); - }); - }); - }); - }); - }); - - test('auto expand single child folders', () => { - return model.setInput(SAMPLE.DEEP).then(() => { - return model.expand(SAMPLE.DEEP.children[0]).then(() => { - assert(model.isExpanded(SAMPLE.DEEP.children[0])); - assert(model.isExpanded(SAMPLE.DEEP.children[0].children[0])); - }); - }); - }); - - test('expand can trigger refresh', () => { - // MUnit.expect(16); - return model.setInput(SAMPLE.AB).then(() => { - - assert(!model.isExpanded(SAMPLE.AB.children[0])); - - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - - const f: () => void = counter.listen(model.onRefreshItemChildren, (e) => { - assert.equal(e.item.id, 'a'); - f(); - }); - - const g: () => void = counter.listen(model.onDidRefreshItemChildren, (e) => { - assert.equal(e.item.id, 'a'); - g(); - }); - - return model.expand(SAMPLE.AB.children[0]).then(() => { - assert(model.isExpanded(SAMPLE.AB.children[0])); - - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - - assert.equal(counter.count, 2); - }); - }); - }); - - test('top level collapsed', () => { - return model.setInput(SAMPLE.AB).then(() => { - return model.collapseAll([{ id: 'a' }, { id: 'b' }, { id: 'c' }]).then(() => { - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.previous()!.id, 'b'); - assert.equal(nav.previous()!.id, 'a'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('shouldAutoexpand', () => { - // setup - const model = new TreeModel({ - dataSource: { - getId: (_, e) => e, - hasChildren: (_, e) => true, - getChildren: (_, e) => { - if (e === 'root') { return Promise.resolve(['a', 'b', 'c']); } - if (e === 'b') { return Promise.resolve(['b1']); } - return Promise.resolve([]); - }, - getParent: (_, e): Promise => { throw new Error('not implemented'); }, - shouldAutoexpand: (_, e) => e === 'b' - } - }); - - return model.setInput('root').then(() => { - return model.refresh('root', true); - }).then(() => { - assert(!model.isExpanded('a')); - assert(model.isExpanded('b')); - assert(!model.isExpanded('c')); - }); - }); -}); - -class TestFilter implements _.IFilter { - - public fn: (element: any) => boolean; - - constructor() { - this.fn = () => true; - } - - public isVisible(tree: _.ITree, element: any): boolean { - return this.fn(element); - } -} - -suite('TreeModel - Filter', () => { - let model: model.TreeModel; - let counter: EventCounter; - let filter: TestFilter; - - setup(() => { - counter = new EventCounter(); - filter = new TestFilter(); - model = new TreeModel({ - dataSource: new TestDataSource(), - filter: filter - }); - }); - - teardown(() => { - counter.dispose(); - model.dispose(); - }); - - test('no filter', () => { - return model.setInput(SAMPLE.AB).then(() => { - - return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next()!.id, 'ca'); - assert.equal(nav.next()!.id, 'cb'); - - assert.equal(nav.previous()!.id, 'ca'); - assert.equal(nav.previous()!.id, 'c'); - assert.equal(nav.previous()!.id, 'b'); - assert.equal(nav.previous()!.id, 'ab'); - assert.equal(nav.previous()!.id, 'aa'); - assert.equal(nav.previous()!.id, 'a'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('filter all', () => { - filter.fn = () => false; - - return model.setInput(SAMPLE.AB).then(() => { - return model.refresh().then(() => { - const nav = model.getNavigator(); - assert.equal(nav.next() && false, null); - }); - }); - }); - - test('simple filter', () => { - // hide elements that do not start with 'a' - filter.fn = (e) => e.id[0] === 'a'; - - return model.setInput(SAMPLE.AB).then(() => { - return model.expand({ id: 'a' }).then(() => { - - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'ab'); - assert.equal(nav.previous()!.id, 'aa'); - assert.equal(nav.previous()!.id, 'a'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('simple filter 2', () => { - // hide 'ab' - filter.fn = (e) => e.id !== 'ab'; - - return model.setInput(SAMPLE.AB).then(() => { - return model.expand({ id: 'a' }).then(() => { - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'a'); - assert.equal(nav.next()!.id, 'aa'); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next() && false, null); - }); - }); - }); - - test('simple filter, opposite', () => { - // hide elements that start with 'a' - filter.fn = (e) => e.id[0] !== 'a'; - - return model.setInput(SAMPLE.AB).then(() => { - return model.expand({ id: 'c' }).then(() => { - - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next()!.id, 'ca'); - assert.equal(nav.next()!.id, 'cb'); - assert.equal(nav.previous()!.id, 'ca'); - assert.equal(nav.previous()!.id, 'c'); - assert.equal(nav.previous()!.id, 'b'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('simple filter, mischieving', () => { - // hide the element 'a' - filter.fn = (e) => e.id !== 'a'; - - return model.setInput(SAMPLE.AB).then(() => { - return model.expand({ id: 'c' }).then(() => { - - const nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'b'); - assert.equal(nav.next()!.id, 'c'); - assert.equal(nav.next()!.id, 'ca'); - assert.equal(nav.next()!.id, 'cb'); - assert.equal(nav.previous()!.id, 'ca'); - assert.equal(nav.previous()!.id, 'c'); - assert.equal(nav.previous()!.id, 'b'); - assert.equal(nav.previous() && false, null); - }); - }); - }); - - test('simple filter & previous', () => { - // hide 'b' - filter.fn = (e) => e.id !== 'b'; - - return model.setInput(SAMPLE.AB).then(() => { - const nav = model.getNavigator({ id: 'c' }, false); - assert.equal(nav.previous()!.id, 'a'); - assert.equal(nav.previous() && false, null); - }); - }); -}); - -suite('TreeModel - Traits', () => { - let model: model.TreeModel; - let counter: EventCounter; - - setup(() => { - counter = new EventCounter(); - model = new TreeModel({ - dataSource: new TestDataSource() - }); - }); - - teardown(() => { - counter.dispose(); - model.dispose(); - }); - - test('Selection', () => { - return model.setInput(SAMPLE.AB).then(() => { - assert.equal(model.getSelection().length, 0); - model.select(SAMPLE.AB.children[1]); - assert(model.isSelected(SAMPLE.AB.children[1])); - assert.equal(model.getSelection().length, 1); - model.select(SAMPLE.AB.children[0]); - assert(model.isSelected(SAMPLE.AB.children[0])); - assert.equal(model.getSelection().length, 2); - model.select(SAMPLE.AB.children[2]); - assert(model.isSelected(SAMPLE.AB.children[2])); - assert.equal(model.getSelection().length, 3); - model.deselect(SAMPLE.AB.children[0]); - assert(!model.isSelected(SAMPLE.AB.children[0])); - assert.equal(model.getSelection().length, 2); - model.setSelection([]); - assert(!model.isSelected(SAMPLE.AB.children[0])); - assert(!model.isSelected(SAMPLE.AB.children[1])); - assert(!model.isSelected(SAMPLE.AB.children[2])); - assert.equal(model.getSelection().length, 0); - model.selectAll([SAMPLE.AB.children[0], SAMPLE.AB.children[1], SAMPLE.AB.children[2]]); - assert.equal(model.getSelection().length, 3); - model.select(SAMPLE.AB.children[0]); - assert.equal(model.getSelection().length, 3); - model.deselectAll([SAMPLE.AB.children[0], SAMPLE.AB.children[1], SAMPLE.AB.children[2]]); - assert.equal(model.getSelection().length, 0); - model.deselect(SAMPLE.AB.children[0]); - assert.equal(model.getSelection().length, 0); - - model.setSelection([SAMPLE.AB.children[0]]); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[0])); - assert(!model.isSelected(SAMPLE.AB.children[1])); - assert(!model.isSelected(SAMPLE.AB.children[2])); - - model.setSelection([SAMPLE.AB.children[0], SAMPLE.AB.children[1], SAMPLE.AB.children[2]]); - assert.equal(model.getSelection().length, 3); - assert(model.isSelected(SAMPLE.AB.children[0])); - assert(model.isSelected(SAMPLE.AB.children[1])); - assert(model.isSelected(SAMPLE.AB.children[2])); - - model.setSelection([SAMPLE.AB.children[1], SAMPLE.AB.children[2]]); - assert.equal(model.getSelection().length, 2); - assert(!model.isSelected(SAMPLE.AB.children[0])); - assert(model.isSelected(SAMPLE.AB.children[1])); - assert(model.isSelected(SAMPLE.AB.children[2])); - - model.setSelection([]); - assert.deepEqual(model.getSelection(), []); - assert.equal(model.getSelection().length, 0); - assert(!model.isSelected(SAMPLE.AB.children[0])); - assert(!model.isSelected(SAMPLE.AB.children[1])); - assert(!model.isSelected(SAMPLE.AB.children[2])); - - model.selectNext(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[0])); - - model.selectNext(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[1])); - - model.selectNext(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[2])); - - model.selectNext(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[2])); - - model.selectPrevious(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[1])); - - model.selectPrevious(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[0])); - - model.selectPrevious(); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[0])); - - model.selectNext(2); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[2])); - - model.selectPrevious(4); - assert.equal(model.getSelection().length, 1); - assert(model.isSelected(SAMPLE.AB.children[0])); - - assert.equal(model.isSelected(SAMPLE.AB.children[0]), true); - assert.equal(model.isSelected(SAMPLE.AB.children[2]), false); - }); - }); - - test('Focus', () => { - return model.setInput(SAMPLE.AB).then(() => { - assert(!model.getFocus()); - model.setFocus(SAMPLE.AB.children[1]); - assert(model.isFocused(SAMPLE.AB.children[1])); - assert(model.getFocus()); - model.setFocus(SAMPLE.AB.children[0]); - assert(model.isFocused(SAMPLE.AB.children[0])); - assert(model.getFocus()); - model.setFocus(SAMPLE.AB.children[2]); - assert(model.isFocused(SAMPLE.AB.children[2])); - assert(model.getFocus()); - model.setFocus(); - assert(!model.isFocused(SAMPLE.AB.children[0])); - assert(!model.isFocused(SAMPLE.AB.children[1])); - assert(!model.isFocused(SAMPLE.AB.children[2])); - assert(!model.getFocus()); - - model.setFocus(SAMPLE.AB.children[0]); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[0])); - assert(!model.isFocused(SAMPLE.AB.children[1])); - assert(!model.isFocused(SAMPLE.AB.children[2])); - - model.setFocus(); - assert(!model.getFocus()); - assert(!model.isFocused(SAMPLE.AB.children[0])); - assert(!model.isFocused(SAMPLE.AB.children[1])); - assert(!model.isFocused(SAMPLE.AB.children[2])); - - model.focusNext(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[0])); - - model.focusNext(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[1])); - - model.focusNext(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[2])); - - model.focusNext(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[2])); - - model.focusPrevious(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[1])); - - model.focusPrevious(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[0])); - - model.focusPrevious(); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[0])); - - model.focusNext(2); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[2])); - - model.focusPrevious(4); - assert(model.getFocus()); - assert(model.isFocused(SAMPLE.AB.children[0])); - - assert.equal(model.isFocused(SAMPLE.AB.children[0]), true); - assert.equal(model.isFocused(SAMPLE.AB.children[2]), false); - - model.focusFirst(); - assert(model.isFocused(SAMPLE.AB.children[0])); - model.focusNth(0); - assert(model.isFocused(SAMPLE.AB.children[0])); - model.focusNth(1); - assert(model.isFocused(SAMPLE.AB.children[1])); - }); - }); - - test('Highlight', () => { - return model.setInput(SAMPLE.AB).then(() => { - assert(!model.getHighlight()); - model.setHighlight(SAMPLE.AB.children[1]); - assert(model.isHighlighted(SAMPLE.AB.children[1])); - assert(model.getHighlight()); - model.setHighlight(SAMPLE.AB.children[0]); - assert(model.isHighlighted(SAMPLE.AB.children[0])); - assert(model.getHighlight()); - model.setHighlight(SAMPLE.AB.children[2]); - assert(model.isHighlighted(SAMPLE.AB.children[2])); - assert(model.getHighlight()); - model.setHighlight(); - assert(!model.isHighlighted(SAMPLE.AB.children[0])); - assert(!model.isHighlighted(SAMPLE.AB.children[1])); - assert(!model.isHighlighted(SAMPLE.AB.children[2])); - assert(!model.getHighlight()); - - model.setHighlight(SAMPLE.AB.children[0]); - assert(model.getHighlight()); - assert(model.isHighlighted(SAMPLE.AB.children[0])); - assert(!model.isHighlighted(SAMPLE.AB.children[1])); - assert(!model.isHighlighted(SAMPLE.AB.children[2])); - - assert.equal(model.isHighlighted(SAMPLE.AB.children[0]), true); - assert.equal(model.isHighlighted(SAMPLE.AB.children[2]), false); - - model.setHighlight(); - assert(!model.getHighlight()); - assert(!model.isHighlighted(SAMPLE.AB.children[0])); - assert(!model.isHighlighted(SAMPLE.AB.children[1])); - assert(!model.isHighlighted(SAMPLE.AB.children[2])); - }); - }); -}); - -class DynamicModel implements _.IDataSource { - - private data: any; - public promiseFactory: { (): Promise; } | null; - - private readonly _onGetChildren = new Emitter(); - readonly onGetChildren: Event = this._onGetChildren.event; - - private readonly _onDidGetChildren = new Emitter(); - readonly onDidGetChildren: Event = this._onDidGetChildren.event; - - constructor() { - this.data = { root: [] }; - this.promiseFactory = null; - } - - public addChild(parent: string, child: string): void { - if (!this.data[parent]) { - this.data[parent] = []; - } - this.data[parent].push(child); - } - - public removeChild(parent: string, child: string): void { - this.data[parent].splice(this.data[parent].indexOf(child), 1); - if (this.data[parent].length === 0) { - delete this.data[parent]; - } - } - - public move(element: string, oldParent: string, newParent: string): void { - this.removeChild(oldParent, element); - this.addChild(newParent, element); - } - - public rename(parent: string, oldName: string, newName: string): void { - this.removeChild(parent, oldName); - this.addChild(parent, newName); - } - - public getId(tree: _.ITree, element: any): string { - return element; - } - - public hasChildren(tree: _.ITree, element: any): boolean { - return !!this.data[element]; - } - - public getChildren(tree: _.ITree, element: any): Promise { - this._onGetChildren.fire(element); - const result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); - return result.then(() => { - this._onDidGetChildren.fire(element); - return Promise.resolve(this.data[element]); - }); - } - - public getParent(tree: _.ITree, element: any): Promise { - throw new Error('Not implemented'); - } -} - -suite('TreeModel - Dynamic data model', () => { - let model: model.TreeModel; - let dataModel: DynamicModel; - let counter: EventCounter; - - setup(() => { - counter = new EventCounter(); - dataModel = new DynamicModel(); - model = new TreeModel({ - dataSource: dataModel, - }); - }); - - teardown(() => { - counter.dispose(); - model.dispose(); - }); - - test('items get property disposed', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); - dataModel.addChild('father', 'daughter'); - dataModel.addChild('son', 'baby'); - - return model.setInput('root').then(() => { - return model.expandAll(['grandfather', 'father', 'son']).then(() => { - dataModel.removeChild('grandfather', 'father'); - - const items = ['baby', 'son', 'daughter', 'father']; - let times = 0; - counter.listen(model.onDidDisposeItem, item => { - assert.equal(items[times++], item.id); - }); - - return model.refresh().then(() => { - assert.equal(times, items.length); - assert.equal(counter.count, 4); - }); - }); - }); - }); - - test('addChild, removeChild, collapse', () => { - dataModel.addChild('root', 'super'); - dataModel.addChild('root', 'hyper'); - dataModel.addChild('root', 'mega'); - - return model.setInput('root').then(() => { - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'super'); - assert.equal(nav.next()!.id, 'hyper'); - assert.equal(nav.next()!.id, 'mega'); - assert.equal(nav.next() && false, null); - - dataModel.removeChild('root', 'hyper'); - return model.refresh().then(() => { - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'super'); - assert.equal(nav.next()!.id, 'mega'); - assert.equal(nav.next() && false, null); - - dataModel.addChild('mega', 'micro'); - dataModel.addChild('mega', 'nano'); - dataModel.addChild('mega', 'pico'); - - return model.refresh().then(() => { - return model.expand('mega').then(() => { - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'super'); - assert.equal(nav.next()!.id, 'mega'); - assert.equal(nav.next()!.id, 'micro'); - assert.equal(nav.next()!.id, 'nano'); - assert.equal(nav.next()!.id, 'pico'); - assert.equal(nav.next() && false, null); - - model.collapse('mega'); - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'super'); - assert.equal(nav.next()!.id, 'mega'); - assert.equal(nav.next() && false, null); - }); - }); - }); - }); - }); - - test('move', () => { - dataModel.addChild('root', 'super'); - dataModel.addChild('super', 'apples'); - dataModel.addChild('super', 'bananas'); - dataModel.addChild('super', 'pears'); - dataModel.addChild('root', 'hyper'); - dataModel.addChild('root', 'mega'); - - return model.setInput('root').then(() => { - - return model.expand('super').then(() => { - - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'super'); - assert.equal(nav.next()!.id, 'apples'); - assert.equal(nav.next()!.id, 'bananas'); - assert.equal(nav.next()!.id, 'pears'); - assert.equal(nav.next()!.id, 'hyper'); - assert.equal(nav.next()!.id, 'mega'); - assert.equal(nav.next() && false, null); - - dataModel.move('bananas', 'super', 'hyper'); - dataModel.move('apples', 'super', 'mega'); - - return model.refresh().then(() => { - - return model.expandAll(['hyper', 'mega']).then(() => { - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'super'); - assert.equal(nav.next()!.id, 'pears'); - assert.equal(nav.next()!.id, 'hyper'); - assert.equal(nav.next()!.id, 'bananas'); - assert.equal(nav.next()!.id, 'mega'); - assert.equal(nav.next()!.id, 'apples'); - assert.equal(nav.next() && false, null); - }); - }); - }); - }); - }); - - test('refreshing grandfather recursively should not refresh collapsed father\'s children immediately', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); - - return model.setInput('root').then(() => { - return model.expand('grandfather').then(() => { - return model.collapse('father').then(() => { - let times = 0; - let listener = dataModel.onGetChildren((element) => { - times++; - assert.equal(element, 'grandfather'); - }); - - return model.refresh('grandfather').then(() => { - assert.equal(times, 1); - listener.dispose(); - - listener = dataModel.onGetChildren((element) => { - times++; - assert.equal(element, 'father'); - }); - - return model.expand('father').then(() => { - assert.equal(times, 2); - listener.dispose(); - }); - }); - }); - }); - }); - }); - - test('simultaneously refreshing two disjoint elements should parallelize the refreshes', () => { - dataModel.addChild('root', 'father'); - dataModel.addChild('root', 'mother'); - dataModel.addChild('father', 'son'); - dataModel.addChild('mother', 'daughter'); - - return model.setInput('root').then(() => { - return model.expand('father').then(() => { - return model.expand('mother').then(() => { - - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'father'); - assert.equal(nav.next()!.id, 'son'); - assert.equal(nav.next()!.id, 'mother'); - assert.equal(nav.next()!.id, 'daughter'); - assert.equal(nav.next() && false, null); - - dataModel.removeChild('father', 'son'); - dataModel.removeChild('mother', 'daughter'); - dataModel.addChild('father', 'brother'); - dataModel.addChild('mother', 'sister'); - - dataModel.promiseFactory = () => { return timeout(0); }; - - let getTimes = 0; - let gotTimes = 0; - const getListener = dataModel.onGetChildren((element) => { getTimes++; }); - const gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - - const p1 = model.refresh('father'); - assert.equal(getTimes, 1); - assert.equal(gotTimes, 0); - - const p2 = model.refresh('mother'); - assert.equal(getTimes, 2); - assert.equal(gotTimes, 0); - - return Promise.all([p1, p2]).then(() => { - assert.equal(getTimes, 2); - assert.equal(gotTimes, 2); - - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'father'); - assert.equal(nav.next()!.id, 'brother'); - assert.equal(nav.next()!.id, 'mother'); - assert.equal(nav.next()!.id, 'sister'); - assert.equal(nav.next() && false, null); - - getListener.dispose(); - gotListener.dispose(); - }); - }); - }); - }); - }); - - test('simultaneously recursively refreshing two intersecting elements should concatenate the refreshes - ancestor first', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); - - return model.setInput('root').then(() => { - return model.expand('grandfather').then(() => { - return model.expand('father').then(() => { - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'grandfather'); - assert.equal(nav.next()!.id, 'father'); - assert.equal(nav.next()!.id, 'son'); - assert.equal(nav.next() && false, null); - - let refreshTimes = 0; - counter.listen(model.onDidRefreshItem, (e) => { refreshTimes++; }); - - let getTimes = 0; - const getListener = dataModel.onGetChildren((element) => { getTimes++; }); - - let gotTimes = 0; - const gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - - const p1Completes: Array<(value?: any) => void> = []; - dataModel.promiseFactory = () => { return new Promise((c) => { p1Completes.push(c); }); }; - - model.refresh('grandfather').then(() => { - // just a single get - assert.equal(refreshTimes, 1); // (+1) grandfather - assert.equal(getTimes, 1); - assert.equal(gotTimes, 0); - - // unblock the first get - p1Completes.shift()!(); - - // once the first get is unblocked, the second get should appear - assert.equal(refreshTimes, 2); // (+1) first father refresh - assert.equal(getTimes, 2); - assert.equal(gotTimes, 1); - - let p2Complete: () => void; - dataModel.promiseFactory = () => { return new Promise((c) => { p2Complete = c; }); }; - const p2 = model.refresh('father'); - - // same situation still - assert.equal(refreshTimes, 3); // (+1) second father refresh - assert.equal(getTimes, 2); - assert.equal(gotTimes, 1); - - // unblock the second get - p1Completes.shift()!(); - - // the third get should have appeared, it should've been waiting for the second one - assert.equal(refreshTimes, 4); // (+1) first son request - assert.equal(getTimes, 3); - assert.equal(gotTimes, 2); - - p2Complete!(); - - // all good - assert.equal(refreshTimes, 5); // (+1) second son request - assert.equal(getTimes, 3); - assert.equal(gotTimes, 3); - - return p2.then(() => { - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'grandfather'); - assert.equal(nav.next()!.id, 'father'); - assert.equal(nav.next()!.id, 'son'); - assert.equal(nav.next() && false, null); - - getListener.dispose(); - gotListener.dispose(); - }); - }); - }); - }); - }); - }); - - test('refreshing an empty element that adds children should still keep it collapsed', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - - return model.setInput('root').then(() => { - return model.expand('grandfather').then(() => { - return model.expand('father').then(() => { - assert(!model.isExpanded('father')); - - dataModel.addChild('father', 'son'); - - return model.refresh('father').then(() => { - assert(!model.isExpanded('father')); - }); - }); - }); - }); - }); - - test('refreshing a collapsed element that adds children should still keep it collapsed', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); - - return model.setInput('root').then(() => { - return model.expand('grandfather').then(() => { - return model.expand('father').then(() => { - return model.collapse('father').then(() => { - assert(!model.isExpanded('father')); - - dataModel.addChild('father', 'daughter'); - - return model.refresh('father').then(() => { - assert(!model.isExpanded('father')); - }); - }); - }); - }); - }); - }); - - test('recursively refreshing an ancestor of an expanded element, should keep that element expanded', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); - - return model.setInput('root').then(() => { - return model.expand('grandfather').then(() => { - return model.expand('father').then(() => { - assert(model.isExpanded('grandfather')); - assert(model.isExpanded('father')); - - return model.refresh('grandfather').then(() => { - assert(model.isExpanded('grandfather')); - assert(model.isExpanded('father')); - }); - }); - }); - }); - }); - - test('recursively refreshing an ancestor of a collapsed element, should keep that element collapsed', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); - - return model.setInput('root').then(() => { - return model.expand('grandfather').then(() => { - return model.expand('father').then(() => { - return model.collapse('father').then(() => { - assert(model.isExpanded('grandfather')); - assert(!model.isExpanded('father')); - - return model.refresh('grandfather').then(() => { - assert(model.isExpanded('grandfather')); - assert(!model.isExpanded('father')); - }); - }); - }); - }); - }); - }); - - test('Bug 10855:[explorer] quickly deleting things causes NPE in tree - intersectsLock should always be called when trying to unlock', () => { - dataModel.addChild('root', 'father'); - dataModel.addChild('father', 'son'); - dataModel.addChild('root', 'mother'); - dataModel.addChild('mother', 'daughter'); - - return model.setInput('root').then(() => { - - // delay expansions and refreshes - dataModel.promiseFactory = () => { return timeout(0); }; - - const promises: Promise[] = []; - - promises.push(model.expand('father')); - dataModel.removeChild('root', 'father'); - promises.push(model.refresh('root')); - - promises.push(model.expand('mother')); - dataModel.removeChild('root', 'mother'); - promises.push(model.refresh('root')); - - return Promise.all(promises).then(() => { - assert(true, 'all good'); - }, (errs) => { - assert(false, 'should not fail'); - }); - }); - }); -}); - -suite('TreeModel - bugs', () => { - let counter: EventCounter; - - setup(() => { - counter = new EventCounter(); - }); - - teardown(() => { - counter.dispose(); - }); - - /** - * This bug occurs when an item is expanded right during its removal - */ - test('Bug 10566:[tree] build viewlet is broken after some time', () => { - // setup - let model = new TreeModel({ - dataSource: { - getId: (_, e) => e, - hasChildren: (_, e) => e === 'root' || e === 'bart', - getChildren: (_, e) => { - if (e === 'root') { return getRootChildren(); } - if (e === 'bart') { return getBartChildren(); } - return Promise.resolve([]); - }, - getParent: (_, e): Promise => { throw new Error('not implemented'); }, - } - }); - - let listeners = []; - - // helpers - const getGetRootChildren = (children: string[], millis = 0) => () => timeout(millis).then(() => children); - let getRootChildren = getGetRootChildren(['homer', 'bart', 'lisa', 'marge', 'maggie'], 0); - const getGetBartChildren = (millis = 0) => () => timeout(millis).then(() => ['milhouse', 'nelson']); - const getBartChildren = getGetBartChildren(0); - - // item expanding should not exist! - counter.listen(model.onExpandItem, () => { assert(false, 'should never receive item:expanding event'); }); - counter.listen(model.onDidExpandItem, () => { assert(false, 'should never receive item:expanded event'); }); - - return model.setInput('root').then(() => { - - // remove bart - getRootChildren = getGetRootChildren(['homer', 'lisa', 'marge', 'maggie'], 10); - - // refresh root - const p1 = model.refresh('root', true).then(() => { - assert(true); - }, () => { - assert(false, 'should never reach this'); - }); - - // at the same time, try to expand bart! - const p2 = model.expand('bart').then(() => { - assert(false, 'should never reach this'); - }, () => { - assert(true, 'bart should fail to expand since he was removed meanwhile'); - }); - - // what now? - return Promise.all([p1, p2]); - - }).then(() => { - - // teardown - while (listeners.length > 0) { listeners.pop()(); } - listeners = null; - model.dispose(); - - assert.equal(counter.count, 0); - }); - }); - - test('collapsed resolved parent should also update all children visibility on refresh', async function () { - const counter = new EventCounter(); - const dataModel = new DynamicModel(); - - let isSonVisible = true; - const filter: _.IFilter = { - isVisible(_, element) { - return element !== 'son' || isSonVisible; - } - }; - - const model = new TreeModel({ dataSource: dataModel, filter }); - - dataModel.addChild('root', 'father'); - dataModel.addChild('father', 'son'); - - await model.setInput('root'); - await model.expand('father'); - - let nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'father'); - assert.equal(nav.next()!.id, 'son'); - assert.equal(nav.next(), null); - - await model.collapse('father'); - isSonVisible = false; - - await model.refresh(undefined, true); - await model.expand('father'); - - nav = model.getNavigator(); - assert.equal(nav.next()!.id, 'father'); - assert.equal(nav.next(), null); - - counter.dispose(); - model.dispose(); - }); -}); diff --git a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts deleted file mode 100644 index 97c2846ebb6..00000000000 --- a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts +++ /dev/null @@ -1,252 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { ArrayIterator } from 'vs/base/common/iterator'; -import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; - -function makeItem(id: any, height: any): any { - return { - id: id, - getHeight: function () { return height; }, - isExpanded: function () { return false; }, - getAllTraits: () => [] - }; -} - -function makeItems(...args: any[]) { - let r: any[] = []; - - for (let i = 0; i < args.length; i += 2) { - r.push(makeItem(args[i], args[i + 1])); - } - - return r; -} - -function makeNavigator(...args: any[]): any { - let items = makeItems.apply(null, args); - let i = 0; - - return { - next: function () { - return items[i++] || null; - } - }; -} - -class TestHeightMap extends HeightMap { - - protected createViewItem(item: any): IViewItem { - return { - model: item, - top: 0, - height: item.getHeight(), - width: 0 - }; - } -} - -suite('TreeView - HeightMap', () => { - let rangeMap: HeightMap; - - setup(() => { - rangeMap = new TestHeightMap(); - rangeMap.onInsertItems(makeNavigator('a', 3, 'b', 30, 'c', 25, 'd', 2)); - }); - - teardown(() => { - rangeMap.dispose(); - rangeMap = null!; - }); - - test('simple', () => { - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'b'); - assert.equal(rangeMap.itemAt(32), 'b'); - assert.equal(rangeMap.itemAt(33), 'c'); - assert.equal(rangeMap.itemAt(40), 'c'); - assert.equal(rangeMap.itemAt(57), 'c'); - assert.equal(rangeMap.itemAt(58), 'd'); - assert.equal(rangeMap.itemAt(59), 'd'); - assert.throws(() => rangeMap.itemAt(60)); - }); - - test('onInsertItems at beginning', () => { - let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); - rangeMap.onInsertItems(navigator); - - assert.equal(rangeMap.itemAt(0), 'x'); - assert.equal(rangeMap.itemAt(3), 'x'); - assert.equal(rangeMap.itemAt(4), 'y'); - assert.equal(rangeMap.itemAt(23), 'y'); - assert.equal(rangeMap.itemAt(24), 'z'); - assert.equal(rangeMap.itemAt(31), 'z'); - assert.equal(rangeMap.itemAt(32), 'a'); - assert.equal(rangeMap.itemAt(34), 'a'); - assert.equal(rangeMap.itemAt(35), 'b'); - assert.equal(rangeMap.itemAt(64), 'b'); - assert.equal(rangeMap.itemAt(65), 'c'); - assert.equal(rangeMap.itemAt(89), 'c'); - assert.equal(rangeMap.itemAt(90), 'd'); - assert.equal(rangeMap.itemAt(91), 'd'); - assert.throws(() => rangeMap.itemAt(92)); - }); - - test('onInsertItems in middle', () => { - let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); - rangeMap.onInsertItems(navigator, 'a'); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'x'); - assert.equal(rangeMap.itemAt(6), 'x'); - assert.equal(rangeMap.itemAt(7), 'y'); - assert.equal(rangeMap.itemAt(26), 'y'); - assert.equal(rangeMap.itemAt(27), 'z'); - assert.equal(rangeMap.itemAt(34), 'z'); - assert.equal(rangeMap.itemAt(35), 'b'); - assert.equal(rangeMap.itemAt(64), 'b'); - assert.equal(rangeMap.itemAt(65), 'c'); - assert.equal(rangeMap.itemAt(89), 'c'); - assert.equal(rangeMap.itemAt(90), 'd'); - assert.equal(rangeMap.itemAt(91), 'd'); - assert.throws(() => rangeMap.itemAt(92)); - }); - - test('onInsertItems at end', () => { - let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); - rangeMap.onInsertItems(navigator, 'd'); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'b'); - assert.equal(rangeMap.itemAt(32), 'b'); - assert.equal(rangeMap.itemAt(33), 'c'); - assert.equal(rangeMap.itemAt(57), 'c'); - assert.equal(rangeMap.itemAt(58), 'd'); - assert.equal(rangeMap.itemAt(59), 'd'); - assert.equal(rangeMap.itemAt(60), 'x'); - assert.equal(rangeMap.itemAt(63), 'x'); - assert.equal(rangeMap.itemAt(64), 'y'); - assert.equal(rangeMap.itemAt(83), 'y'); - assert.equal(rangeMap.itemAt(84), 'z'); - assert.equal(rangeMap.itemAt(91), 'z'); - assert.throws(() => rangeMap.itemAt(92)); - }); - - test('onRemoveItems at beginning', () => { - rangeMap.onRemoveItems(new ArrayIterator(['a', 'b'])); - - assert.equal(rangeMap.itemAt(0), 'c'); - assert.equal(rangeMap.itemAt(24), 'c'); - assert.equal(rangeMap.itemAt(25), 'd'); - assert.equal(rangeMap.itemAt(26), 'd'); - assert.throws(() => rangeMap.itemAt(27)); - }); - - test('onRemoveItems in middle', () => { - rangeMap.onRemoveItems(new ArrayIterator(['c'])); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'b'); - assert.equal(rangeMap.itemAt(32), 'b'); - assert.equal(rangeMap.itemAt(33), 'd'); - assert.equal(rangeMap.itemAt(34), 'd'); - assert.throws(() => rangeMap.itemAt(35)); - }); - - test('onRemoveItems at end', () => { - rangeMap.onRemoveItems(new ArrayIterator(['c', 'd'])); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'b'); - assert.equal(rangeMap.itemAt(32), 'b'); - assert.throws(() => rangeMap.itemAt(33)); - }); - - test('onRefreshItems at beginning', () => { - let navigator = makeNavigator('a', 1, 'b', 1); - rangeMap.onRefreshItems(navigator); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(1), 'b'); - assert.equal(rangeMap.itemAt(2), 'c'); - assert.equal(rangeMap.itemAt(26), 'c'); - assert.equal(rangeMap.itemAt(27), 'd'); - assert.equal(rangeMap.itemAt(28), 'd'); - assert.throws(() => rangeMap.itemAt(29)); - }); - - test('onRefreshItems in middle', () => { - let navigator = makeNavigator('b', 40, 'c', 4); - rangeMap.onRefreshItems(navigator); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'b'); - assert.equal(rangeMap.itemAt(42), 'b'); - assert.equal(rangeMap.itemAt(43), 'c'); - assert.equal(rangeMap.itemAt(46), 'c'); - assert.equal(rangeMap.itemAt(47), 'd'); - assert.equal(rangeMap.itemAt(48), 'd'); - assert.throws(() => rangeMap.itemAt(49)); - }); - - test('onRefreshItems at end', () => { - let navigator = makeNavigator('d', 22); - rangeMap.onRefreshItems(navigator); - - assert.equal(rangeMap.itemAt(0), 'a'); - assert.equal(rangeMap.itemAt(2), 'a'); - assert.equal(rangeMap.itemAt(3), 'b'); - assert.equal(rangeMap.itemAt(32), 'b'); - assert.equal(rangeMap.itemAt(33), 'c'); - assert.equal(rangeMap.itemAt(57), 'c'); - assert.equal(rangeMap.itemAt(58), 'd'); - assert.equal(rangeMap.itemAt(79), 'd'); - assert.throws(() => rangeMap.itemAt(80)); - }); - - test('withItemsInRange', () => { - let i = 0; - let itemsInRange = ['a', 'b']; - rangeMap.withItemsInRange(2, 27, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - - i = 0; - itemsInRange = ['a', 'b']; - rangeMap.withItemsInRange(0, 3, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - - i = 0; - itemsInRange = ['a']; - rangeMap.withItemsInRange(0, 2, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - - i = 0; - itemsInRange = ['a']; - rangeMap.withItemsInRange(0, 2, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - - i = 0; - itemsInRange = ['b', 'c']; - rangeMap.withItemsInRange(15, 39, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - - i = 0; - itemsInRange = ['a', 'b', 'c', 'd']; - rangeMap.withItemsInRange(1, 58, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - - i = 0; - itemsInRange = ['c', 'd']; - rangeMap.withItemsInRange(45, 58, function (item) { assert.equal(item, itemsInRange[i++]); }); - assert.equal(i, itemsInRange.length); - }); -}); diff --git a/src/vs/base/test/browser/actionbar.test.ts b/src/vs/base/test/browser/actionbar.test.ts new file mode 100644 index 00000000000..049c4d48bb3 --- /dev/null +++ b/src/vs/base/test/browser/actionbar.test.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ActionBar, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action, Separator } from 'vs/base/common/actions'; + +suite('Actionbar', () => { + + test('prepareActions()', function () { + let a1 = new Separator(); + let a2 = new Separator(); + let a3 = new Action('a3'); + let a4 = new Separator(); + let a5 = new Separator(); + let a6 = new Action('a6'); + let a7 = new Separator(); + + let actions = prepareActions([a1, a2, a3, a4, a5, a6, a7]); + assert.strictEqual(actions.length, 3); // duplicate separators get removed + assert(actions[0] === a3); + assert(actions[1] === a5); + assert(actions[2] === a6); + }); + + test('hasAction()', function () { + const container = document.createElement('div'); + const actionbar = new ActionBar(container); + + let a1 = new Action('a1'); + let a2 = new Action('a2'); + + actionbar.push(a1); + assert.equal(actionbar.hasAction(a1), true); + assert.equal(actionbar.hasAction(a2), false); + + actionbar.pull(0); + assert.equal(actionbar.hasAction(a1), false); + + actionbar.push(a1, { index: 1 }); + actionbar.push(a2, { index: 0 }); + assert.equal(actionbar.hasAction(a1), true); + assert.equal(actionbar.hasAction(a2), true); + + actionbar.pull(0); + assert.equal(actionbar.hasAction(a1), true); + assert.equal(actionbar.hasAction(a2), false); + + actionbar.pull(0); + assert.equal(actionbar.hasAction(a1), false); + assert.equal(actionbar.hasAction(a2), false); + + actionbar.push(a1); + assert.equal(actionbar.hasAction(a1), true); + actionbar.clear(); + assert.equal(actionbar.hasAction(a1), false); + }); +}); diff --git a/src/vs/base/test/browser/codicons.test.ts b/src/vs/base/test/browser/codicons.test.ts new file mode 100644 index 00000000000..fd5b0332a51 --- /dev/null +++ b/src/vs/base/test/browser/codicons.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { renderCodicons } from 'vs/base/browser/codicons'; +import * as assert from 'assert'; + +suite('renderCodicons', () => { + + test('no codicons', () => { + const result = renderCodicons(' hello World .'); + + assert.equal(elementsToString(result), ' hello World .'); + }); + + test('codicon only', () => { + const result = renderCodicons('$(alert)'); + + assert.equal(elementsToString(result), ''); + }); + + test('codicon and non-codicon strings', () => { + const result = renderCodicons(` $(alert) Unresponsive`); + + assert.equal(elementsToString(result), ' Unresponsive'); + }); + + test('multiple codicons', () => { + const result = renderCodicons('$(check)$(error)'); + + assert.equal(elementsToString(result), ''); + }); + + test('escaped codicon', () => { + const result = renderCodicons('\\$(escaped)'); + + assert.equal(elementsToString(result), '$(escaped)'); + }); + + test('codicon with animation', () => { + const result = renderCodicons('$(zip~anim)'); + + assert.equal(elementsToString(result), ''); + }); + + const elementsToString = (elements: Array): string => { + return elements + .map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem) + .reduce((a, b) => a + b, ''); + }; +}); diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 369f160803b..4c40ea2a065 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -3,48 +3,283 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareFileNames, compareFileExtensions } from 'vs/base/common/comparers'; +import { compareFileNames, compareFileExtensions, compareFileNamesDefault, compareFileExtensionsDefault } from 'vs/base/common/comparers'; import * as assert from 'assert'; +const compareLocale = (a: string, b: string) => a.localeCompare(b); +const compareLocaleNumeric = (a: string, b: string) => a.localeCompare(b, undefined, { numeric: true }); + + suite('Comparers', () => { test('compareFileNames', () => { + // + // Comparisons with the same results as compareFileNamesDefault + // + + // name-only comparisons assert(compareFileNames(null, null) === 0, 'null should be equal'); assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values'); assert(compareFileNames('', '') === 0, 'empty should be equal'); assert(compareFileNames('abc', 'abc') === 0, 'equal names should be equal'); - assert(compareFileNames('.abc', '.abc') === 0, 'equal full names should be equal'); - assert(compareFileNames('.env', '.env.example') < 0, 'filenames with extensions should come after those without'); - assert(compareFileNames('.env.example', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly'); + assert(compareFileNames('z', 'A') > 0, 'z comes is after A regardless of case'); + assert(compareFileNames('Z', 'a') > 0, 'Z comes after a regardless of case'); + + // name plus extension comparisons + assert(compareFileNames('bbb.aaa', 'aaa.bbb') > 0, 'files with extensions are compared first by filename'); + assert(compareFileNames('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole name all at once by locale'); + + // dotfile comparisons + assert(compareFileNames('.abc', '.abc') === 0, 'equal dotfile names should be equal'); + assert(compareFileNames('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly'); + assert(compareFileNames('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); + assert(compareFileNames('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); + assert(compareFileNames('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot'); + + // dotfile vs non-dotfile comparisons + assert(compareFileNames(null, '.abc') < 0, 'null should come before dotfiles'); + assert(compareFileNames('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); + assert(compareFileNames('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); + assert(compareFileNames('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); + assert(compareFileNames('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); + + // numeric comparisons assert(compareFileNames('1', '1') === 0, 'numerically equal full names should be equal'); assert(compareFileNames('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); assert(compareFileNames('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); assert(compareFileNames('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long'); + assert(compareFileNames('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); + assert(compareFileNames('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); + + // + // Comparisons with different results than compareFileNamesDefault + // + + // name-only comparisons + assert(compareFileNames('a', 'A') !== compareLocale('a', 'A'), 'the same letter does not sort by locale'); + assert(compareFileNames('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter does not sort by locale'); + assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNames), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order'); + assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNames), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents do not sort in locale order'); + + // numeric comparisons + assert(compareFileNames('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order'); + assert(compareFileNames('abc.txt1', 'abc.txt01') > 0, 'same name plus extensions with equal numbers sort in unicode order'); + assert(compareFileNames('art01', 'Art01') !== 'art01'.localeCompare('Art01', undefined, { numeric: true }), + 'a numerically equivalent word of a different case does not compare numerically based on locale'); + }); test('compareFileExtensions', () => { + // + // Comparisons with the same results as compareFileExtensionsDefault + // + + // name-only comparisons assert(compareFileExtensions(null, null) === 0, 'null should be equal'); - assert(compareFileExtensions(null, '.abc') < 0, 'null should come before real files'); assert(compareFileExtensions(null, 'abc') < 0, 'null should come before real files without extension'); assert(compareFileExtensions('', '') === 0, 'empty should be equal'); assert(compareFileExtensions('abc', 'abc') === 0, 'equal names should be equal'); - assert(compareFileExtensions('.abc', '.abc') === 0, 'equal full names should be equal'); + assert(compareFileExtensions('z', 'A') > 0, 'z comes after A'); + assert(compareFileExtensions('Z', 'a') > 0, 'Z comes after a'); + + // name plus extension comparisons assert(compareFileExtensions('file.ext', 'file.ext') === 0, 'equal full names should be equal'); assert(compareFileExtensions('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); - assert(compareFileExtensions('.ext', 'a.ext') < 0, 'if equal extensions, filenames should be compared, empty filename should come before others'); - assert(compareFileExtensions('file.aaa', 'file.bbb') < 0, 'files should be compared by extensions'); + assert(compareFileExtensions('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); assert(compareFileExtensions('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extensions even if filenames compare differently'); + assert(compareFileExtensions('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names'); + assert(compareFileExtensions('agg.go', 'agg_repo.go') < 0, 'shorter names short before longer names even when the longer name contains an underscore'); + assert(compareFileExtensions('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name'); + + // dotfile comparisons + assert(compareFileExtensions('.abc', '.abc') === 0, 'equal dotfiles should be equal'); + assert(compareFileExtensions('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case'); + + // dotfile vs non-dotfile comparisons + assert(compareFileExtensions(null, '.abc') < 0, 'null should come before dotfiles'); + assert(compareFileExtensions('.env', 'aaa.env') < 0, 'if equal extensions, filenames should be compared, empty filename should come before others'); + assert(compareFileExtensions('.MD', 'a.md') < 0, 'if extensions differ in case, files sort by extension in unicode order'); + + // numeric comparisons assert(compareFileExtensions('1', '1') === 0, 'numerically equal full names should be equal'); assert(compareFileExtensions('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); assert(compareFileExtensions('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); assert(compareFileExtensions('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long'); + assert(compareFileExtensions('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); + assert(compareFileExtensions('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); + assert(compareFileExtensions('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); assert(compareFileExtensions('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal'); assert(compareFileExtensions('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); assert(compareFileExtensions('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long'); assert(compareFileExtensions('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared'); - assert(compareFileExtensions('file2.ext2', 'file1.ext10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); - assert(compareFileExtensions('file.ext01', 'file.ext1') < 0, 'extensions with equal numbers should be in alphabetical order'); + assert(compareFileExtensions('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically'); + + // + // Comparisons with different results from compareFileExtensionsDefault + // + + // name-only comparisions + assert(compareFileExtensions('a', 'A') !== compareLocale('a', 'A'), 'the same letter of different case does not sort by locale'); + assert(compareFileExtensions('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter of different case does not sort by locale'); + assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensions), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order'); + assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensions), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents do not sort in locale order'); + + // name plus extension comparisons + assert(compareFileExtensions('a.MD', 'a.md') !== compareLocale('MD', 'md'), 'case differences in extensions do not sort by locale'); + assert(compareFileExtensions('a.md', 'A.md') !== compareLocale('a', 'A'), 'case differences in names do not sort by locale'); + assert(compareFileExtensions('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order'); + + // dotfile comparisons + assert(compareFileExtensions('.env', '.aaa.env') < 0, 'a dotfile with an extension is treated as a name plus an extension - equal extensions'); + assert(compareFileExtensions('.env', '.env.aaa') > 0, 'a dotfile with an extension is treated as a name plus an extension - unequal extensions'); + + // dotfile vs non-dotfile comparisons + assert(compareFileExtensions('.env', 'aaa') > 0, 'filenames without extensions come before dotfiles'); + assert(compareFileExtensions('.md', 'A.MD') > 0, 'a file with an uppercase extension sorts before a dotfile of the same lowercase extension'); + + // numeric comparisons + assert(compareFileExtensions('abc.txt01', 'abc.txt1') < 0, 'extensions with equal numbers sort in unicode order'); + assert(compareFileExtensions('art01', 'Art01') !== compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case does not compare by locale'); + assert(compareFileExtensions('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order'); + assert(compareFileExtensions('txt.abc01', 'txt.abc1') < 0, 'extensions with equivalent numbers sort in unicode order'); + + }); + + test('compareFileNamesDefault', () => { + + // + // Comparisons with the same results as compareFileNames + // + + // name-only comparisons + assert(compareFileNamesDefault(null, null) === 0, 'null should be equal'); + assert(compareFileNamesDefault(null, 'abc') < 0, 'null should be come before real values'); + assert(compareFileNamesDefault('', '') === 0, 'empty should be equal'); + assert(compareFileNamesDefault('abc', 'abc') === 0, 'equal names should be equal'); + assert(compareFileNamesDefault('z', 'A') > 0, 'z comes is after A regardless of case'); + assert(compareFileNamesDefault('Z', 'a') > 0, 'Z comes after a regardless of case'); + + // name plus extension comparisons + assert(compareFileNamesDefault('file.ext', 'file.ext') === 0, 'equal full names should be equal'); + assert(compareFileNamesDefault('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); + assert(compareFileNamesDefault('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); + assert(compareFileNamesDefault('bbb.aaa', 'aaa.bbb') > 0, 'files should be compared by names even if extensions compare differently'); + assert(compareFileNamesDefault('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole filename in locale order'); + + // dotfile comparisons + assert(compareFileNamesDefault('.abc', '.abc') === 0, 'equal dotfile names should be equal'); + assert(compareFileNamesDefault('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly'); + assert(compareFileNamesDefault('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); + assert(compareFileNamesDefault('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); + assert(compareFileNamesDefault('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot'); + + // dotfile vs non-dotfile comparisons + assert(compareFileNamesDefault(null, '.abc') < 0, 'null should come before dotfiles'); + assert(compareFileNamesDefault('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); + assert(compareFileNamesDefault('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); + assert(compareFileNamesDefault('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); + assert(compareFileNamesDefault('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); + + // numeric comparisons + assert(compareFileNamesDefault('1', '1') === 0, 'numerically equal full names should be equal'); + assert(compareFileNamesDefault('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); + assert(compareFileNamesDefault('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); + assert(compareFileNamesDefault('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long'); + assert(compareFileNamesDefault('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); + assert(compareFileNamesDefault('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); + + // + // Comparisons with different results than compareFileNames + // + + // name-only comparisons + assert(compareFileNamesDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale'); + assert(compareFileNamesDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter sorts by locale'); + assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNamesDefault), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order'); + + // numeric comparisons + assert(compareFileNamesDefault('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest number first'); + assert(compareFileNamesDefault('abc.txt1', 'abc.txt01') < 0, 'same name plus extensions with equal numbers sort shortest number first'); + assert(compareFileNamesDefault('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale'); + + }); + + test('compareFileExtensionsDefault', () => { + + // + // Comparisons with the same result as compareFileExtensions + // + + // name-only comparisons + assert(compareFileExtensionsDefault(null, null) === 0, 'null should be equal'); + assert(compareFileExtensionsDefault(null, 'abc') < 0, 'null should come before real files without extensions'); + assert(compareFileExtensionsDefault('', '') === 0, 'empty should be equal'); + assert(compareFileExtensionsDefault('abc', 'abc') === 0, 'equal names should be equal'); + assert(compareFileExtensionsDefault('z', 'A') > 0, 'z comes after A'); + assert(compareFileExtensionsDefault('Z', 'a') > 0, 'Z comes after a'); + + // name plus extension comparisons + assert(compareFileExtensionsDefault('file.ext', 'file.ext') === 0, 'equal full filenames should be equal'); + assert(compareFileExtensionsDefault('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared'); + assert(compareFileExtensionsDefault('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions'); + assert(compareFileExtensionsDefault('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extension first'); + assert(compareFileExtensionsDefault('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names'); + assert(compareFileExtensionsDefault('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name'); + + // dotfile comparisons + assert(compareFileExtensionsDefault('.abc', '.abc') === 0, 'equal dotfiles should be equal'); + assert(compareFileExtensionsDefault('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case'); + + // dotfile vs non-dotfile comparisons + assert(compareFileExtensionsDefault(null, '.abc') < 0, 'null should come before dotfiles'); + assert(compareFileExtensionsDefault('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions'); + assert(compareFileExtensionsDefault('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files'); + + // numeric comparisons + assert(compareFileExtensionsDefault('1', '1') === 0, 'numerically equal full names should be equal'); + assert(compareFileExtensionsDefault('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal'); + assert(compareFileExtensionsDefault('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order'); + assert(compareFileExtensionsDefault('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order'); + assert(compareFileExtensionsDefault('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically'); + assert(compareFileExtensionsDefault('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number'); + assert(compareFileExtensionsDefault('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); + assert(compareFileExtensionsDefault('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal'); + assert(compareFileExtensionsDefault('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); + assert(compareFileExtensionsDefault('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long'); + assert(compareFileExtensionsDefault('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared'); + assert(compareFileExtensionsDefault('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically'); + + // + // Comparisons with different results than compareFileExtensions + // + + // name-only comparisons + assert(compareFileExtensionsDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale'); + assert(compareFileExtensionsDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter of different case sorts by locale'); + assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensionsDefault), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order'); + + // name plus extension comparisons + assert(compareFileExtensionsDefault('a.MD', 'a.md') === compareLocale('MD', 'md'), 'case differences in extensions sort by locale'); + assert(compareFileExtensionsDefault('a.md', 'A.md') === compareLocale('a', 'A'), 'case differences in names sort by locale'); + assert(compareFileExtensionsDefault('aggregate.go', 'aggregate_repo.go') > 0, 'names with the same extension sort in full filename locale order'); + + // dotfile comparisons + assert(compareFileExtensionsDefault('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots'); + assert(compareFileExtensionsDefault('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first'); + + // dotfile vs non-dotfile comparisons + assert(compareFileExtensionsDefault('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions'); + assert(compareFileExtensionsDefault('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files'); + + // numeric comparisons + assert(compareFileExtensionsDefault('abc.txt01', 'abc.txt1') > 0, 'extensions with equal numbers should be in shortest-first order'); + assert(compareFileExtensionsDefault('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale'); + assert(compareFileExtensionsDefault('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest string first'); + assert(compareFileExtensionsDefault('txt.abc01', 'txt.abc1') > 0, 'extensions with equivalent numbers sort shortest extension first'); + }); }); diff --git a/src/vs/base/test/browser/dom.test.ts b/src/vs/base/test/browser/dom.test.ts index 61252159d23..99b4f1b90fc 100644 --- a/src/vs/base/test/browser/dom.test.ts +++ b/src/vs/base/test/browser/dom.test.ts @@ -13,12 +13,12 @@ suite('dom', () => { let element = document.createElement('div'); element.className = 'foobar boo far'; - assert(dom.hasClass(element, 'foobar')); - assert(dom.hasClass(element, 'boo')); - assert(dom.hasClass(element, 'far')); - assert(!dom.hasClass(element, 'bar')); - assert(!dom.hasClass(element, 'foo')); - assert(!dom.hasClass(element, '')); + assert(element.classList.contains('foobar')); + assert(element.classList.contains('boo')); + assert(element.classList.contains('far')); + assert(!element.classList.contains('bar')); + assert(!element.classList.contains('foo')); + assert(!element.classList.contains('')); }); test('removeClass', () => { @@ -26,63 +26,56 @@ suite('dom', () => { let element = document.createElement('div'); element.className = 'foobar boo far'; - dom.removeClass(element, 'boo'); - assert(dom.hasClass(element, 'far')); - assert(!dom.hasClass(element, 'boo')); - assert(dom.hasClass(element, 'foobar')); + element.classList.remove('boo'); + assert(element.classList.contains('far')); + assert(!element.classList.contains('boo')); + assert(element.classList.contains('foobar')); assert.equal(element.className, 'foobar far'); element = document.createElement('div'); element.className = 'foobar boo far'; - dom.removeClass(element, 'far'); - assert(!dom.hasClass(element, 'far')); - assert(dom.hasClass(element, 'boo')); - assert(dom.hasClass(element, 'foobar')); + element.classList.remove('far'); + assert(!element.classList.contains('far')); + assert(element.classList.contains('boo')); + assert(element.classList.contains('foobar')); assert.equal(element.className, 'foobar boo'); - dom.removeClass(element, 'boo'); - assert(!dom.hasClass(element, 'far')); - assert(!dom.hasClass(element, 'boo')); - assert(dom.hasClass(element, 'foobar')); + element.classList.remove('boo'); + assert(!element.classList.contains('far')); + assert(!element.classList.contains('boo')); + assert(element.classList.contains('foobar')); assert.equal(element.className, 'foobar'); - dom.removeClass(element, 'foobar'); - assert(!dom.hasClass(element, 'far')); - assert(!dom.hasClass(element, 'boo')); - assert(!dom.hasClass(element, 'foobar')); + element.classList.remove('foobar'); + assert(!element.classList.contains('far')); + assert(!element.classList.contains('boo')); + assert(!element.classList.contains('foobar')); assert.equal(element.className, ''); }); test('removeClass should consider hyphens', function () { let element = document.createElement('div'); - dom.addClass(element, 'foo-bar'); - dom.addClass(element, 'bar'); + element.classList.add('foo-bar'); + element.classList.add('bar'); - assert(dom.hasClass(element, 'foo-bar')); - assert(dom.hasClass(element, 'bar')); + assert(element.classList.contains('foo-bar')); + assert(element.classList.contains('bar')); - dom.removeClass(element, 'bar'); - assert(dom.hasClass(element, 'foo-bar')); - assert(!dom.hasClass(element, 'bar')); + element.classList.remove('bar'); + assert(element.classList.contains('foo-bar')); + assert(!element.classList.contains('bar')); - dom.removeClass(element, 'foo-bar'); - assert(!dom.hasClass(element, 'foo-bar')); - assert(!dom.hasClass(element, 'bar')); + element.classList.remove('foo-bar'); + assert(!element.classList.contains('foo-bar')); + assert(!element.classList.contains('bar')); }); - //test('[perf] hasClass * 100000', () => { - // - // for (let i = 0; i < 100000; i++) { - // let element = document.createElement('div'); - // element.className = 'foobar boo far'; - // - // assert(dom.hasClass(element, 'far')); - // assert(dom.hasClass(element, 'boo')); - // assert(dom.hasClass(element, 'foobar')); - // } - //}); + test('multibyteAwareBtoa', () => { + assert.equal(dom.multibyteAwareBtoa('hello world'), dom.multibyteAwareBtoa('hello world')); + assert.ok(dom.multibyteAwareBtoa('平仮名')); + }); suite('$', () => { test('should build simple nodes', () => { @@ -93,6 +86,22 @@ suite('dom', () => { assert(!div.firstChild); }); + test('should buld nodes with id', () => { + const div = $('div#foo'); + assert(div); + assert(div instanceof HTMLElement); + assert.equal(div.tagName, 'DIV'); + assert.equal(div.id, 'foo'); + }); + + test('should buld nodes with class-name', () => { + const div = $('div.foo'); + assert(div); + assert(div instanceof HTMLElement); + assert.equal(div.tagName, 'DIV'); + assert.equal(div.className, 'foo'); + }); + test('should build nodes with attributes', () => { let div = $('div', { class: 'test' }); assert.equal(div.className, 'test'); @@ -111,5 +120,12 @@ suite('dom', () => { assert.equal(div.firstChild && div.firstChild.textContent, 'hello'); }); + + test('should build nodes with text children', () => { + let div = $('div', undefined, 'foobar'); + let firstChild = div.firstChild as HTMLElement; + assert.equal(firstChild.tagName, undefined); + assert.equal(firstChild.textContent, 'foobar'); + }); }); }); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 499565a641c..78f82030b2c 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -6,7 +6,9 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; -import { MarkdownString } from 'vs/base/common/htmlContent'; +import { MarkdownString, IMarkdownString } from 'vs/base/common/htmlContent'; +import { URI } from 'vs/base/common/uri'; +import { parse } from 'vs/base/common/marshalling'; suite('MarkdownRenderer', () => { suite('Images', () => { @@ -16,7 +18,6 @@ suite('MarkdownRenderer', () => { const result: HTMLElement = renderMarkdown(markdown); const renderer = new marked.Renderer(); const imageFromMarked = marked(markdown.value, { - sanitize: true, renderer }).trim(); assert.strictEqual(result.innerHTML, imageFromMarked); @@ -27,7 +28,6 @@ suite('MarkdownRenderer', () => { const result: HTMLElement = renderMarkdown(markdown); const renderer = new marked.Renderer(); const imageFromMarked = marked(markdown.value, { - sanitize: true, renderer }).trim(); assert.strictEqual(result.innerHTML, imageFromMarked); @@ -98,4 +98,21 @@ suite('MarkdownRenderer', () => { }); + test('npm Hover Run Script not working #90855', function () { + + const md: IMarkdownString = JSON.parse('{"value":"[Run Script](command:npm.runScriptFromHover?%7B%22documentUri%22%3A%7B%22%24mid%22%3A1%2C%22fsPath%22%3A%22c%3A%5C%5CUsers%5C%5Cjrieken%5C%5CCode%5C%5C_sample%5C%5Cfoo%5C%5Cpackage.json%22%2C%22_sep%22%3A1%2C%22external%22%3A%22file%3A%2F%2F%2Fc%253A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22path%22%3A%22%2Fc%3A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22scheme%22%3A%22file%22%7D%2C%22script%22%3A%22echo%22%7D \\"Run the script as a task\\")","supportThemeIcons":false,"isTrusted":true,"uris":{"__uri_e49443":{"$mid":1,"fsPath":"c:\\\\Users\\\\jrieken\\\\Code\\\\_sample\\\\foo\\\\package.json","_sep":1,"external":"file:///c%3A/Users/jrieken/Code/_sample/foo/package.json","path":"/c:/Users/jrieken/Code/_sample/foo/package.json","scheme":"file"},"command:npm.runScriptFromHover?%7B%22documentUri%22%3A%7B%22%24mid%22%3A1%2C%22fsPath%22%3A%22c%3A%5C%5CUsers%5C%5Cjrieken%5C%5CCode%5C%5C_sample%5C%5Cfoo%5C%5Cpackage.json%22%2C%22_sep%22%3A1%2C%22external%22%3A%22file%3A%2F%2F%2Fc%253A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22path%22%3A%22%2Fc%3A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22scheme%22%3A%22file%22%7D%2C%22script%22%3A%22echo%22%7D":{"$mid":1,"path":"npm.runScriptFromHover","scheme":"command","query":"{\\"documentUri\\":\\"__uri_e49443\\",\\"script\\":\\"echo\\"}"}}}'); + const element = renderMarkdown(md); + + const anchor = element.querySelector('a')!; + assert.ok(anchor); + assert.ok(anchor.dataset['href']); + + const uri = URI.parse(anchor.dataset['href']!); + + const data = <{ script: string, documentUri: URI }>parse(decodeURIComponent(uri.query)); + assert.ok(data); + assert.equal(data.script, 'echo'); + assert.ok(data.documentUri.toString().startsWith('file:///c%3A/')); + }); + }); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 2620db0b7d8..c36a51fd213 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -91,7 +91,7 @@ suite('Splitview', () => { splitview.addView(view2, 20); splitview.addView(view3, 20); - let viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + let viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 3, 'split view should have 3 views'); let sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -99,7 +99,7 @@ suite('Splitview', () => { splitview.removeView(2); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 2, 'split view should have 2 views'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -107,7 +107,7 @@ suite('Splitview', () => { splitview.removeView(0); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 1, 'split view should have 1 view'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -115,7 +115,7 @@ suite('Splitview', () => { splitview.removeView(0); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 0, 'split view should have no views'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 95411fbbc71..8d66a9b3607 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { ITreeNode, ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { hasClass } from 'vs/base/browser/dom'; import { timeout } from 'vs/base/common/async'; interface Element { @@ -103,8 +102,8 @@ suite('AsyncDataTree', function () { await tree.setInput(model.root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(!hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(!twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); model.get('a').children = [ { id: 'aa' }, @@ -151,8 +150,8 @@ suite('AsyncDataTree', function () { assert.deepStrictEqual(getChildrenCalls, ['root']); let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(!hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(!twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); assert(tree.getNode().children[0].collapsed); model.get('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]; @@ -160,8 +159,8 @@ suite('AsyncDataTree', function () { assert.deepStrictEqual(getChildrenCalls, ['root', 'root']); twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(hasClass(twistie, 'collapsible')); - assert(hasClass(twistie, 'collapsed')); + assert(twistie.classList.contains('collapsible')); + assert(twistie.classList.contains('collapsed')); assert(tree.getNode().children[0].collapsed); model.get('a').children = []; @@ -169,8 +168,8 @@ suite('AsyncDataTree', function () { assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root']); twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(!hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(!twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); assert(tree.getNode().children[0].collapsed); model.get('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]; @@ -178,8 +177,8 @@ suite('AsyncDataTree', function () { assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root', 'root']); twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(hasClass(twistie, 'collapsible')); - assert(hasClass(twistie, 'collapsed')); + assert(twistie.classList.contains('collapsible')); + assert(twistie.classList.contains('collapsed')); assert(tree.getNode().children[0].collapsed); }); @@ -241,8 +240,8 @@ suite('AsyncDataTree', function () { await tree.expand(model.get('a')); let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); assert(!tree.getNode(model.get('a')).collapsed); tree.collapse(model.get('a')); @@ -250,8 +249,8 @@ suite('AsyncDataTree', function () { await tree.updateChildren(model.root); twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(!hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(!twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); assert(tree.getNode(model.get('a')).collapsed); }); @@ -295,7 +294,7 @@ suite('AsyncDataTree', function () { return !!element.children && element.children.length > 0; } getChildren(element: Element): Promise { - return new Promise(c => calls.push(() => c(element.children))); + return new Promise(c => calls.push(() => c(element.children || []))); } }; @@ -338,7 +337,7 @@ suite('AsyncDataTree', function () { return !!element.children && element.children.length > 0; } getChildren(element: Element): Promise { - return new Promise(c => calls.push(() => c(element.children))); + return new Promise(c => calls.push(() => c(element.children || []))); } }; @@ -387,22 +386,22 @@ suite('AsyncDataTree', function () { assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(!hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(!twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); model.get('a').children = [{ id: 'aa' }]; await tree.updateChildren(model.get('a'), false); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(hasClass(twistie, 'collapsible')); - assert(hasClass(twistie, 'collapsed')); + assert(twistie.classList.contains('collapsible')); + assert(twistie.classList.contains('collapsed')); model.get('a').children = []; await tree.updateChildren(model.get('a'), false); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; - assert(!hasClass(twistie, 'collapsible')); - assert(!hasClass(twistie, 'collapsed')); + assert(!twistie.classList.contains('collapsible')); + assert(!twistie.classList.contains('collapsed')); }); test('issues #84569, #82629 - rerender', async () => { diff --git a/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts index b69e61abcb8..18eb84d5183 100644 --- a/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts @@ -5,9 +5,9 @@ import * as assert from 'assert'; import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import { ISpliceable } from 'vs/base/common/sequence'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; interface IResolvedCompressedTreeElement extends ICompressedTreeElement { readonly element: T; @@ -16,7 +16,7 @@ interface IResolvedCompressedTreeElement extends ICompressedTreeElement { function resolve(treeElement: ICompressedTreeElement): IResolvedCompressedTreeElement { const result: any = { element: treeElement.element }; - const children = Iterator.collect(Iterator.map(Iterator.from(treeElement.children), resolve)); + const children = [...Iterable.map(Iterable.from(treeElement.children), resolve)]; if (treeElement.incompressible) { result.incompressible = true; @@ -289,11 +289,12 @@ suite('CompressedObjectTree', function () { }); }); - function toSpliceable(arr: T[]): ISpliceable { + function toList(arr: T[]): IList { return { splice(start: number, deleteCount: number, elements: T[]): void { arr.splice(start, deleteCount, ...elements); - } + }, + updateElementHeight() { } }; } @@ -305,7 +306,7 @@ suite('CompressedObjectTree', function () { test('ctor', () => { const list: ITreeNode>[] = []; - const model = new CompressedObjectTreeModel('test', toSpliceable(list)); + const model = new CompressedObjectTreeModel('test', toList(list)); assert(model); assert.equal(list.length, 0); assert.equal(model.size, 0); @@ -313,115 +314,115 @@ suite('CompressedObjectTree', function () { test('flat', () => { const list: ITreeNode>[] = []; - const model = new CompressedObjectTreeModel('test', toSpliceable(list)); + const model = new CompressedObjectTreeModel('test', toList(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [[0], [1], [2]]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 3 }, { element: 4 }, { element: 5 }, - ])); + ]); assert.deepEqual(toArray(list), [[3], [4], [5]]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); test('nested', () => { const list: ITreeNode>[] = []; - const model = new CompressedObjectTreeModel('test', toSpliceable(list)); + const model = new CompressedObjectTreeModel('test', toList(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [[0], [10], [11], [12], [1], [2]]); assert.equal(model.size, 6); - model.setChildren(12, Iterator.fromArray([ + model.setChildren(12, [ { element: 120 }, { element: 121 } - ])); + ]); assert.deepEqual(toArray(list), [[0], [10], [11], [12], [120], [121], [1], [2]]); assert.equal(model.size, 8); - model.setChildren(0, Iterator.empty()); + model.setChildren(0); assert.deepEqual(toArray(list), [[0], [1], [2]]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); test('compressed', () => { const list: ITreeNode>[] = []; - const model = new CompressedObjectTreeModel('test', toSpliceable(list)); + const model = new CompressedObjectTreeModel('test', toList(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { - element: 1, children: Iterator.fromArray([{ - element: 11, children: Iterator.fromArray([{ - element: 111, children: Iterator.fromArray([ + element: 1, children: [{ + element: 11, children: [{ + element: 111, children: [ { element: 1111 }, { element: 1112 }, { element: 1113 }, - ]) - }]) - }]) + ] + }] + }] } - ])); + ]); assert.deepEqual(toArray(list), [[1, 11, 111], [1111], [1112], [1113]]); assert.equal(model.size, 6); - model.setChildren(11, Iterator.fromArray([ + model.setChildren(11, [ { element: 111 }, { element: 112 }, { element: 113 }, - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113]]); assert.equal(model.size, 5); - model.setChildren(113, Iterator.fromArray([ + model.setChildren(113, [ { element: 1131 } - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131]]); assert.equal(model.size, 6); - model.setChildren(1131, Iterator.fromArray([ + model.setChildren(1131, [ { element: 1132 } - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131, 1132]]); assert.equal(model.size, 7); - model.setChildren(1131, Iterator.fromArray([ + model.setChildren(1131, [ { element: 1132 }, { element: 1133 }, - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131], [1132], [1133]]); assert.equal(model.size, 8); diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index d1914e5584f..6c9dfeae8ea 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -5,15 +5,14 @@ import * as assert from 'assert'; import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; -import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator } from 'vs/base/common/iterator'; -import { IndexTreeModel, IIndexTreeNode } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { IndexTreeModel, IIndexTreeNode, IList } from 'vs/base/browser/ui/tree/indexTreeModel'; -function toSpliceable(arr: T[]): ISpliceable { +function toList(arr: T[]): IList { return { splice(start: number, deleteCount: number, elements: T[]): void { arr.splice(start, deleteCount, ...elements); - } + }, + updateElementHeight() { } }; } @@ -25,20 +24,20 @@ suite('IndexTreeModel', function () { test('ctor', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); assert(model); assert.equal(list.length, 0); }); test('insert', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); assert.deepEqual(list[0].element, 0); @@ -54,19 +53,19 @@ suite('IndexTreeModel', function () { test('deep insert', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); assert.deepEqual(list[0].element, 0); @@ -91,19 +90,19 @@ suite('IndexTreeModel', function () { test('deep insert collapsed', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); assert.deepEqual(list[0].element, 0); @@ -119,13 +118,13 @@ suite('IndexTreeModel', function () { test('delete', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); @@ -144,19 +143,19 @@ suite('IndexTreeModel', function () { test('nested delete', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); @@ -178,19 +177,19 @@ suite('IndexTreeModel', function () { test('deep delete', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); @@ -206,19 +205,19 @@ suite('IndexTreeModel', function () { test('hidden delete', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); @@ -231,19 +230,19 @@ suite('IndexTreeModel', function () { test('collapse', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); @@ -262,19 +261,19 @@ suite('IndexTreeModel', function () { test('expand', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); @@ -302,9 +301,9 @@ suite('IndexTreeModel', function () { test('collapse should recursively adjust visible count', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 1, children: [ { @@ -319,7 +318,7 @@ suite('IndexTreeModel', function () { { element: 21 } ] } - ])); + ]); assert.deepEqual(list.length, 5); assert.deepEqual(toArray(list), [1, 11, 111, 2, 21]); @@ -335,15 +334,15 @@ suite('IndexTreeModel', function () { test('setCollapsible', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 } - ]) + ] } - ])); + ]); assert.deepEqual(list.length, 2); @@ -356,13 +355,15 @@ suite('IndexTreeModel', function () { assert.deepEqual(list[1].collapsible, false); assert.deepEqual(list[1].collapsed, false); - model.setCollapsed([0], true); - assert.deepEqual(list.length, 1); + assert.deepEqual(model.setCollapsed([0], true), false); assert.deepEqual(list[0].element, 0); assert.deepEqual(list[0].collapsible, false); - assert.deepEqual(list[0].collapsed, true); + assert.deepEqual(list[0].collapsed, false); + assert.deepEqual(list[1].element, 10); + assert.deepEqual(list[1].collapsible, false); + assert.deepEqual(list[1].collapsed, false); - model.setCollapsed([0], false); + assert.deepEqual(model.setCollapsed([0], false), false); assert.deepEqual(list[0].element, 0); assert.deepEqual(list[0].collapsible, false); assert.deepEqual(list[0].collapsed, false); @@ -379,13 +380,13 @@ suite('IndexTreeModel', function () { assert.deepEqual(list[1].collapsible, false); assert.deepEqual(list[1].collapsed, false); - model.setCollapsed([0], true); + assert.deepEqual(model.setCollapsed([0], true), true); assert.deepEqual(list.length, 1); assert.deepEqual(list[0].element, 0); assert.deepEqual(list[0].collapsible, true); assert.deepEqual(list[0].collapsed, true); - model.setCollapsed([0], false); + assert.deepEqual(model.setCollapsed([0], false), true); assert.deepEqual(list[0].element, 0); assert.deepEqual(list[0].collapsible, true); assert.deepEqual(list[0].collapsed, false); @@ -402,9 +403,9 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); + const model = new IndexTreeModel('test', toList(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, @@ -416,7 +417,7 @@ suite('IndexTreeModel', function () { { element: 7 } ] } - ])); + ]); assert.deepEqual(list.length, 4); assert.deepEqual(toArray(list), [0, 2, 4, 6]); @@ -436,16 +437,16 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); + const model = new IndexTreeModel('test', toList(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, { element: 2 } ] } - ])); + ]); assert.deepEqual(toArray(list), []); }); @@ -459,9 +460,9 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); + const model = new IndexTreeModel('test', toList(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, @@ -473,7 +474,7 @@ suite('IndexTreeModel', function () { { element: 7 } ] }, - ])); + ]); assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]); @@ -498,9 +499,9 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); + const model = new IndexTreeModel('test', toList(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'vscode', children: [ { element: '.build' }, @@ -520,7 +521,7 @@ suite('IndexTreeModel', function () { } ] }, - ])); + ]); assert.deepEqual(list.length, 10); @@ -544,9 +545,9 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); + const model = new IndexTreeModel('test', toList(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'vscode', children: [ { element: '.build' }, @@ -566,7 +567,7 @@ suite('IndexTreeModel', function () { } ] }, - ])); + ]); assert.deepEqual(list.length, 10); @@ -590,9 +591,9 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); + const model = new IndexTreeModel('test', toList(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'vscode', collapsed: true, children: [ { element: '.build' }, @@ -612,7 +613,7 @@ suite('IndexTreeModel', function () { } ] }, - ])); + ]); assert.deepEqual(toArray(list), ['vscode']); @@ -638,19 +639,19 @@ suite('IndexTreeModel', function () { test('simple', function () { const list: IIndexTreeNode[] = []; - const model = new IndexTreeModel('test', toSpliceable(list), -1); + const model = new IndexTreeModel('test', toList(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(model.getNodeLocation(list[0]), [0]); assert.deepEqual(model.getNodeLocation(list[1]), [0, 0]); @@ -668,9 +669,9 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); + const model = new IndexTreeModel('test', toList(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, @@ -682,7 +683,7 @@ suite('IndexTreeModel', function () { { element: 7 } ] } - ])); + ]); assert.deepEqual(model.getNodeLocation(list[0]), [0]); assert.deepEqual(model.getNodeLocation(list[1]), [0, 1]); @@ -700,13 +701,13 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); + const model = new IndexTreeModel('test', toList(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'silver' }, { element: 'gold' }, { element: 'platinum' } - ])); + ]); assert.deepEqual(toArray(list), ['silver', 'gold', 'platinum']); @@ -714,11 +715,11 @@ suite('IndexTreeModel', function () { model.refilter(); assert.deepEqual(toArray(list), ['platinum']); - model.splice([0], Number.POSITIVE_INFINITY, Iterator.fromArray([ + model.splice([0], Number.POSITIVE_INFINITY, [ { element: 'silver' }, { element: 'gold' }, { element: 'platinum' } - ])); + ]); assert.deepEqual(toArray(list), ['platinum']); model.refilter(); @@ -734,7 +735,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); + const model = new IndexTreeModel('test', toList(list), 'root', { filter }); model.splice([0], 0, [ { element: 'a', children: [{ element: 'aa' }] }, diff --git a/src/vs/base/test/browser/ui/tree/objectTree.test.ts b/src/vs/base/test/browser/ui/tree/objectTree.test.ts index d384c368d6e..166a99c00b3 100644 --- a/src/vs/base/test/browser/ui/tree/objectTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTree.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ObjectTree, CompressibleObjectTree, ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -import { Iterator } from 'vs/base/common/iterator'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; suite('ObjectTree', function () { @@ -46,17 +45,17 @@ suite('ObjectTree', function () { }); test('should be able to navigate', () => { - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(); @@ -87,17 +86,17 @@ suite('ObjectTree', function () { }); test('should skip collapsed nodes', () => { - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(); @@ -118,17 +117,17 @@ suite('ObjectTree', function () { test('should skip filtered elements', () => { filter = el => el % 2 === 0; - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(); @@ -150,17 +149,17 @@ suite('ObjectTree', function () { }); test('should be able to start from node', () => { - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(1); @@ -291,50 +290,50 @@ suite('CompressibleObjectTree', function () { const tree = new CompressibleObjectTree('test', container, new Delegate(), [new Renderer()]); tree.layout(200); - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 1, children: Iterator.fromArray([{ - element: 11, children: Iterator.fromArray([{ - element: 111, children: Iterator.fromArray([ + element: 1, children: [{ + element: 11, children: [{ + element: 111, children: [ { element: 1111 }, { element: 1112 }, { element: 1113 }, - ]) - }]) - }]) + ] + }] + }] } - ])); + ]); let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']); - tree.setChildren(11, Iterator.fromArray([ + tree.setChildren(11, [ { element: 111 }, { element: 112 }, { element: 113 }, - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113']); - tree.setChildren(113, Iterator.fromArray([ + tree.setChildren(113, [ { element: 1131 } - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113/1131']); - tree.setChildren(1131, Iterator.fromArray([ + tree.setChildren(1131, [ { element: 1132 } - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113/1131/1132']); - tree.setChildren(1131, Iterator.fromArray([ + tree.setChildren(1131, [ { element: 1132 }, { element: 1133 }, - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113/1131', '1132', '1133']); @@ -348,19 +347,19 @@ suite('CompressibleObjectTree', function () { const tree = new CompressibleObjectTree('test', container, new Delegate(), [new Renderer()]); tree.layout(200); - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 1, children: Iterator.fromArray([{ - element: 11, children: Iterator.fromArray([{ - element: 111, children: Iterator.fromArray([ + element: 1, children: [{ + element: 11, children: [{ + element: 111, children: [ { element: 1111 }, { element: 1112 }, { element: 1113 }, - ]) - }]) - }]) + ] + }] + }] } - ])); + ]); let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']); diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index 66ecd17238f..4663962b557 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -4,16 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import { ISpliceable } from 'vs/base/common/sequence'; +import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; -import { Iterator } from 'vs/base/common/iterator'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; -function toSpliceable(arr: T[]): ISpliceable { +function toList(arr: T[]): IList { return { splice(start: number, deleteCount: number, elements: T[]): void { + // console.log(`splice (${start}, ${deleteCount}, ${elements.length} [${elements.join(', ')}] )`); // debugging arr.splice(start, deleteCount, ...elements); - } + }, + updateElementHeight() { } }; } @@ -25,7 +26,7 @@ suite('ObjectTreeModel', function () { test('ctor', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list)); + const model = new ObjectTreeModel('test', toList(list)); assert(model); assert.equal(list.length, 0); assert.equal(model.size, 0); @@ -33,81 +34,81 @@ suite('ObjectTreeModel', function () { test('flat', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list)); + const model = new ObjectTreeModel('test', toList(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [0, 1, 2]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 3 }, { element: 4 }, { element: 5 }, - ])); + ]); assert.deepEqual(toArray(list), [3, 4, 5]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); test('nested', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list)); + const model = new ObjectTreeModel('test', toList(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [0, 10, 11, 12, 1, 2]); assert.equal(model.size, 6); - model.setChildren(12, Iterator.fromArray([ + model.setChildren(12, [ { element: 120 }, { element: 121 } - ])); + ]); assert.deepEqual(toArray(list), [0, 10, 11, 12, 120, 121, 1, 2]); assert.equal(model.size, 8); - model.setChildren(0, Iterator.empty()); + model.setChildren(0); assert.deepEqual(toArray(list), [0, 1, 2]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); test('setChildren on collapsed node', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list)); + const model = new ObjectTreeModel('test', toList(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 0, collapsed: true } - ])); + ]); assert.deepEqual(toArray(list), [0]); - model.setChildren(0, Iterator.fromArray([ + model.setChildren(0, [ { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [0]); @@ -117,7 +118,7 @@ suite('ObjectTreeModel', function () { test('setChildren on expanded, unrevealed node', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list)); + const model = new ObjectTreeModel('test', toList(list)); model.setChildren(null, [ { @@ -143,7 +144,7 @@ suite('ObjectTreeModel', function () { test('collapse state is preserved with strict identity', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list), { collapseByDefault: true }); + const model = new ObjectTreeModel('test', toList(list), { collapseByDefault: true }); const data = [{ element: 'father', children: [{ element: 'child' }] }]; model.setChildren(null, data); @@ -173,7 +174,7 @@ suite('ObjectTreeModel', function () { let compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1; const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } }); + const model = new ObjectTreeModel('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } }); const data = [ { element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] }, { element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] }, @@ -188,7 +189,7 @@ suite('ObjectTreeModel', function () { let compare: (a: string, b: string) => number = () => 0; const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } }); + const model = new ObjectTreeModel('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } }); const data = [ { element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] }, { element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] }, @@ -223,7 +224,7 @@ suite('ObjectTreeModel', function () { test('expandTo', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list), { collapseByDefault: true }); + const model = new ObjectTreeModel('test', toList(list), { collapseByDefault: true }); model.setChildren(null, [ { @@ -241,4 +242,35 @@ suite('ObjectTreeModel', function () { model.expandTo(1000); assert.deepEqual(toArray(list), [0, 10, 100, 1000, 11, 12, 1, 2]); }); + + test('issue #95641', () => { + const list: ITreeNode[] = []; + let fn = (_: string) => true; + const filter = new class implements ITreeFilter { + filter(element: string, parentVisibility: TreeVisibility): TreeVisibility { + if (element === 'file') { + return TreeVisibility.Recurse; + } + + return fn(element) ? TreeVisibility.Visible : parentVisibility; + } + }; + const model = new ObjectTreeModel('test', toList(list), { filter }); + + model.setChildren(null, [{ element: 'file', children: [{ element: 'hello' }] }]); + assert.deepEqual(toArray(list), ['file', 'hello']); + + fn = (el: string) => el === 'world'; + model.refilter(); + assert.deepEqual(toArray(list), []); + + model.setChildren('file', [{ element: 'world' }]); + assert.deepEqual(toArray(list), ['file', 'world']); + + model.setChildren('file', [{ element: 'hello' }]); + assert.deepEqual(toArray(list), []); + + model.setChildren('file', [{ element: 'world' }]); + assert.deepEqual(toArray(list), ['file', 'world']); + }); }); diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index 4c0fe3e3e17..8b202b6eb22 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -31,6 +31,24 @@ suite('Arrays', () => { assert.equal(array[idx], 1); }); + test('quickSelect', () => { + + function assertMedian(expexted: number, data: number[], nth: number = Math.floor(data.length / 2)) { + const compare = (a: number, b: number) => a - b; + let actual1 = arrays.quickSelect(nth, data, compare); + assert.equal(actual1, expexted); + + let actual2 = data.slice().sort(compare)[nth]; + assert.equal(actual2, expexted); + } + + assertMedian(5, [9, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5]); + assertMedian(8, [9, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5], 8); + assertMedian(8, [13, 4, 8]); + assertMedian(4, [13, 4, 8, 4, 4]); + assertMedian(13, [13, 4, 8], 2); + }); + test('stableSort', () => { function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { for (let i = 0; i < num; i++) { @@ -342,5 +360,13 @@ suite('Arrays', () => { arrays.coalesceInPlace(sparse); assert.equal(sparse.length, 5); }); -}); + test('insert, remove', function () { + const array: string[] = []; + const remove = arrays.insert(array, 'foo'); + assert.equal(array[0], 'foo'); + + remove(); + assert.equal(array.length, 0); + }); +}); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 586c0fa8a7f..a7d505b2116 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import * as async from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; suite('Async', () => { @@ -526,7 +527,7 @@ suite('Async', () => { r1Queue.queue(syncPromiseFactory); - return new Promise(c => setTimeout(() => c(), 0)).then(() => { + return new Promise(c => setTimeout(() => c(), 0)).then(() => { const r1Queue2 = queue.queueFor(URI.file('/some/path')); assert.notEqual(r1Queue, r1Queue2); // previous one got disposed after finishing }); @@ -557,4 +558,165 @@ suite('Async', () => { assert.equal(error, error); } }); + + test('TaskSequentializer - pending basics', async function () { + const sequentializer = new async.TaskSequentializer(); + + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(2323)); + assert.ok(!sequentializer.pending); + + // pending removes itself after done + await sequentializer.setPending(1, Promise.resolve()); + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(1)); + assert.ok(!sequentializer.pending); + + // pending removes itself after done (use async.timeout) + sequentializer.setPending(2, async.timeout(1)); + assert.ok(sequentializer.hasPending()); + assert.ok(sequentializer.hasPending(2)); + assert.ok(!sequentializer.hasPending(1)); + assert.ok(sequentializer.pending); + + await async.timeout(2); + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(2)); + assert.ok(!sequentializer.pending); + }); + + test('TaskSequentializer - pending and next (finishes instantly)', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes instantly + let nextDone = false; + const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); + + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); + + test('TaskSequentializer - pending and next (finishes after timeout)', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes after async.timeout + let nextDone = false; + const res = sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; })); + + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); + + test('TaskSequentializer - pending and multiple next (last one wins)', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes after async.timeout + let firstDone = false; + let firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); + + let secondDone = false; + let secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); + + let thirdDone = false; + let thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); + + await Promise.all([firstRes, secondRes, thirdRes]); + assert.ok(pendingDone); + assert.ok(!firstDone); + assert.ok(!secondDone); + assert.ok(thirdDone); + }); + + test('TaskSequentializer - cancel pending', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingCancelled = false; + sequentializer.setPending(1, async.timeout(1), () => pendingCancelled = true); + sequentializer.cancelPending(); + + assert.ok(pendingCancelled); + }); + + test('raceCancellation', async () => { + const cts = new CancellationTokenSource(); + + const now = Date.now(); + + const p = async.raceCancellation(async.timeout(100), cts.token); + cts.cancel(); + + await p; + + assert.ok(Date.now() - now < 100); + }); + + test('raceTimeout', async () => { + const cts = new CancellationTokenSource(); + + // timeout wins + let now = Date.now(); + let timedout = false; + + const p1 = async.raceTimeout(async.timeout(100), 1, () => timedout = true); + cts.cancel(); + + await p1; + + assert.ok(Date.now() - now < 100); + assert.equal(timedout, true); + + // promise wins + now = Date.now(); + timedout = false; + + const p2 = async.raceTimeout(async.timeout(1), 100, () => timedout = true); + cts.cancel(); + + await p2; + + assert.ok(Date.now() - now < 100); + assert.equal(timedout, false); + }); + + test('SequencerByKey', async () => { + const s = new async.SequencerByKey(); + + const r1 = await s.queue('key1', () => Promise.resolve('hello')); + assert.equal(r1, 'hello'); + + await s.queue('key2', () => Promise.reject(new Error('failed'))).then(() => { + throw new Error('should not be resolved'); + }, err => { + // Expected error + assert.equal(err.message, 'failed'); + }); + + // Still works after a queued promise is rejected + const r3 = await s.queue('key2', () => Promise.resolve('hello')); + assert.equal(r3, 'hello'); + }); + + test('IntervalCounter', async () => { + const counter = new async.IntervalCounter(10); + assert.equal(counter.increment(), 1); + assert.equal(counter.increment(), 2); + assert.equal(counter.increment(), 3); + + await async.timeout(20); + + assert.equal(counter.increment(), 1); + assert.equal(counter.increment(), 2); + assert.equal(counter.increment(), 3); + }); }); diff --git a/src/vs/base/test/common/codicon.test.ts b/src/vs/base/test/common/codicon.test.ts index b3fdb5bde1b..8d974ea0ad4 100644 --- a/src/vs/base/test/common/codicon.test.ts +++ b/src/vs/base/test/common/codicon.test.ts @@ -2,9 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { IMatch } from 'vs/base/common/filters'; import { matchesFuzzyCodiconAware, parseCodicons, IParsedCodicons } from 'vs/base/common/codicon'; +import { stripCodicons } from 'vs/base/common/codicons'; export interface ICodiconFilter { // Returns null if word doesn't match. @@ -64,3 +66,13 @@ suite('Codicon', () => { ]); }); }); + +suite('Codicons', () => { + + test('stripCodicons', () => { + assert.equal(stripCodicons('Hello World'), 'Hello World'); + assert.equal(stripCodicons('$(Hello World'), '$(Hello World'); + assert.equal(stripCodicons('$(Hello) World'), ' World'); + assert.equal(stripCodicons('$(Hello) W$(oi)rld'), ' Wrld'); + }); +}); diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index 47d94c1dbf3..5ee0207a3de 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -152,6 +152,14 @@ suite('Color', () => { assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 0.502, 1)), new RGBA(0, 128, 128, 1)); assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 0.502, 1)), new RGBA(0, 0, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0, 0)), new RGBA(0, 0, 0, 0)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0, 1)), new RGBA(0, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 1, 1)), new RGBA(255, 255, 255, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 1, 1, 1)), new RGBA(255, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0.753, 1)), new RGBA(192, 192, 192, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0.502, 1)), new RGBA(128, 128, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(360, 1, 0.502, 1)), new RGBA(128, 0, 0, 1)); + }); test('HSVA.fromRGBA', () => { diff --git a/src/vs/base/test/common/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts index eb3d8da7a46..03993437ffc 100644 --- a/src/vs/base/test/common/extpath.test.ts +++ b/src/vs/base/test/common/extpath.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import * as extpath from 'vs/base/common/extpath'; import * as platform from 'vs/base/common/platform'; +import { CharCode } from 'vs/base/common/charCode'; suite('Paths', () => { @@ -56,6 +57,13 @@ suite('Paths', () => { assert.ok(!extpath.isValidBasename('aux')); assert.ok(!extpath.isValidBasename('Aux')); assert.ok(!extpath.isValidBasename('LPT0')); + assert.ok(!extpath.isValidBasename('aux.txt')); + assert.ok(!extpath.isValidBasename('com0.abc')); + assert.ok(extpath.isValidBasename('LPT00')); + assert.ok(extpath.isValidBasename('aux1')); + assert.ok(extpath.isValidBasename('aux1.txt')); + assert.ok(extpath.isValidBasename('aux1.aux.txt')); + assert.ok(!extpath.isValidBasename('test.txt.')); assert.ok(!extpath.isValidBasename('test.txt..')); assert.ok(!extpath.isValidBasename('test.txt ')); @@ -114,4 +122,56 @@ suite('Paths', () => { assert.ok(!extpath.isRootOrDriveLetter('/path')); } }); + + test('isWindowsDriveLetter', () => { + assert.ok(!extpath.isWindowsDriveLetter(0)); + assert.ok(!extpath.isWindowsDriveLetter(-1)); + assert.ok(extpath.isWindowsDriveLetter(CharCode.A)); + assert.ok(extpath.isWindowsDriveLetter(CharCode.z)); + }); + + test('indexOfPath', () => { + assert.equal(extpath.indexOfPath('/foo', '/bar', true), -1); + assert.equal(extpath.indexOfPath('/foo', '/FOO', false), -1); + assert.equal(extpath.indexOfPath('/foo', '/FOO', true), 0); + assert.equal(extpath.indexOfPath('/some/long/path', '/some/long', false), 0); + assert.equal(extpath.indexOfPath('/some/long/path', '/PATH', true), 10); + }); + + test('parseLineAndColumnAware', () => { + let res = extpath.parseLineAndColumnAware('/foo/bar'); + assert.equal(res.path, '/foo/bar'); + assert.equal(res.line, undefined); + assert.equal(res.column, undefined); + + res = extpath.parseLineAndColumnAware('/foo/bar:33'); + assert.equal(res.path, '/foo/bar'); + assert.equal(res.line, 33); + assert.equal(res.column, 1); + + res = extpath.parseLineAndColumnAware('/foo/bar:33:34'); + assert.equal(res.path, '/foo/bar'); + assert.equal(res.line, 33); + assert.equal(res.column, 34); + + res = extpath.parseLineAndColumnAware('C:\\foo\\bar'); + assert.equal(res.path, 'C:\\foo\\bar'); + assert.equal(res.line, undefined); + assert.equal(res.column, undefined); + + res = extpath.parseLineAndColumnAware('C:\\foo\\bar:33'); + assert.equal(res.path, 'C:\\foo\\bar'); + assert.equal(res.line, 33); + assert.equal(res.column, 1); + + res = extpath.parseLineAndColumnAware('C:\\foo\\bar:33:34'); + assert.equal(res.path, 'C:\\foo\\bar'); + assert.equal(res.line, 33); + assert.equal(res.column, 34); + + res = extpath.parseLineAndColumnAware('/foo/bar:abb'); + assert.equal(res.path, '/foo/bar:abb'); + assert.equal(res.line, undefined); + assert.equal(res.column, undefined); + }); }); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 0cf70ae8555..17a3f681c73 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -67,7 +67,7 @@ suite('Filters', () => { filterNotOk(matchesPrefix, 'x', 'alpha'); filterOk(matchesPrefix, 'A', 'alpha', [{ start: 0, end: 1 }]); filterOk(matchesPrefix, 'AlPh', 'alPHA', [{ start: 0, end: 4 }]); - filterNotOk(matchesPrefix, 'T', '4'); // see https://github.com/Microsoft/vscode/issues/22401 + filterNotOk(matchesPrefix, 'T', '4'); // see https://github.com/microsoft/vscode/issues/22401 }); test('CamelCaseFilter', () => { @@ -343,6 +343,36 @@ suite('Filters', () => { ); }); + test('Freeze when fjfj -> jfjf, https://github.com/microsoft/vscode/issues/91807', function () { + assertMatches( + 'jfjfj', + 'fjfjfjfjfjfjfjfjfjfjfj', + undefined, fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfj', + 'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + undefined, fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfj', + 'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + undefined, fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfj', + 'fJfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + 'f^J^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', // strong match + fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfj', + 'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + 'f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', // any match + fuzzyScore, { firstMatchCanBeWeak: true } + ); + }); + test('fuzzyScore, issue #26423', function () { assertMatches('baba', 'abababab', undefined, fuzzyScore); diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts similarity index 56% rename from src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts rename to src/vs/base/test/common/fuzzyScorer.test.ts index a7bbe6fb2a0..5aac1fb98b4 100644 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as scorer from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import * as scorer from 'vs/base/common/fuzzyScorer'; import { URI } from 'vs/base/common/uri'; -import { basename, dirname, sep } from 'vs/base/common/path'; +import { basename, dirname, sep, posix, win32 } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; +import { Schemas } from 'vs/base/common/network'; class ResourceAccessorClass implements scorer.IItemAccessor { @@ -26,6 +27,40 @@ class ResourceAccessorClass implements scorer.IItemAccessor { const ResourceAccessor = new ResourceAccessorClass(); +class ResourceWithSlashAccessorClass implements scorer.IItemAccessor { + + getItemLabel(resource: URI): string { + return basename(resource.fsPath); + } + + getItemDescription(resource: URI): string { + return posix.normalize(dirname(resource.path)); + } + + getItemPath(resource: URI): string { + return posix.normalize(resource.path); + } +} + +const ResourceWithSlashAccessor = new ResourceWithSlashAccessorClass(); + +class ResourceWithBackslashAccessorClass implements scorer.IItemAccessor { + + getItemLabel(resource: URI): string { + return basename(resource.fsPath); + } + + getItemDescription(resource: URI): string { + return win32.normalize(dirname(resource.path)); + } + + getItemPath(resource: URI): string { + return win32.normalize(resource.path); + } +} + +const ResourceWithBackslashAccessor = new ResourceWithBackslashAccessorClass(); + class NullAccessorClass implements scorer.IItemAccessor { getItemLabel(resource: URI): string { @@ -41,41 +76,44 @@ class NullAccessorClass implements scorer.IItemAccessor { } } -function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score { - return scorer.score(target, query, query.toLowerCase(), fuzzy); +function _doScore(target: string, query: string, fuzzy: boolean): scorer.FuzzyScore { + const preparedQuery = scorer.prepareQuery(query); + + return scorer.scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, fuzzy); } -function scoreItem(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor, cache: scorer.ScorerCache): scorer.IItemScore { - return scorer.scoreItem(item, scorer.prepareQuery(query), fuzzy, accessor, cache); +function _doScore2(target: string, query: string, matchOffset: number = 0): scorer.FuzzyScore2 { + const preparedQuery = scorer.prepareQuery(query); + + return scorer.scoreFuzzy2(target, preparedQuery, 0, matchOffset); } -function compareItemsByScore(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor, cache: scorer.ScorerCache, fallbackComparer = scorer.fallbackCompare): number { - return scorer.compareItemsByScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache, fallbackComparer); +function scoreItem(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor): scorer.IItemScore { + return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null)); +} + +function compareItemsByScore(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor): number { + return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null)); } const NullAccessor = new NullAccessorClass(); -let cache: scorer.ScorerCache = Object.create(null); -suite('Quick Open Scorer', () => { - - setup(() => { - cache = Object.create(null); - }); +suite('Fuzzy Scorer', () => { test('score (fuzzy)', function () { const target = 'HeLlo-World'; - const scores: scorer.Score[] = []; + const scores: scorer.FuzzyScore[] = []; scores.push(_doScore(target, 'HelLo-World', true)); // direct case match scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple) scores.push(_doScore(target, 'hw', true)); // direct mix-case prefix (multiple) scores.push(_doScore(target, 'H', true)); // direct case prefix scores.push(_doScore(target, 'h', true)); // direct mix-case prefix - scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit) scores.push(_doScore(target, 'W', true)); // direct case word prefix - scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix scores.push(_doScore(target, 'Ld', true)); // in-string case match (multiple) + scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit) + scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix scores.push(_doScore(target, 'L', true)); // in-string case match scores.push(_doScore(target, 'l', true)); // in-string mix-case match scores.push(_doScore(target, '4', true)); // no match @@ -85,13 +123,13 @@ suite('Quick Open Scorer', () => { assert.deepEqual(scores, sortedScores); // Assert scoring positions - let positions = scores[0][1]; - assert.equal(positions.length, 'HelLo-World'.length); + // let positions = scores[0][1]; + // assert.equal(positions.length, 'HelLo-World'.length); - positions = scores[2][1]; - assert.equal(positions.length, 'HW'.length); - assert.equal(positions[0], 0); - assert.equal(positions[1], 6); + // positions = scores[2][1]; + // assert.equal(positions.length, 'HW'.length); + // assert.equal(positions[0], 0); + // assert.equal(positions[1], 6); }); test('score (non fuzzy)', function () { @@ -109,16 +147,16 @@ suite('Quick Open Scorer', () => { }); test('scoreItem - matches are proper', function () { - let res = scoreItem(null, 'something', true, ResourceAccessor, cache); + let res = scoreItem(null, 'something', true, ResourceAccessor); assert.ok(!res.score); const resource = URI.file('/xyz/some/path/someFile123.txt'); - res = scoreItem(resource, 'something', true, NullAccessor, cache); + res = scoreItem(resource, 'something', true, NullAccessor); assert.ok(!res.score); // Path Identity - const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); + const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor); assert.ok(identityRes.score); assert.equal(identityRes.descriptionMatch!.length, 1); assert.equal(identityRes.labelMatch!.length, 1); @@ -128,7 +166,7 @@ suite('Quick Open Scorer', () => { assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); // Basename Prefix - const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); + const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor); assert.ok(basenamePrefixRes.score); assert.ok(!basenamePrefixRes.descriptionMatch); assert.equal(basenamePrefixRes.labelMatch!.length, 1); @@ -136,7 +174,7 @@ suite('Quick Open Scorer', () => { assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); // Basename Camelcase - const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); + const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor); assert.ok(basenameCamelcaseRes.score); assert.ok(!basenameCamelcaseRes.descriptionMatch); assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); @@ -146,7 +184,7 @@ suite('Quick Open Scorer', () => { assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); // Basename Match - const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); + const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor); assert.ok(basenameRes.score); assert.ok(!basenameRes.descriptionMatch); assert.equal(basenameRes.labelMatch!.length, 2); @@ -156,7 +194,7 @@ suite('Quick Open Scorer', () => { assert.equal(basenameRes.labelMatch![1].end, 5); // Path Match - const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -168,7 +206,7 @@ suite('Quick Open Scorer', () => { assert.equal(pathRes.descriptionMatch![0].end, 4); // No Match - const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); + const noRes = scoreItem(resource, '987', true, ResourceAccessor); assert.ok(!noRes.score); assert.ok(!noRes.labelMatch); assert.ok(!noRes.descriptionMatch); @@ -180,12 +218,55 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score > noRes.score); }); + test('scoreItem - multiple', function () { + const resource = URI.file('/xyz/some/path/someFile123.txt'); + + let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor); + assert.ok(res1.score); + assert.equal(res1.labelMatch?.length, 1); + assert.equal(res1.labelMatch![0].start, 0); + assert.equal(res1.labelMatch![0].end, 4); + assert.equal(res1.descriptionMatch?.length, 1); + assert.equal(res1.descriptionMatch![0].start, 1); + assert.equal(res1.descriptionMatch![0].end, 4); + + let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor); + assert.ok(res2.score); + assert.equal(res1.score, res2.score); + assert.equal(res2.labelMatch?.length, 1); + assert.equal(res2.labelMatch![0].start, 0); + assert.equal(res2.labelMatch![0].end, 4); + assert.equal(res2.descriptionMatch?.length, 1); + assert.equal(res2.descriptionMatch![0].start, 1); + assert.equal(res2.descriptionMatch![0].end, 4); + + let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor); + assert.ok(res3.score); + assert.ok(res3.score > res2.score); + assert.equal(res3.labelMatch?.length, 1); + assert.equal(res3.labelMatch![0].start, 0); + assert.equal(res3.labelMatch![0].end, 11); + assert.equal(res3.descriptionMatch?.length, 1); + assert.equal(res3.descriptionMatch![0].start, 1); + assert.equal(res3.descriptionMatch![0].end, 4); + + let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor); + assert.ok(res4.score); + assert.ok(res4.score < res2.score); + assert.equal(res4.labelMatch?.length, 0); + assert.equal(res4.descriptionMatch?.length, 2); + assert.equal(res4.descriptionMatch![0].start, 2); + assert.equal(res4.descriptionMatch![0].end, 4); + assert.equal(res4.descriptionMatch![1].start, 10); + assert.equal(res4.descriptionMatch![1].end, 14); + }); + test('scoreItem - invalid input', function () { - let res = scoreItem(null, null!, true, ResourceAccessor, cache); + let res = scoreItem(null, null!, true, ResourceAccessor); assert.equal(res.score, 0); - res = scoreItem(null, 'null', true, ResourceAccessor, cache); + res = scoreItem(null, 'null', true, ResourceAccessor); assert.equal(res.score, 0); }); @@ -195,7 +276,7 @@ suite('Quick Open Scorer', () => { // xsp is more relevant to the end of the file path even though it matches // fuzzy also in the beginning. we verify the more relevant match at the // end gets returned. - const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -210,7 +291,7 @@ suite('Quick Open Scorer', () => { test('scoreItem - avoid match scattering (bug #36119)', function () { const resource = URI.file('projects/ui/cula/ats/target.mk'); - const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -224,7 +305,7 @@ suite('Quick Open Scorer', () => { // expect "ad" to be matched towards the end of the file because the // match is more compact - const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'ad', true, ResourceAccessor); assert.ok(res.score); assert.ok(res.descriptionMatch); assert.ok(!res.labelMatch!.length); @@ -238,14 +319,14 @@ suite('Quick Open Scorer', () => { test('scoreItem - proper target offset', function () { const resource = URI.file('etem'); - const res = scoreItem(resource, 'teem', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'teem', true, ResourceAccessor); assert.ok(!res.score); }); test('scoreItem - proper target offset #2', function () { const resource = URI.file('ede'); - const res = scoreItem(resource, 'de', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'de', true, ResourceAccessor); assert.equal(res.labelMatch!.length, 1); assert.equal(res.labelMatch![0].start, 1); @@ -255,7 +336,7 @@ suite('Quick Open Scorer', () => { test('scoreItem - proper target offset #3', function () { const resource = URI.file('/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg'); - const res = scoreItem(resource, 'debug', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'debug', true, ResourceAccessor); assert.equal(res.descriptionMatch!.length, 3); assert.equal(res.descriptionMatch![0].start, 9); @@ -275,10 +356,35 @@ suite('Quick Open Scorer', () => { test('scoreItem - no match unless query contained in sequence', function () { const resource = URI.file('abcde'); - const res = scoreItem(resource, 'edcda', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'edcda', true, ResourceAccessor); assert.ok(!res.score); }); + test('scoreItem - match if using slash or backslash (local, remote resource)', function () { + const localResource = URI.file('abcde/super/duper'); + const remoteResource = URI.from({ scheme: Schemas.vscodeRemote, path: 'abcde/super/duper' }); + + for (const resource of [localResource, remoteResource]) { + let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithSlashAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithBackslashAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithSlashAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithBackslashAccessor); + assert.ok(res.score); + } + }); + test('compareItemsByScore - identity', function () { const resourceA = URI.file('/some/path/fileA.txt'); const resourceB = URI.file('/some/path/other/fileB.txt'); @@ -287,12 +393,12 @@ suite('Quick Open Scorer', () => { // Full resource A path let query = ResourceAccessor.getItemPath(resourceA); - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -300,12 +406,12 @@ suite('Quick Open Scorer', () => { // Full resource B path query = ResourceAccessor.getItemPath(resourceB); - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -319,12 +425,12 @@ suite('Quick Open Scorer', () => { // Full resource A basename let query = ResourceAccessor.getItemLabel(resourceA); - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -332,12 +438,12 @@ suite('Quick Open Scorer', () => { // Full resource B basename query = ResourceAccessor.getItemLabel(resourceB); - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -351,12 +457,12 @@ suite('Quick Open Scorer', () => { // resource A camelcase let query = 'fA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -364,12 +470,12 @@ suite('Quick Open Scorer', () => { // resource B camelcase query = 'fB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -383,12 +489,12 @@ suite('Quick Open Scorer', () => { // Resource A part of basename let query = 'fileA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -396,12 +502,12 @@ suite('Quick Open Scorer', () => { // Resource B part of basename query = 'fileB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -415,12 +521,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'pathfileA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -428,12 +534,12 @@ suite('Quick Open Scorer', () => { // Resource B part of path query = 'pathfileB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -447,12 +553,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'somepath'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -466,12 +572,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'file'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); @@ -485,12 +591,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'somepath'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -503,54 +609,49 @@ suite('Quick Open Scorer', () => { let query = 'co/te'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); }); - test('compareFilesByScore - allow to provide fallback sorter (bug #31591)', function () { - const resourceA = URI.file('virtual/vscode.d.ts'); - const resourceB = URI.file('vscode/src/vs/vscode.d.ts'); + test('compareFilesByScore - prefer matches in label over description if scores are otherwise equal', function () { + const resourceA = URI.file('parts/quick/arrow-left-dark.svg'); + const resourceB = URI.file('parts/quickopen/quickopen.ts'); - let query = 'vscode'; + let query = 'partsquick'; - let res = [resourceA, resourceB].sort((r1, r2) => { - return compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => { - if (r1 as any /* TS fail */ === resourceA) { - return -1; - } - - return 1; - }); - }); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => { - return compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => { - if (r1 as any /* TS fail */ === resourceB) { - return -1; - } - - return 1; - }); - }); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); + test('compareFilesByScore - prefer camel case matches', function () { + const resourceA = URI.file('config/test/NullPointerException.java'); + const resourceB = URI.file('config/test/nopointerexception.java'); + + for (const query of ['npe', 'NPE']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + test('compareFilesByScore - prefer more compact camel case matches', function () { const resourceA = URI.file('config/test/openthisAnythingHandler.js'); const resourceB = URI.file('config/test/openthisisnotsorelevantforthequeryAnyHand.js'); let query = 'AH'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -561,11 +662,11 @@ suite('Quick Open Scorer', () => { let query = 'xp'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -576,11 +677,11 @@ suite('Quick Open Scorer', () => { let query = 'xp'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -591,11 +692,11 @@ suite('Quick Open Scorer', () => { let query = 'exfile'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -608,18 +709,18 @@ suite('Quick Open Scorer', () => { let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js'; - let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); - res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); query = isWindows ? 'un1\\index.js' : 'un1/index.js'; - res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -630,10 +731,10 @@ suite('Quick Open Scorer', () => { let query = 'StatVideoindex'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); }); @@ -643,10 +744,10 @@ suite('Quick Open Scorer', () => { let query = 'reproreduxts'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -657,10 +758,10 @@ suite('Quick Open Scorer', () => { let query = 'bookpageIndex'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); }); @@ -670,10 +771,10 @@ suite('Quick Open Scorer', () => { let query = isWindows ? 'ui\\icons' : 'ui/icons'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -683,10 +784,10 @@ suite('Quick Open Scorer', () => { let query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -696,10 +797,10 @@ suite('Quick Open Scorer', () => { let query = 'djancosig'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -710,12 +811,12 @@ suite('Quick Open Scorer', () => { let query = 'protectedconfig.php'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); @@ -727,10 +828,10 @@ suite('Quick Open Scorer', () => { let query = 'gradientmain'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -740,10 +841,10 @@ suite('Quick Open Scorer', () => { let query = 'abc'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -753,10 +854,10 @@ suite('Quick Open Scorer', () => { let query = 'xyz'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -766,10 +867,10 @@ suite('Quick Open Scorer', () => { let query = 'async'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -779,10 +880,10 @@ suite('Quick Open Scorer', () => { let query = 'partisettings'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -792,10 +893,10 @@ suite('Quick Open Scorer', () => { let query = 'tipsindex.cshtml'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -805,10 +906,10 @@ suite('Quick Open Scorer', () => { let query = 'listview'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -819,18 +920,289 @@ suite('Quick Open Scorer', () => { let query = 'filesexplorerview.ts'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); - test('prepareSearchForScoring', () => { - assert.equal(scorer.prepareQuery(' f*a ').value, 'fa'); - assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts'); - assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts'); + test('compareFilesByScore - prefer case match (bug #96122)', function () { + const resourceA = URI.file('lists.php'); + const resourceB = URI.file('lib/Lists.php'); + + let query = 'Lists.php'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - prefer shorter match (bug #103052) - foo bar', function () { + const resourceA = URI.file('app/emails/foo.bar.js'); + const resourceB = URI.file('app/emails/other-footer.other-bar.js'); + + for (const query of ['foo bar', 'foobar']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + + test('compareFilesByScore - prefer shorter match (bug #103052) - payment model', function () { + const resourceA = URI.file('app/components/payment/payment.model.js'); + const resourceB = URI.file('app/components/online-payments-history/online-payments-history.model.js'); + + for (const query of ['payment model', 'paymentmodel']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + + test('compareFilesByScore - prefer shorter match (bug #103052) - color', function () { + const resourceA = URI.file('app/constants/color.js'); + const resourceB = URI.file('app/components/model/input/pick-avatar-color.js'); + + for (const query of ['color js', 'colorjs']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + } + }); + + test('compareFilesByScore - prefer strict case prefix', function () { + const resourceA = URI.file('app/constants/color.js'); + const resourceB = URI.file('app/components/model/input/Color.js'); + + let query = 'Color'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + query = 'color'; + + res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + }); + + test('compareFilesByScore - prefer prefix (bug #103052)', function () { + const resourceA = URI.file('test/smoke/src/main.ts'); + const resourceB = URI.file('src/vs/editor/common/services/semantikTokensProviderStyling.ts'); + + let query = 'smoke main.ts'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + }); + + test('compareFilesByScore - boost better prefix match if multiple queries are used', function () { + const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts'); + const resourceB = URI.file('src/vs/workbench/browser/workbench.ts'); + + for (const query of ['workbench.ts browser', 'browser workbench.ts', 'browser workbench', 'workbench browser']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + } + }); + + test('compareFilesByScore - boost shorter prefix match if multiple queries are used', function () { + const resourceA = URI.file('src/vs/workbench/browser/actions/windowActions.ts'); + const resourceB = URI.file('src/vs/workbench/electron-browser/window.ts'); + + for (const query of ['window browser', 'window.ts browser']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + } + }); + + test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () { + const resourceA = URI.file('mesh_editor_lifetime_job.h'); + const resourceB = URI.file('lifetime_job.h'); + + for (const query of ['m life, life m']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + } + }); + + test('prepareQuery', () => { + assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa'); + assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts'); + assert.equal(scorer.prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase()); + assert.equal(scorer.prepareQuery('model Tester.ts').normalized, 'modelTester.ts'); + assert.equal(scorer.prepareQuery('Model Tester.ts').normalizedLowercase, 'modeltester.ts'); assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false); assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true); + + // with spaces + let query = scorer.prepareQuery('He*llo World'); + assert.equal(query.original, 'He*llo World'); + assert.equal(query.normalized, 'HelloWorld'); + assert.equal(query.normalizedLowercase, 'HelloWorld'.toLowerCase()); + assert.equal(query.values?.length, 2); + assert.equal(query.values?.[0].original, 'He*llo'); + assert.equal(query.values?.[0].normalized, 'Hello'); + assert.equal(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase()); + assert.equal(query.values?.[1].original, 'World'); + assert.equal(query.values?.[1].normalized, 'World'); + assert.equal(query.values?.[1].normalizedLowercase, 'World'.toLowerCase()); + + let restoredQuery = scorer.pieceToQuery(query.values!); + assert.equal(restoredQuery.original, query.original); + assert.equal(restoredQuery.values?.length, query.values?.length); + assert.equal(restoredQuery.containsPathSeparator, query.containsPathSeparator); + + // with spaces that are empty + query = scorer.prepareQuery(' Hello World '); + assert.equal(query.original, ' Hello World '); + assert.equal(query.originalLowercase, ' Hello World '.toLowerCase()); + assert.equal(query.normalized, 'HelloWorld'); + assert.equal(query.normalizedLowercase, 'HelloWorld'.toLowerCase()); + assert.equal(query.values?.length, 2); + assert.equal(query.values?.[0].original, 'Hello'); + assert.equal(query.values?.[0].originalLowercase, 'Hello'.toLowerCase()); + assert.equal(query.values?.[0].normalized, 'Hello'); + assert.equal(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase()); + assert.equal(query.values?.[1].original, 'World'); + assert.equal(query.values?.[1].originalLowercase, 'World'.toLowerCase()); + assert.equal(query.values?.[1].normalized, 'World'); + assert.equal(query.values?.[1].normalizedLowercase, 'World'.toLowerCase()); + + // Path related + if (isWindows) { + assert.equal(scorer.prepareQuery('C:\\some\\path').pathNormalized, 'C:\\some\\path'); + assert.equal(scorer.prepareQuery('C:\\some\\path').normalized, 'C:\\some\\path'); + assert.equal(scorer.prepareQuery('C:\\some\\path').containsPathSeparator, true); + assert.equal(scorer.prepareQuery('C:/some/path').pathNormalized, 'C:\\some\\path'); + assert.equal(scorer.prepareQuery('C:/some/path').normalized, 'C:\\some\\path'); + assert.equal(scorer.prepareQuery('C:/some/path').containsPathSeparator, true); + } else { + assert.equal(scorer.prepareQuery('/some/path').pathNormalized, '/some/path'); + assert.equal(scorer.prepareQuery('/some/path').normalized, '/some/path'); + assert.equal(scorer.prepareQuery('/some/path').containsPathSeparator, true); + assert.equal(scorer.prepareQuery('\\some\\path').pathNormalized, '/some/path'); + assert.equal(scorer.prepareQuery('\\some\\path').normalized, '/some/path'); + assert.equal(scorer.prepareQuery('\\some\\path').containsPathSeparator, true); + } + }); + + test('fuzzyScore2 (matching)', function () { + const target = 'HeLlo-World'; + + for (const offset of [0, 3]) { + let [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HeLlo-World', offset); + + assert.ok(score); + assert.equal(matches.length, 1); + assert.equal(matches[0].start, 0 + offset); + assert.equal(matches[0].end, target.length + offset); + + [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HW', offset); + + assert.ok(score); + assert.equal(matches.length, 2); + assert.equal(matches[0].start, 0 + offset); + assert.equal(matches[0].end, 1 + offset); + assert.equal(matches[1].start, 6 + offset); + assert.equal(matches[1].end, 7 + offset); + } + }); + + test('fuzzyScore2 (multiple queries)', function () { + const target = 'HeLlo-World'; + + const [firstSingleScore, firstSingleMatches] = _doScore2(target, 'HelLo'); + const [secondSingleScore, secondSingleMatches] = _doScore2(target, 'World'); + const firstAndSecondSingleMatches = [...firstSingleMatches || [], ...secondSingleMatches || []]; + + let [multiScore, multiMatches] = _doScore2(target, 'HelLo World'); + + function assertScore() { + assert.ok(multiScore ?? 0 >= ((firstSingleScore ?? 0) + (secondSingleScore ?? 0))); + for (let i = 0; multiMatches && i < multiMatches.length; i++) { + const multiMatch = multiMatches[i]; + const firstAndSecondSingleMatch = firstAndSecondSingleMatches[i]; + + if (multiMatch && firstAndSecondSingleMatch) { + assert.equal(multiMatch.start, firstAndSecondSingleMatch.start); + assert.equal(multiMatch.end, firstAndSecondSingleMatch.end); + } else { + assert.fail(); + } + } + } + + function assertNoScore() { + assert.equal(multiScore, undefined); + assert.equal(multiMatches.length, 0); + } + + assertScore(); + + [multiScore, multiMatches] = _doScore2(target, 'World HelLo'); + assertScore(); + + [multiScore, multiMatches] = _doScore2(target, 'World HelLo World'); + assertScore(); + + [multiScore, multiMatches] = _doScore2(target, 'World HelLo Nothing'); + assertNoScore(); + + [multiScore, multiMatches] = _doScore2(target, 'More Nothing'); + assertNoScore(); + }); + + test('fuzzyScore2 (#95716)', function () { + const target = '# ❌ Wow'; + + const score = _doScore2(target, '❌'); + assert.ok(score); + assert.ok(typeof score[0] === 'number'); + assert.ok(score[1].length > 0); }); }); diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index 58b5904b63d..b5074f4ffa5 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { hash } from 'vs/base/common/hash'; +import { hash, StringSHA1 } from 'vs/base/common/hash'; suite('Hash', () => { test('string', () => { @@ -32,12 +32,18 @@ suite('Hash', () => { assert.equal(hash([1, 2, 3]), hash([1, 2, 3])); assert.equal(hash(['foo', 'bar']), hash(['foo', 'bar'])); assert.equal(hash([]), hash([])); + assert.equal(hash([]), hash(new Array())); assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo'])); assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', null])); + assert.notEqual(hash(['foo', 'bar', null]), hash(['bar', 'foo', null])); + assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', undefined])); + assert.notEqual(hash(['foo', 'bar', undefined]), hash(['bar', 'foo', undefined])); + assert.notEqual(hash(['foo', 'bar', null]), hash(['foo', 'bar', undefined])); }); test('object', () => { assert.equal(hash({}), hash({})); + assert.equal(hash({}), hash(Object.create(null))); assert.equal(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' })); assert.equal(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' })); assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' })); @@ -45,12 +51,48 @@ suite('Hash', () => { }); test('array - unexpected collision', function () { - this.skip(); const a = hash([undefined, undefined, undefined, undefined, undefined]); const b = hash([undefined, undefined, 'HHHHHH', [{ line: 0, character: 0 }, { line: 0, character: 0 }], undefined]); - // console.log(a); - // console.log(b); assert.notEqual(a, b); }); + test('all different', () => { + const candidates: any[] = [ + null, undefined, {}, [], 0, false, true, '', ' ', [null], [undefined], [undefined, undefined], { '': undefined }, { [' ']: undefined }, + 'ab', 'ba', ['ab'] + ]; + const hashes: number[] = candidates.map(hash); + for (let i = 0; i < hashes.length; i++) { + assert.equal(hashes[i], hash(candidates[i])); // verify that repeated invocation returns the same hash + for (let k = i + 1; k < hashes.length; k++) { + assert.notEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} and ${JSON.stringify(candidates[k])}`); + } + } + }); + + + function checkSHA1(strings: string[], expected: string) { + const hash = new StringSHA1(); + for (const str of strings) { + hash.update(str); + } + const actual = hash.digest(); + assert.equal(actual, expected); + } + + test('sha1-1', () => { + checkSHA1(['\udd56'], '9bdb77276c1852e1fb067820472812fcf6084024'); + }); + + test('sha1-2', () => { + checkSHA1(['\udb52'], '9bdb77276c1852e1fb067820472812fcf6084024'); + }); + + test('sha1-3', () => { + checkSHA1(['\uda02ꑍ'], '9b483a471f22fe7e09d83f221871a987244bbd3f'); + }); + + test('sha1-4', () => { + checkSHA1(['hello'], 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'); + }); }); diff --git a/src/vs/base/test/common/history.test.ts b/src/vs/base/test/common/history.test.ts index fc32e74324b..8b92348be6f 100644 --- a/src/vs/base/test/common/history.test.ts +++ b/src/vs/base/test/common/history.test.ts @@ -106,6 +106,40 @@ suite('History Navigator', () => { assert.deepEqual(['2', '3', '1'], toArray(testObject)); }); + test('previous returns null if the current position is the first one', () => { + const testObject = new HistoryNavigator(['1', '2', '3']); + + testObject.first(); + + assert.deepEqual(testObject.previous(), null); + }); + + test('previous returns object if the current position is not the first one', () => { + const testObject = new HistoryNavigator(['1', '2', '3']); + + testObject.first(); + testObject.next(); + + assert.deepEqual(testObject.previous(), '1'); + }); + + test('next returns null if the current position is the last one', () => { + const testObject = new HistoryNavigator(['1', '2', '3']); + + testObject.last(); + + assert.deepEqual(testObject.next(), null); + }); + + test('next returns object if the current position is not the last one', () => { + const testObject = new HistoryNavigator(['1', '2', '3']); + + testObject.last(); + testObject.previous(); + + assert.deepEqual(testObject.next(), '3'); + }); + test('clear', () => { const testObject = new HistoryNavigator(['a', 'b', 'c']); assert.equal(testObject.previous(), 'c'); diff --git a/src/vs/base/test/common/iterator.test.ts b/src/vs/base/test/common/iterator.test.ts index b7a165c5095..7f32bc3eb6a 100644 --- a/src/vs/base/test/common/iterator.test.ts +++ b/src/vs/base/test/common/iterator.test.ts @@ -4,16 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; -suite('Iterator', () => { - test('concat', () => { - const first = Iterator.fromArray([1, 2, 3]); - const second = Iterator.fromArray([4, 5, 6]); - const third = Iterator.fromArray([7, 8, 9]); - const actualIterator = Iterator.concat(first, second, third); - const actual = Iterator.collect(actualIterator); +suite('Iterable', function () { - assert.deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9]); + const customIterable = new class { + + *[Symbol.iterator]() { + yield 'one'; + yield 'two'; + yield 'three'; + } + }; + + test('first', function () { + + assert.equal(Iterable.first([]), undefined); + assert.equal(Iterable.first([1]), 1); + assert.equal(Iterable.first(customIterable), 'one'); + assert.equal(Iterable.first(customIterable), 'one'); // fresh }); -}); \ No newline at end of file + +}); diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 4d15ad2046c..7aa87cc6b0f 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable, dispose, ReferenceCollection } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose, IDisposable, MultiDisposeError, ReferenceCollection, toDisposable } from 'vs/base/common/lifecycle'; class Disposable implements IDisposable { isDisposed = false; @@ -48,6 +48,108 @@ suite('Lifecycle', () => { assert(disposable.isDisposed); assert(disposable2.isDisposed); }); + + test('dispose array should dispose all if a child throws on dispose', () => { + const disposedValues = new Set(); + + let thrownError: any; + try { + dispose([ + toDisposable(() => { disposedValues.add(1); }), + toDisposable(() => { throw new Error('I am error'); }), + toDisposable(() => { disposedValues.add(3); }), + ]); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(3)); + assert.strictEqual(thrownError.message, 'I am error'); + }); + + test('dispose array should rethrow composite error if multiple entries throw on dispose', () => { + const disposedValues = new Set(); + + let thrownError: any; + try { + dispose([ + toDisposable(() => { disposedValues.add(1); }), + toDisposable(() => { throw new Error('I am error 1'); }), + toDisposable(() => { throw new Error('I am error 2'); }), + toDisposable(() => { disposedValues.add(4); }), + ]); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(4)); + assert.ok(thrownError instanceof MultiDisposeError); + assert.strictEqual((thrownError as MultiDisposeError).errors.length, 2); + assert.strictEqual((thrownError as MultiDisposeError).errors[0].message, 'I am error 1'); + assert.strictEqual((thrownError as MultiDisposeError).errors[1].message, 'I am error 2'); + }); + + test('Action bar has broken accessibility #100273', function () { + let array = [{ dispose() { } }, { dispose() { } }]; + let array2 = dispose(array); + + assert.equal(array.length, 2); + assert.equal(array2.length, 0); + assert.ok(array !== array2); + + let set = new Set([{ dispose() { } }, { dispose() { } }]); + let setValues = set.values(); + let setValues2 = dispose(setValues); + assert.ok(setValues === setValues2); + }); +}); + +suite('DisposableStore', () => { + test('dispose should call all child disposes even if a child throws on dispose', () => { + const disposedValues = new Set(); + + const store = new DisposableStore(); + store.add(toDisposable(() => { disposedValues.add(1); })); + store.add(toDisposable(() => { throw new Error('I am error'); })); + store.add(toDisposable(() => { disposedValues.add(3); })); + + let thrownError: any; + try { + store.dispose(); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(3)); + assert.strictEqual(thrownError.message, 'I am error'); + }); + + test('dispose should throw composite error if multiple children throw on dispose', () => { + const disposedValues = new Set(); + + const store = new DisposableStore(); + store.add(toDisposable(() => { disposedValues.add(1); })); + store.add(toDisposable(() => { throw new Error('I am error 1'); })); + store.add(toDisposable(() => { throw new Error('I am error 2'); })); + store.add(toDisposable(() => { disposedValues.add(4); })); + + let thrownError: any; + try { + store.dispose(); + } catch (e) { + thrownError = e; + } + + assert.ok(disposedValues.has(1)); + assert.ok(disposedValues.has(4)); + assert.ok(thrownError instanceof MultiDisposeError); + assert.strictEqual((thrownError as MultiDisposeError).errors.length, 2); + assert.strictEqual((thrownError as MultiDisposeError).errors[0].message, 'I am error 1'); + assert.strictEqual((thrownError as MultiDisposeError).errors[1].message, 'I am error 2'); + }); }); suite('Reference Collection', () => { diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index 7dc178dbbc0..e63fda850ac 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -16,9 +16,12 @@ suite('LinkedList', function () { // assert toArray assert.deepEqual(list.toArray(), elements); - // assert iterator - for (let iter = list.iterator(), element = iter.next(); !element.done; element = iter.next()) { - assert.equal(elements.shift(), element.value); + // assert Symbol.iterator (1) + assert.deepEqual([...list], elements); + + // assert Symbol.iterator (2) + for (const item of list) { + assert.equal(item, elements.shift()); } assert.equal(elements.length, 0); } diff --git a/src/vs/base/test/common/linkedText.test.ts b/src/vs/base/test/common/linkedText.test.ts new file mode 100644 index 00000000000..a7b61a558c2 --- /dev/null +++ b/src/vs/base/test/common/linkedText.test.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { parseLinkedText } from 'vs/base/common/linkedText'; + +suite('LinkedText', () => { + test('parses correctly', () => { + assert.deepEqual(parseLinkedText('').nodes, []); + assert.deepEqual(parseLinkedText('hello').nodes, ['hello']); + assert.deepEqual(parseLinkedText('hello there').nodes, ['hello there']); + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).').nodes, [ + 'Some message with ', + { label: 'link text', href: 'http://link.href' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").').nodes, [ + 'Some message with ', + { label: 'link text', href: 'http://link.href', title: 'and a title' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a title\').').nodes, [ + 'Some message with ', + { label: 'link text', href: 'http://link.href', title: 'and a title' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a \'title\'").').nodes, [ + 'Some message with ', + { label: 'link text', href: 'http://link.href', title: 'and a \'title\'' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a "title"\').').nodes, [ + 'Some message with ', + { label: 'link text', href: 'http://link.href', title: 'and a "title"' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [ + 'Some message with [link text](random stuff).' + ]); + assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).').nodes, [ + 'Some message with ', + { label: 'https link', href: 'https://link.href' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [https link](https:).').nodes, [ + 'Some message with [https link](https:).' + ]); + assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).').nodes, [ + 'Some message with ', + { label: 'a command', href: 'command:foobar' }, + '.' + ]); + assert.deepEqual(parseLinkedText('Some message with [a command](command:).').nodes, [ + 'Some message with [a command](command:).' + ]); + assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...').nodes, [ + 'link ', + { label: 'one', href: 'command:foo', title: 'nice' }, + ' and link ', + { label: 'two', href: 'http://foo' }, + '...' + ]); + assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...').nodes, [ + 'link\n', + { label: 'one', href: 'command:foo', title: 'nice' }, + '\nand link ', + { label: 'two', href: 'http://foo' }, + '...' + ]); + }); +}); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 8afd0496f24..2615f098e4c 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, mapToSerializable, serializableToMap } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator } from 'vs/base/common/map'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { IteratorResult } from 'vs/base/common/iterator'; +import { extUriIgnorePathCase } from 'vs/base/common/resources'; suite('Map', () => { @@ -14,8 +14,8 @@ suite('Map', () => { let map = new LinkedMap(); map.set('ak', 'av'); map.set('bk', 'bv'); - assert.deepStrictEqual(map.keys(), ['ak', 'bk']); - assert.deepStrictEqual(map.values(), ['av', 'bv']); + assert.deepStrictEqual([...map.keys()], ['ak', 'bk']); + assert.deepStrictEqual([...map.values()], ['av', 'bv']); assert.equal(map.first, 'av'); assert.equal(map.last, 'bv'); }); @@ -24,16 +24,16 @@ suite('Map', () => { let map = new LinkedMap(); map.set('ak', 'av'); map.set('ak', 'av', Touch.AsOld); - assert.deepStrictEqual(map.keys(), ['ak']); - assert.deepStrictEqual(map.values(), ['av']); + assert.deepStrictEqual([...map.keys()], ['ak']); + assert.deepStrictEqual([...map.values()], ['av']); }); test('LinkedMap - Touch New one', () => { let map = new LinkedMap(); map.set('ak', 'av'); map.set('ak', 'av', Touch.AsNew); - assert.deepStrictEqual(map.keys(), ['ak']); - assert.deepStrictEqual(map.values(), ['av']); + assert.deepStrictEqual([...map.keys()], ['ak']); + assert.deepStrictEqual([...map.values()], ['av']); }); test('LinkedMap - Touch Old two', () => { @@ -41,8 +41,8 @@ suite('Map', () => { map.set('ak', 'av'); map.set('bk', 'bv'); map.set('bk', 'bv', Touch.AsOld); - assert.deepStrictEqual(map.keys(), ['bk', 'ak']); - assert.deepStrictEqual(map.values(), ['bv', 'av']); + assert.deepStrictEqual([...map.keys()], ['bk', 'ak']); + assert.deepStrictEqual([...map.values()], ['bv', 'av']); }); test('LinkedMap - Touch New two', () => { @@ -50,8 +50,8 @@ suite('Map', () => { map.set('ak', 'av'); map.set('bk', 'bv'); map.set('ak', 'av', Touch.AsNew); - assert.deepStrictEqual(map.keys(), ['bk', 'ak']); - assert.deepStrictEqual(map.values(), ['bv', 'av']); + assert.deepStrictEqual([...map.keys()], ['bk', 'ak']); + assert.deepStrictEqual([...map.values()], ['bv', 'av']); }); test('LinkedMap - Touch Old from middle', () => { @@ -60,8 +60,8 @@ suite('Map', () => { map.set('bk', 'bv'); map.set('ck', 'cv'); map.set('bk', 'bv', Touch.AsOld); - assert.deepStrictEqual(map.keys(), ['bk', 'ak', 'ck']); - assert.deepStrictEqual(map.values(), ['bv', 'av', 'cv']); + assert.deepStrictEqual([...map.keys()], ['bk', 'ak', 'ck']); + assert.deepStrictEqual([...map.values()], ['bv', 'av', 'cv']); }); test('LinkedMap - Touch New from middle', () => { @@ -70,8 +70,8 @@ suite('Map', () => { map.set('bk', 'bv'); map.set('ck', 'cv'); map.set('bk', 'bv', Touch.AsNew); - assert.deepStrictEqual(map.keys(), ['ak', 'ck', 'bk']); - assert.deepStrictEqual(map.values(), ['av', 'cv', 'bv']); + assert.deepStrictEqual([...map.keys()], ['ak', 'ck', 'bk']); + assert.deepStrictEqual([...map.values()], ['av', 'cv', 'bv']); }); test('LinkedMap - basics', function () { @@ -130,6 +130,61 @@ suite('Map', () => { assert.ok(!map.has('1')); }); + test('LinkedMap - Iterators', () => { + const map = new LinkedMap(); + map.set(1, 1); + map.set(2, 2); + map.set(3, 3); + + for (const elem of map.keys()) { + assert.ok(elem); + } + + for (const elem of map.values()) { + assert.ok(elem); + } + + for (const elem of map.entries()) { + assert.ok(elem); + } + + { + const keys = map.keys(); + const values = map.values(); + const entries = map.entries(); + map.get(1); + keys.next(); + values.next(); + entries.next(); + } + + { + const keys = map.keys(); + const values = map.values(); + const entries = map.entries(); + map.get(1, Touch.AsNew); + + let exceptions: number = 0; + try { + keys.next(); + } catch (err) { + exceptions++; + } + try { + values.next(); + } catch (err) { + exceptions++; + } + try { + entries.next(); + } catch (err) { + exceptions++; + } + + assert.strictEqual(exceptions, 3); + } + }); + test('LinkedMap - LRU Cache simple', () => { const cache = new LRUCache(5); @@ -137,10 +192,10 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); cache.set(6, 6); assert.strictEqual(cache.size, 5); - assert.deepStrictEqual(cache.keys(), [2, 3, 4, 5, 6]); + assert.deepStrictEqual([...cache.keys()], [2, 3, 4, 5, 6]); cache.set(7, 7); assert.strictEqual(cache.size, 5); - assert.deepStrictEqual(cache.keys(), [3, 4, 5, 6, 7]); + assert.deepStrictEqual([...cache.keys()], [3, 4, 5, 6, 7]); let values: number[] = []; [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [3, 4, 5, 6, 7]); @@ -151,11 +206,11 @@ suite('Map', () => { [1, 2, 3, 4, 5].forEach(value => cache.set(value, value)); assert.strictEqual(cache.size, 5); - assert.deepStrictEqual(cache.keys(), [1, 2, 3, 4, 5]); + assert.deepStrictEqual([...cache.keys()], [1, 2, 3, 4, 5]); cache.get(3); - assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]); + assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]); cache.peek(4); - assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]); + assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]); let values: number[] = []; [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [1, 2, 3, 4, 5]); @@ -170,7 +225,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 10); cache.limit = 5; assert.strictEqual(cache.size, 5); - assert.deepStrictEqual(cache.keys(), [6, 7, 8, 9, 10]); + assert.deepStrictEqual([...cache.keys()], [6, 7, 8, 9, 10]); cache.limit = 20; assert.strictEqual(cache.size, 5); for (let i = 11; i <= 20; i++) { @@ -182,7 +237,7 @@ suite('Map', () => { values.push(cache.get(i)!); assert.strictEqual(cache.get(i), i); } - assert.deepStrictEqual(cache.values(), values); + assert.deepStrictEqual([...cache.values()], values); }); test('LinkedMap - LRU Cache limit with ratio', () => { @@ -194,11 +249,11 @@ suite('Map', () => { assert.strictEqual(cache.size, 10); cache.set(11, 11); assert.strictEqual(cache.size, 5); - assert.deepStrictEqual(cache.keys(), [7, 8, 9, 10, 11]); + assert.deepStrictEqual([...cache.keys()], [7, 8, 9, 10, 11]); let values: number[] = []; - cache.keys().forEach(key => values.push(cache.get(key)!)); + [...cache.keys()].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [7, 8, 9, 10, 11]); - assert.deepStrictEqual(cache.values(), values); + assert.deepStrictEqual([...cache.values()], values); }); test('LinkedMap - toJSON / fromJSON', () => { @@ -223,7 +278,6 @@ suite('Map', () => { assert.equal(key, 'ck'); assert.equal(value, 'cv'); } - i++; }); }); @@ -238,7 +292,7 @@ suite('Map', () => { map.delete('1'); assert.equal(map.get('1'), undefined); assert.equal(map.size, 0); - assert.equal(map.keys().length, 0); + assert.equal([...map.keys()].length, 0); }); test('LinkedMap - delete Head', function () { @@ -252,8 +306,8 @@ suite('Map', () => { map.delete('1'); assert.equal(map.get('2'), 2); assert.equal(map.size, 1); - assert.equal(map.keys().length, 1); - assert.equal(map.keys()[0], 2); + assert.equal([...map.keys()].length, 1); + assert.equal([...map.keys()][0], 2); }); test('LinkedMap - delete Tail', function () { @@ -267,8 +321,8 @@ suite('Map', () => { map.delete('2'); assert.equal(map.get('1'), 1); assert.equal(map.size, 1); - assert.equal(map.keys().length, 1); - assert.equal(map.keys()[0], 1); + assert.equal([...map.keys()].length, 1); + assert.equal([...map.keys()][0], 1); }); @@ -313,7 +367,66 @@ suite('Map', () => { assert.equal(iter.hasNext(), false); }); - function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { + test('URIIterator', function () { + const iter = new UriIterator(false); + iter.reset(URI.parse('file:///usr/bin/file.txt')); + + assert.equal(iter.value(), 'file'); + // assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.cmp('file'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'usr'); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'bin'); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'file.txt'); + assert.equal(iter.hasNext(), false); + + + iter.reset(URI.parse('file://share/usr/bin/file.txt?foo')); + + // scheme + assert.equal(iter.value(), 'file'); + // assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.cmp('file'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + // authority + assert.equal(iter.value(), 'share'); + assert.equal(iter.cmp('SHARe'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'usr'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'bin'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'file.txt'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // query + assert.equal(iter.value(), 'foo'); + assert.equal(iter.cmp('z') > 0, true); + assert.equal(iter.cmp('a') < 0, true); + assert.equal(iter.hasNext(), false); + }); + + function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { const map = new Map(); for (const [key, value] of elements) { map.set(key, value); @@ -321,11 +434,22 @@ suite('Map', () => { map.forEach((value, key) => { assert.equal(trie.get(key), value); }); + + // forEach + let forEachCount = 0; trie.forEach((element, key) => { assert.equal(element, map.get(key)); - map.delete(key); + forEachCount++; }); - assert.equal(map.size, 0); + assert.equal(map.size, forEachCount); + + // iterator + let iterCount = 0; + for (let [key, value] of trie) { + assert.equal(value, map.get(key)); + iterCount++; + } + assert.equal(map.size, iterCount); } test('TernarySearchTree - set', function () { @@ -379,7 +503,7 @@ suite('Map', () => { }); test('TernarySearchTree - basics', function () { - let trie = new TernarySearchTree(new StringIterator()); + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('bar', 2); @@ -409,17 +533,42 @@ suite('Map', () => { }); test('TernarySearchTree - delete & cleanup', function () { - let trie = new TernarySearchTree(new StringIterator()); + // normal delete + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); - + assertTernarySearchTree(trie, ['foo', 1], ['foobar', 2], ['bar', 3]); trie.delete('foo'); + assertTernarySearchTree(trie, ['foobar', 2], ['bar', 3]); trie.delete('foobar'); + assertTernarySearchTree(trie, ['bar', 3]); + + // superstr-delete + trie = new TernarySearchTree(new StringIterator()); + trie.set('foo', 1); + trie.set('foobar', 2); + trie.set('bar', 3); + trie.deleteSuperstr('foo'); + assertTernarySearchTree(trie, ['bar', 3]); + + trie = new TernarySearchTree(new StringIterator()); + trie.set('foo', 1); + trie.set('foobar', 2); + trie.set('bar', 3); + trie.deleteSuperstr('fo'); + assertTernarySearchTree(trie, ['bar', 3]); + + // trie = new TernarySearchTree(new StringIterator()); + // trie.set('foo', 1); + // trie.set('foobar', 2); + // trie.set('bar', 3); + // trie.deleteSuperStr('f'); + // assertTernarySearchTree(trie, ['bar', 3]); }); test('TernarySearchTree (PathSegments) - basics', function () { - let trie = new TernarySearchTree(new PathIterator()); + let trie = new TernarySearchTree(new PathIterator()); trie.set('/user/foo/bar', 1); trie.set('/user/foo', 2); @@ -443,7 +592,7 @@ suite('Map', () => { test('TernarySearchTree (PathSegments) - lookup', function () { - const map = new TernarySearchTree(new PathIterator()); + const map = new TernarySearchTree(new PathIterator()); map.set('/user/foo/bar', 1); map.set('/user/foo', 2); map.set('/user/foo/flip/flop', 3); @@ -457,7 +606,7 @@ suite('Map', () => { test('TernarySearchTree (PathSegments) - superstr', function () { - const map = new TernarySearchTree(new PathIterator()); + const map = new TernarySearchTree(new PathIterator()); map.set('/user/foo/bar', 1); map.set('/user/foo', 2); map.set('/user/foo/flip/flop', 3); @@ -494,6 +643,135 @@ suite('Map', () => { assert.equal(map.findSuperstr('/userr'), undefined); }); + + test('TernarySearchTree (PathSegments) - delete_superstr', function () { + + const map = new TernarySearchTree(new PathIterator()); + map.set('/user/foo/bar', 1); + map.set('/user/foo', 2); + map.set('/user/foo/flip/flop', 3); + map.set('/usr/foo', 4); + + assertTernarySearchTree(map, + ['/user/foo/bar', 1], + ['/user/foo', 2], + ['/user/foo/flip/flop', 3], + ['/usr/foo', 4], + ); + + // not a segment + map.deleteSuperstr('/user/fo'); + assertTernarySearchTree(map, + ['/user/foo/bar', 1], + ['/user/foo', 2], + ['/user/foo/flip/flop', 3], + ['/usr/foo', 4], + ); + + // delete a segment + map.set('/user/foo/bar', 1); + map.set('/user/foo', 2); + map.set('/user/foo/flip/flop', 3); + map.set('/usr/foo', 4); + map.deleteSuperstr('/user/foo'); + assertTernarySearchTree(map, + ['/usr/foo', 4], + ); + }); + + test('TernarySearchTree (URI) - basics', function () { + let trie = new TernarySearchTree(new UriIterator(false)); + + trie.set(URI.file('/user/foo/bar'), 1); + trie.set(URI.file('/user/foo'), 2); + trie.set(URI.file('/user/foo/flip/flop'), 3); + + assert.equal(trie.get(URI.file('/user/foo/bar')), 1); + assert.equal(trie.get(URI.file('/user/foo')), 2); + assert.equal(trie.get(URI.file('/user/foo/flip/flop')), 3); + + assert.equal(trie.findSubstr(URI.file('/user/bar')), undefined); + assert.equal(trie.findSubstr(URI.file('/user/foo')), 2); + assert.equal(trie.findSubstr(URI.file('/user/foo/ba')), 2); + assert.equal(trie.findSubstr(URI.file('/user/foo/far/boo')), 2); + assert.equal(trie.findSubstr(URI.file('/user/foo/bar')), 1); + assert.equal(trie.findSubstr(URI.file('/user/foo/bar/far/boo')), 1); + }); + + test('TernarySearchTree (URI) - lookup', function () { + + const map = new TernarySearchTree(new UriIterator(false)); + map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); + map.set(URI.parse('http://foo.bar/user/foo?query'), 2); + map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3); + map.set(URI.parse('http://foo.bar/user/foo/flip/flop'), 3); + + assert.equal(map.get(URI.parse('http://foo.bar/foo')), undefined); + assert.equal(map.get(URI.parse('http://foo.bar/user')), undefined); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar')), 1); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo?query')), 2); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo?Query')), undefined); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo?QUERY')), 3); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar/boo')), undefined); + }); + + test('TernarySearchTree (PathSegments) - superstr', function () { + + const map = new TernarySearchTree(new UriIterator(false)); + map.set(URI.file('/user/foo/bar'), 1); + map.set(URI.file('/user/foo'), 2); + map.set(URI.file('/user/foo/flip/flop'), 3); + map.set(URI.file('/usr/foo'), 4); + + let item: IteratorResult; + let iter = map.findSuperstr(URI.file('/user'))!; + + item = iter.next(); + assert.equal(item.value, 2); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 1); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 3); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + iter = map.findSuperstr(URI.file('/usr'))!; + item = iter.next(); + assert.equal(item.value, 4); + assert.equal(item.done, false); + + item = iter.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + iter = map.findSuperstr(URI.file('/'))!; + item = iter.next(); + assert.equal(item.value, 2); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 1); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 3); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 4); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + assert.equal(map.findSuperstr(URI.file('/not')), undefined); + assert.equal(map.findSuperstr(URI.file('/us')), undefined); + assert.equal(map.findSuperstr(URI.file('/usrr')), undefined); + assert.equal(map.findSuperstr(URI.file('/userr')), undefined); + }); + + test('ResourceMap - basics', function () { const map = new ResourceMap(); @@ -506,18 +784,21 @@ suite('Map', () => { assert.equal(map.size, 0); - map.set(resource1, 1); + let res = map.set(resource1, 1); + assert.ok(res === map); map.set(resource2, '2'); map.set(resource3, true); - const values = map.values(); + const values = [...map.values()]; assert.equal(values[0], 1); assert.equal(values[1], '2'); assert.equal(values[2], true); let counter = 0; - map.forEach(value => { + map.forEach((value, key, mapObj) => { assert.equal(value, values[counter++]); + assert.ok(URI.isUri(key)); + assert.ok(map === mapObj); }); const obj = Object.create(null); @@ -602,45 +883,32 @@ suite('Map', () => { assert.equal(map.get(uncFile), 'true'); }); - // test('ResourceMap - files (ignorecase)', function () { - // const map = new ResourceMap(true); + test('ResourceMap - files (ignorecase)', function () { + const map = new ResourceMap(uri => extUriIgnorePathCase.getComparisonKey(uri)); - // const fileA = URI.parse('file://some/filea'); - // const fileB = URI.parse('some://some/other/fileb'); - // const fileAUpper = URI.parse('file://SOME/FILEA'); + const fileA = URI.parse('file://some/filea'); + const fileB = URI.parse('some://some/other/fileb'); + const fileAUpper = URI.parse('file://SOME/FILEA'); - // map.set(fileA, 'true'); - // assert.equal(map.get(fileA), 'true'); + map.set(fileA, 'true'); + assert.equal(map.get(fileA), 'true'); - // assert.equal(map.get(fileAUpper), 'true'); + assert.equal(map.get(fileAUpper), 'true'); - // assert.ok(!map.get(fileB)); + assert.ok(!map.get(fileB)); - // map.set(fileAUpper, 'false'); - // assert.equal(map.get(fileAUpper), 'false'); + map.set(fileAUpper, 'false'); + assert.equal(map.get(fileAUpper), 'false'); - // assert.equal(map.get(fileA), 'false'); + assert.equal(map.get(fileA), 'false'); - // const windowsFile = URI.file('c:\\test with %25\\c#code'); - // const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); + const windowsFile = URI.file('c:\\test with %25\\c#code'); + const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); - // map.set(windowsFile, 'true'); - // map.set(uncFile, 'true'); + map.set(windowsFile, 'true'); + map.set(uncFile, 'true'); - // assert.equal(map.get(windowsFile), 'true'); - // assert.equal(map.get(uncFile), 'true'); - // }); - - test('mapToSerializable / serializableToMap', function () { - const map = new Map(); - map.set('1', 'foo'); - map.set('2', null!); - map.set('3', 'bar'); - - const map2 = serializableToMap(mapToSerializable(map)); - assert.equal(map2.size, map.size); - assert.equal(map2.get('1'), map.get('1')); - assert.equal(map2.get('2'), map.get('2')); - assert.equal(map2.get('3'), map.get('3')); + assert.equal(map.get(windowsFile), 'true'); + assert.equal(map.get(uncFile), 'true'); }); }); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index f33f741f624..a76c30f2cc5 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -8,6 +8,18 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; suite('MarkdownString', () => { + test('Escape leading whitespace', function () { + const mds = new MarkdownString(); + mds.appendText('Hello\n Not a code block'); + assert.equal(mds.value, 'Hello\n\n    Not a code block'); + }); + + test('MarkdownString.appendText doesn\'t escape quote #109040', function () { + const mds = new MarkdownString(); + mds.appendText('> Text\n>More'); + assert.equal(mds.value, '\\> Text\n\n\\>More'); + }); + test('appendText', () => { const mds = new MarkdownString(); diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index 3d163580a6d..a7ac5a177a6 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime'; +import { guessMimeTypes, registerTextMime } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; suite('Mime', () => { @@ -126,53 +126,4 @@ suite('Mime', () => { assert.deepEqual(guessMimeTypes(URI.parse(`data:;label:something.data;description:data,`)), ['text/data', 'text/plain']); }); - - test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => { - const id = 'plumbus0'; - const mime = `text/${id}`; - for (let extension of ['one', 'two']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('shleem', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1'); - }); - - test('Filename Suggestion - Suggest prefix with first extension that begins with a dot', () => { - const id = 'plumbus1'; - const mime = `text/${id}`; - for (let extension of ['plumbus', '.shleem', '.gazorpazorp']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('plumbus1', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1.shleem'); - }); - - test('Filename Suggestion - Suggest first relevant extension when there are none that begin with a dot', () => { - const id = 'plumbus2'; - const mime = `text/${id}`; - for (let extension of ['plumbus', 'shleem', 'gazorpazorp']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('plumbus2', 'Untitled-1'); - assert.equal(suggested, 'plumbus'); - }); - - test('Filename Suggestion - Should ignore user-configured associations', () => { - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: 'plumbus', userConfigured: true }); - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.shleem', userConfigured: true }); - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.gazorpazorp', userConfigured: false }); - - let suggested = suggestFilename('plumbus3', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1.gazorpazorp'); - - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: 'plumbus', userConfigured: true }); - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.shleem', userConfigured: true }); - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.gazorpazorp', userConfigured: true }); - - suggested = suggestFilename('plumbus4', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1'); - }); }); diff --git a/src/vs/workbench/test/electron-browser/api/mock.ts b/src/vs/base/test/common/mock.ts similarity index 100% rename from src/vs/workbench/test/electron-browser/api/mock.ts rename to src/vs/base/test/common/mock.ts diff --git a/src/vs/base/test/common/normalization.test.ts b/src/vs/base/test/common/normalization.test.ts new file mode 100644 index 00000000000..aebe84a0c6e --- /dev/null +++ b/src/vs/base/test/common/normalization.test.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { removeAccents } from 'vs/base/common/normalization'; + +suite('Normalization', () => { + + test('removeAccents', function () { + assert.equal(removeAccents('joào'), 'joao'); + assert.equal(removeAccents('joáo'), 'joao'); + assert.equal(removeAccents('joâo'), 'joao'); + assert.equal(removeAccents('joäo'), 'joao'); + // assert.equal(strings.removeAccents('joæo'), 'joao'); // not an accent + assert.equal(removeAccents('joão'), 'joao'); + assert.equal(removeAccents('joåo'), 'joao'); + assert.equal(removeAccents('joåo'), 'joao'); + assert.equal(removeAccents('joāo'), 'joao'); + + assert.equal(removeAccents('fôo'), 'foo'); + assert.equal(removeAccents('föo'), 'foo'); + assert.equal(removeAccents('fòo'), 'foo'); + assert.equal(removeAccents('fóo'), 'foo'); + // assert.equal(strings.removeAccents('fœo'), 'foo'); + // assert.equal(strings.removeAccents('føo'), 'foo'); + assert.equal(removeAccents('fōo'), 'foo'); + assert.equal(removeAccents('fõo'), 'foo'); + + assert.equal(removeAccents('andrè'), 'andre'); + assert.equal(removeAccents('andré'), 'andre'); + assert.equal(removeAccents('andrê'), 'andre'); + assert.equal(removeAccents('andrë'), 'andre'); + assert.equal(removeAccents('andrē'), 'andre'); + assert.equal(removeAccents('andrė'), 'andre'); + assert.equal(removeAccents('andrę'), 'andre'); + + assert.equal(removeAccents('hvîc'), 'hvic'); + assert.equal(removeAccents('hvïc'), 'hvic'); + assert.equal(removeAccents('hvíc'), 'hvic'); + assert.equal(removeAccents('hvīc'), 'hvic'); + assert.equal(removeAccents('hvįc'), 'hvic'); + assert.equal(removeAccents('hvìc'), 'hvic'); + + assert.equal(removeAccents('ûdo'), 'udo'); + assert.equal(removeAccents('üdo'), 'udo'); + assert.equal(removeAccents('ùdo'), 'udo'); + assert.equal(removeAccents('údo'), 'udo'); + assert.equal(removeAccents('ūdo'), 'udo'); + + assert.equal(removeAccents('heÿ'), 'hey'); + + // assert.equal(strings.removeAccents('gruß'), 'grus'); + assert.equal(removeAccents('gruś'), 'grus'); + assert.equal(removeAccents('gruš'), 'grus'); + + assert.equal(removeAccents('çool'), 'cool'); + assert.equal(removeAccents('ćool'), 'cool'); + assert.equal(removeAccents('čool'), 'cool'); + + assert.equal(removeAccents('ñice'), 'nice'); + assert.equal(removeAccents('ńice'), 'nice'); + }); +}); diff --git a/src/vs/base/test/common/objects.test.ts b/src/vs/base/test/common/objects.test.ts index 37e428d8a86..38e2954a2ee 100644 --- a/src/vs/base/test/common/objects.test.ts +++ b/src/vs/base/test/common/objects.test.ts @@ -212,4 +212,17 @@ suite('Objects', () => { diff = objects.distinct(base, obj); assert.deepEqual(diff, obj); }); -}); \ No newline at end of file + + test('getCaseInsensitive', () => { + const obj1 = { + lowercase: 123, + mIxEdCaSe: 456 + }; + + assert.equal(obj1.lowercase, objects.getCaseInsensitive(obj1, 'lowercase')); + assert.equal(obj1.lowercase, objects.getCaseInsensitive(obj1, 'lOwErCaSe')); + + assert.equal(obj1.mIxEdCaSe, objects.getCaseInsensitive(obj1, 'MIXEDCASE')); + assert.equal(obj1.mIxEdCaSe, objects.getCaseInsensitive(obj1, 'mixedcase')); + }); +}); diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts index cd54b6fd724..ee0d25c88df 100644 --- a/src/vs/base/test/common/processes.test.ts +++ b/src/vs/base/test/common/processes.test.ts @@ -19,12 +19,13 @@ suite('Processes', () => { VSCODE_CLI: 'x', VSCODE_DEV: 'x', VSCODE_IPC_HOOK: 'x', - VSCODE_LOGS: 'x', VSCODE_NLS_CONFIG: 'x', VSCODE_PORTABLE: 'x', VSCODE_PID: 'x', VSCODE_NODE_CACHED_DATA_DIR: 'x', - VSCODE_NEW_VAR: 'x' + VSCODE_NEW_VAR: 'x', + GDK_PIXBUF_MODULE_FILE: 'x', + GDK_PIXBUF_MODULEDIR: 'x', }; processes.sanitizeProcessEnvironment(env); assert.equal(env['FOO'], 'bar'); diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 5fdf1ba1088..8026e240bba 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator } from 'vs/base/common/resources'; +import { dirname, basename, distinctParents, joinPath, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator, extUri, extUriIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { toSlashes } from 'vs/base/common/extpath'; -import { startsWith } from 'vs/base/common/strings'; import { win32, posix } from 'vs/base/common/path'; @@ -64,8 +63,10 @@ suite('Resources', () => { assert.equal(dirname(URI.parse('foo://a/')).toString(), 'foo://a/'); assert.equal(dirname(URI.parse('foo://a')).toString(), 'foo://a'); - // does not explode (https://github.com/Microsoft/vscode/issues/41987) + // does not explode (https://github.com/microsoft/vscode/issues/41987) dirname(URI.from({ scheme: 'file', authority: '/users/someone/portal.h' })); + + assert.equal(dirname(URI.parse('foo://a/b/c?q')).toString(), 'foo://a/b?q'); }); test('basename', () => { @@ -108,6 +109,7 @@ suite('Resources', () => { assert.equal(joinPath(URI.file('/foo/bar'), '/./file.js').toString(), 'file:///foo/bar/file.js'); assert.equal(joinPath(URI.file('/foo/bar'), '../file.js').toString(), 'file:///foo/file.js'); } + assert.equal(joinPath(URI.parse('foo://a/foo/bar')).toString(), 'foo://a/foo/bar'); assert.equal(joinPath(URI.parse('foo://a/foo/bar'), '/file.js').toString(), 'foo://a/foo/bar/file.js'); assert.equal(joinPath(URI.parse('foo://a/foo/bar'), 'file.js').toString(), 'foo://a/foo/bar/file.js'); assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '/file.js').toString(), 'foo://a/foo/bar/file.js'); @@ -155,6 +157,7 @@ suite('Resources', () => { assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../some/../bar')).toString(), 'foo://a/foo/bar'); assert.equal(normalizePath(URI.parse('foo://a')).toString(), 'foo://a'); assert.equal(normalizePath(URI.parse('foo://a/')).toString(), 'foo://a/'); + assert.equal(normalizePath(URI.parse('foo://a/foo/./bar?q=1')).toString(), URI.parse('foo://a/foo/bar?q%3D1').toString()); }); test('isAbsolute', () => { @@ -231,16 +234,19 @@ suite('Resources', () => { } }); - function assertEqualURI(actual: URI, expected: URI, message?: string) { - if (!isEqual(expected, actual)) { + function assertEqualURI(actual: URI, expected: URI, message?: string, ignoreCase?: boolean) { + let util = ignoreCase ? extUriIgnorePathCase : extUri; + if (!util.isEqual(expected, actual)) { assert.equal(actual.toString(), expected.toString(), message); } } function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean, ignoreCase?: boolean) { - assert.equal(relativePath(u1, u2, ignoreCase), expectedPath, `from ${u1.toString()} to ${u2.toString()}`); + let util = ignoreCase ? extUriIgnorePathCase : extUri; + + assert.equal(util.relativePath(u1, u2), expectedPath, `from ${u1.toString()} to ${u2.toString()}`); if (expectedPath !== undefined && !ignoreJoin) { - assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal'); + assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal', ignoreCase); } } @@ -250,15 +256,15 @@ suite('Resources', () => { assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar/goo'), 'bar/goo'); assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a/foo/bar/goo'), 'foo/bar/goo'); assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://a/foo/bar'), '../bar'); - assertRelativePath(URI.parse('foo://a/foo/xoo/yoo'), URI.parse('foo://a'), '../../..'); + assertRelativePath(URI.parse('foo://a/foo/xoo/yoo'), URI.parse('foo://a'), '../../..', true); assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/'), ''); assertRelativePath(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo'), ''); assertRelativePath(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo/'), ''); assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo'), ''); - assertRelativePath(URI.parse('foo://a'), URI.parse('foo://a'), ''); + assertRelativePath(URI.parse('foo://a'), URI.parse('foo://a'), '', true); assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a/'), ''); - assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a'), ''); - assertRelativePath(URI.parse('foo://a/foo?q'), URI.parse('foo://a/foo/bar#h'), 'bar'); + assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a'), '', true); + assertRelativePath(URI.parse('foo://a/foo?q'), URI.parse('foo://a/foo/bar#h'), 'bar', true); assertRelativePath(URI.parse('foo://'), URI.parse('foo://a/b'), undefined); assertRelativePath(URI.parse('foo://a2/b'), URI.parse('foo://a/b'), undefined); assertRelativePath(URI.parse('goo://a/b'), URI.parse('foo://a/b'), undefined); @@ -297,7 +303,7 @@ suite('Resources', () => { const p = path.indexOf('/') !== -1 ? posix : win32; if (!p.isAbsolute(path)) { let expectedPath = isWindows ? toSlashes(path) : path; - expectedPath = startsWith(expectedPath, './') ? expectedPath.substr(2) : expectedPath; + expectedPath = expectedPath.startsWith('./') ? expectedPath.substr(2) : expectedPath; assert.equal(relativePath(u1, actual), expectedPath, `relativePath (${u1.toString()}) on actual (${actual.toString()}) should be to path (${expectedPath})`); } } @@ -342,50 +348,85 @@ suite('Resources', () => { }); + function assertIsEqual(u1: URI, u2: URI, ignoreCase: boolean | undefined, expected: boolean) { + + let util = ignoreCase ? extUriIgnorePathCase : extUri; + + assert.equal(util.isEqual(u1, u2), expected, `${u1.toString()}${expected ? '===' : '!=='}${u2.toString()}`); + assert.equal(util.compare(u1, u2) === 0, expected); + assert.equal(util.getComparisonKey(u1) === util.getComparisonKey(u2), expected, `comparison keys ${u1.toString()}, ${u2.toString()}`); + assert.equal(util.isEqualOrParent(u1, u2), expected, `isEqualOrParent ${u1.toString()}, ${u2.toString()}`); + if (!ignoreCase) { + assert.equal(u1.toString() === u2.toString(), expected); + } + } + + test('isEqual', () => { let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); let fileURI2 = isWindows ? URI.file('C:\\foo\\Bar') : URI.file('/foo/Bar'); - assert.equal(isEqual(fileURI, fileURI, true), true); - assert.equal(isEqual(fileURI, fileURI, false), true); - assert.equal(isEqual(fileURI, fileURI, hasToIgnoreCase(fileURI)), true); - assert.equal(isEqual(fileURI, fileURI2, true), true); - assert.equal(isEqual(fileURI, fileURI2, false), false); + assertIsEqual(fileURI, fileURI, true, true); + assertIsEqual(fileURI, fileURI, false, true); + assertIsEqual(fileURI, fileURI, undefined, true); + assertIsEqual(fileURI, fileURI2, true, true); + assertIsEqual(fileURI, fileURI2, false, false); let fileURI3 = URI.parse('foo://server:453/foo/bar'); let fileURI4 = URI.parse('foo://server:453/foo/Bar'); - assert.equal(isEqual(fileURI3, fileURI3, true), true); - assert.equal(isEqual(fileURI3, fileURI3, false), true); - assert.equal(isEqual(fileURI3, fileURI3, hasToIgnoreCase(fileURI3)), true); - assert.equal(isEqual(fileURI3, fileURI4, true), true); - assert.equal(isEqual(fileURI3, fileURI4, false), false); + assertIsEqual(fileURI3, fileURI3, true, true); + assertIsEqual(fileURI3, fileURI3, false, true); + assertIsEqual(fileURI3, fileURI3, undefined, true); + assertIsEqual(fileURI3, fileURI4, true, true); + assertIsEqual(fileURI3, fileURI4, false, false); - assert.equal(isEqual(fileURI, fileURI3, true), false); + assertIsEqual(fileURI, fileURI3, true, false); - assert.equal(isEqual(URI.parse('foo://server'), URI.parse('foo://server/')), true); + assertIsEqual(URI.parse('file://server'), URI.parse('file://server/'), true, true); + assertIsEqual(URI.parse('http://server'), URI.parse('http://server/'), true, true); + assertIsEqual(URI.parse('foo://server'), URI.parse('foo://server/'), true, false); // only selected scheme have / as the default path + assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo/'), true, false); + assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo?'), true, true); + + let fileURI5 = URI.parse('foo://server:453/foo/bar?q=1'); + let fileURI6 = URI.parse('foo://server:453/foo/bar#xy'); + + assertIsEqual(fileURI5, fileURI5, true, true); + assertIsEqual(fileURI5, fileURI3, true, false); + assertIsEqual(fileURI6, fileURI6, true, true); + assertIsEqual(fileURI6, fileURI5, true, false); + assertIsEqual(fileURI6, fileURI3, true, false); }); test('isEqualOrParent', () => { + let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); let fileURI2 = isWindows ? URI.file('c:\\foo') : URI.file('/foo'); let fileURI2b = isWindows ? URI.file('C:\\Foo\\') : URI.file('/Foo/'); - assert.equal(isEqualOrParent(fileURI, fileURI, true), true, '1'); - assert.equal(isEqualOrParent(fileURI, fileURI, false), true, '2'); - assert.equal(isEqualOrParent(fileURI, fileURI2, true), true, '3'); - assert.equal(isEqualOrParent(fileURI, fileURI2, false), true, '4'); - assert.equal(isEqualOrParent(fileURI, fileURI2b, true), true, '5'); - assert.equal(isEqualOrParent(fileURI, fileURI2b, false), false, '6'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI), true, '1'); + assert.equal(extUri.isEqualOrParent(fileURI, fileURI), true, '2'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2), true, '3'); + assert.equal(extUri.isEqualOrParent(fileURI, fileURI2), true, '4'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2b), true, '5'); + assert.equal(extUri.isEqualOrParent(fileURI, fileURI2b), false, '6'); - assert.equal(isEqualOrParent(fileURI2, fileURI, false), false, '7'); - assert.equal(isEqualOrParent(fileURI2b, fileURI2, true), true, '8'); + assert.equal(extUri.isEqualOrParent(fileURI2, fileURI), false, '7'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI2b, fileURI2), true, '8'); let fileURI3 = URI.parse('foo://server:453/foo/bar/goo'); let fileURI4 = URI.parse('foo://server:453/foo/'); let fileURI5 = URI.parse('foo://server:453/foo'); - assert.equal(isEqualOrParent(fileURI3, fileURI3, true), true, '11'); - assert.equal(isEqualOrParent(fileURI3, fileURI3, false), true, '12'); - assert.equal(isEqualOrParent(fileURI3, fileURI4, true), true, '13'); - assert.equal(isEqualOrParent(fileURI3, fileURI4, false), true, '14'); - assert.equal(isEqualOrParent(fileURI3, fileURI, true), false, '15'); - assert.equal(isEqualOrParent(fileURI5, fileURI5, true), true, '16'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI3, true), true, '11'); + assert.equal(extUri.isEqualOrParent(fileURI3, fileURI3), true, '12'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI4, true), true, '13'); + assert.equal(extUri.isEqualOrParent(fileURI3, fileURI4), true, '14'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI, true), false, '15'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI5, fileURI5, true), true, '16'); + + let fileURI6 = URI.parse('foo://server:453/foo?q=1'); + let fileURI7 = URI.parse('foo://server:453/foo/bar?q=1'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI5), false, '17'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI6), true, '18'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI6), true, '19'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI5), false, '20'); }); }); diff --git a/src/vs/base/test/common/skipList.test.ts b/src/vs/base/test/common/skipList.test.ts new file mode 100644 index 00000000000..f6a083e3cb2 --- /dev/null +++ b/src/vs/base/test/common/skipList.test.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { SkipList } from 'vs/base/common/skipList'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { binarySearch } from 'vs/base/common/arrays'; + + +suite('SkipList', function () { + + function assertValues(list: SkipList, expected: V[]) { + assert.equal(list.size, expected.length); + assert.deepEqual([...list.values()], expected); + + let valuesFromEntries = [...list.entries()].map(entry => entry[1]); + assert.deepEqual(valuesFromEntries, expected); + + let valuesFromIter = [...list].map(entry => entry[1]); + assert.deepEqual(valuesFromIter, expected); + + let i = 0; + list.forEach((value, _key, map) => { + assert.ok(map === list); + assert.deepEqual(value, expected[i++]); + }); + } + + function assertKeys(list: SkipList, expected: K[]) { + assert.equal(list.size, expected.length); + assert.deepEqual([...list.keys()], expected); + + let keysFromEntries = [...list.entries()].map(entry => entry[0]); + assert.deepEqual(keysFromEntries, expected); + + let keysFromIter = [...list].map(entry => entry[0]); + assert.deepEqual(keysFromIter, expected); + + let i = 0; + list.forEach((_value, key, map) => { + assert.ok(map === list); + assert.deepEqual(key, expected[i++]); + }); + } + + test('set/get/delete', function () { + let list = new SkipList((a, b) => a - b); + + assert.equal(list.get(3), undefined); + list.set(3, 1); + assert.equal(list.get(3), 1); + assertValues(list, [1]); + + list.set(3, 3); + assertValues(list, [3]); + + list.set(1, 1); + list.set(4, 4); + assert.equal(list.get(3), 3); + assert.equal(list.get(1), 1); + assert.equal(list.get(4), 4); + assertValues(list, [1, 3, 4]); + + assert.equal(list.delete(17), false); + + assert.equal(list.delete(1), true); + assert.equal(list.get(1), undefined); + assert.equal(list.get(3), 3); + assert.equal(list.get(4), 4); + + assertValues(list, [3, 4]); + }); + + test('Figure 3', function () { + let list = new SkipList((a, b) => a - b); + list.set(3, true); + list.set(6, true); + list.set(7, true); + list.set(9, true); + list.set(12, true); + list.set(19, true); + list.set(21, true); + list.set(25, true); + + assertKeys(list, [3, 6, 7, 9, 12, 19, 21, 25]); + + list.set(17, true); + assert.deepEqual(list.size, 9); + assertKeys(list, [3, 6, 7, 9, 12, 17, 19, 21, 25]); + }); + + test('capacity max', function () { + let list = new SkipList((a, b) => a - b, 10); + list.set(1, true); + list.set(2, true); + list.set(3, true); + list.set(4, true); + list.set(5, true); + list.set(6, true); + list.set(7, true); + list.set(8, true); + list.set(9, true); + list.set(10, true); + list.set(11, true); + list.set(12, true); + + assertKeys(list, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + }); + + const cmp = (a: number, b: number): number => { + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } + }; + + function insertArraySorted(array: number[], element: number) { + let idx = binarySearch(array, element, cmp); + if (idx >= 0) { + array[idx] = element; + } else { + idx = ~idx; + // array = array.slice(0, idx).concat(element, array.slice(idx)); + array.splice(idx, 0, element); + } + return array; + } + + function delArraySorted(array: number[], element: number) { + let idx = binarySearch(array, element, cmp); + if (idx >= 0) { + // array = array.slice(0, idx).concat(array.slice(idx)); + array.splice(idx, 1); + } + return array; + } + + + test('perf', function () { + this.skip(); + + // data + const max = 2 ** 16; + const values = new Set(); + for (let i = 0; i < max; i++) { + let value = Math.floor(Math.random() * max); + values.add(value); + } + console.log(values.size); + + // init + let list = new SkipList(cmp, max); + let sw = new StopWatch(true); + values.forEach(value => list.set(value, true)); + sw.stop(); + console.log(`[LIST] ${list.size} elements after ${sw.elapsed()}ms`); + let array: number[] = []; + sw = new StopWatch(true); + values.forEach(value => array = insertArraySorted(array, value)); + sw.stop(); + console.log(`[ARRAY] ${array.length} elements after ${sw.elapsed()}ms`); + + // get + sw = new StopWatch(true); + let someValues = [...values].slice(0, values.size / 4); + someValues.forEach(key => { + let value = list.get(key); // find + console.assert(value, '[LIST] must have ' + key); + list.get(-key); // miss + }); + sw.stop(); + console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + sw = new StopWatch(true); + someValues.forEach(key => { + let idx = binarySearch(array, key, cmp); // find + console.assert(idx >= 0, '[ARRAY] must have ' + key); + binarySearch(array, -key, cmp); // miss + }); + sw.stop(); + console.log(`[ARRAY] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + + + // insert + sw = new StopWatch(true); + someValues.forEach(key => { + list.set(-key, false); + }); + sw.stop(); + console.log(`[LIST] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`); + sw = new StopWatch(true); + someValues.forEach(key => { + array = insertArraySorted(array, -key); + }); + sw.stop(); + console.log(`[ARRAY] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`); + + // delete + sw = new StopWatch(true); + someValues.forEach(key => { + list.delete(key); // find + list.delete(-key); // miss + }); + sw.stop(); + console.log(`[LIST] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + sw = new StopWatch(true); + someValues.forEach(key => { + array = delArraySorted(array, key); // find + array = delArraySorted(array, -key); // miss + }); + sw.stop(); + console.log(`[ARRAY] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + }); +}); diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index 6d86fbc2eb2..0ef3b7730e9 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isReadableStream, newWriteableStream, Readable, consumeReadable, consumeReadableWithLimit, consumeStream, ReadableStream, toStream, toReadable, transform, consumeStreamWithLimit } from 'vs/base/common/stream'; +import { isReadableStream, newWriteableStream, Readable, consumeReadable, peekReadable, consumeStream, ReadableStream, toStream, toReadable, transform, peekStream, isReadableBufferedStream } from 'vs/base/common/stream'; +import { timeout } from 'vs/base/common/async'; suite('Stream', () => { @@ -13,7 +14,16 @@ suite('Stream', () => { assert.ok(isReadableStream(newWriteableStream(d => d))); }); - test('WriteableStream', () => { + test('isReadableBufferedStream', async () => { + assert.ok(!isReadableBufferedStream(Object.create(null))); + + const stream = newWriteableStream(d => d); + stream.end(); + const bufferedStream = await peekStream(stream, 1); + assert.ok(isReadableBufferedStream(bufferedStream)); + }); + + test('WriteableStream - basics', () => { const stream = newWriteableStream(strings => strings.join()); let error = false; @@ -66,17 +76,92 @@ suite('Stream', () => { assert.equal(chunks.length, 4); }); + test('WriteableStream - removeListener', () => { + const stream = newWriteableStream(strings => strings.join()); + + let error = false; + const errorListener = (e: Error) => { + error = true; + }; + stream.on('error', errorListener); + + let data = false; + const dataListener = () => { + data = true; + }; + stream.on('data', dataListener); + + stream.write('Hello'); + assert.equal(data, true); + + data = false; + stream.removeListener('data', dataListener); + + stream.write('World'); + assert.equal(data, false); + + stream.error(new Error()); + assert.equal(error, true); + + error = false; + stream.removeListener('error', errorListener); + + stream.error(new Error()); + assert.equal(error, false); + }); + + test('WriteableStream - highWaterMark', async () => { + const stream = newWriteableStream(strings => strings.join(), { highWaterMark: 3 }); + + let res = stream.write('1'); + assert.ok(!res); + + res = stream.write('2'); + assert.ok(!res); + + res = stream.write('3'); + assert.ok(!res); + + let promise1 = stream.write('4'); + assert.ok(promise1 instanceof Promise); + + let promise2 = stream.write('5'); + assert.ok(promise2 instanceof Promise); + + let drained1 = false; + (async () => { + await promise1; + drained1 = true; + })(); + + let drained2 = false; + (async () => { + await promise2; + drained2 = true; + })(); + + let data: string | undefined = undefined; + stream.on('data', chunk => { + data = chunk; + }); + assert.ok(data); + + await timeout(0); + assert.equal(drained1, true); + assert.equal(drained2, true); + }); + test('consumeReadable', () => { const readable = arrayToReadable(['1', '2', '3', '4', '5']); const consumed = consumeReadable(readable, strings => strings.join()); assert.equal(consumed, '1,2,3,4,5'); }); - test('consumeReadableWithLimit', () => { + test('peekReadable', () => { for (let i = 0; i < 5; i++) { const readable = arrayToReadable(['1', '2', '3', '4', '5']); - const consumedOrReadable = consumeReadableWithLimit(readable, strings => strings.join(), i); + const consumedOrReadable = peekReadable(readable, strings => strings.join(), i); if (typeof consumedOrReadable === 'string') { assert.fail('Unexpected result'); } else { @@ -86,14 +171,75 @@ suite('Stream', () => { } let readable = arrayToReadable(['1', '2', '3', '4', '5']); - let consumedOrReadable = consumeReadableWithLimit(readable, strings => strings.join(), 5); + let consumedOrReadable = peekReadable(readable, strings => strings.join(), 5); assert.equal(consumedOrReadable, '1,2,3,4,5'); readable = arrayToReadable(['1', '2', '3', '4', '5']); - consumedOrReadable = consumeReadableWithLimit(readable, strings => strings.join(), 6); + consumedOrReadable = peekReadable(readable, strings => strings.join(), 6); assert.equal(consumedOrReadable, '1,2,3,4,5'); }); + test('peekReadable - error handling', async () => { + + // 0 Chunks + let stream = newWriteableStream(data => data); + + let error: Error | undefined = undefined; + let promise = (async () => { + try { + await peekStream(stream, 1); + } catch (err) { + error = err; + } + })(); + + stream.error(new Error()); + await promise; + + assert.ok(error); + + // 1 Chunk + stream = newWriteableStream(data => data); + + error = undefined; + promise = (async () => { + try { + await peekStream(stream, 1); + } catch (err) { + error = err; + } + })(); + + stream.write('foo'); + stream.error(new Error()); + await promise; + + assert.ok(error); + + // 2 Chunks + stream = newWriteableStream(data => data); + + error = undefined; + promise = (async () => { + try { + await peekStream(stream, 1); + } catch (err) { + error = err; + } + })(); + + stream.write('foo'); + stream.write('bar'); + stream.error(new Error()); + await promise; + + assert.ok(!error); + + stream.on('error', err => error = err); + stream.on('data', chunk => { }); + assert.ok(error); + }); + function arrayToReadable(array: T[]): Readable { return { read: () => array.shift() || null @@ -122,26 +268,39 @@ suite('Stream', () => { assert.equal(consumed, '1,2,3,4,5'); }); - test('consumeStreamWithLimit', async () => { + test('peekStream', async () => { for (let i = 0; i < 5; i++) { - const readable = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); + const stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); - const consumedOrStream = await consumeStreamWithLimit(readable, strings => strings.join(), i); - if (typeof consumedOrStream === 'string') { - assert.fail('Unexpected result'); + const result = await peekStream(stream, i); + assert.equal(stream, result.stream); + if (result.ended) { + assert.fail('Unexpected result, stream should not have ended yet'); } else { - const consumed = await consumeStream(consumedOrStream, strings => strings.join()); - assert.equal(consumed, '1,2,3,4,5'); + assert.equal(result.buffer.length, i + 1, `maxChunks: ${i}`); + + const additionalResult: string[] = []; + await consumeStream(stream, strings => { + additionalResult.push(...strings); + + return strings.join(); + }); + + assert.equal([...result.buffer, ...additionalResult].join(), '1,2,3,4,5'); } } let stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); - let consumedOrStream = await consumeStreamWithLimit(stream, strings => strings.join(), 5); - assert.equal(consumedOrStream, '1,2,3,4,5'); + let result = await peekStream(stream, 5); + assert.equal(stream, result.stream); + assert.equal(result.buffer.join(), '1,2,3,4,5'); + assert.equal(result.ended, true); stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); - consumedOrStream = await consumeStreamWithLimit(stream, strings => strings.join(), 6); - assert.equal(consumedOrStream, '1,2,3,4,5'); + result = await peekStream(stream, 6); + assert.equal(stream, result.stream); + assert.equal(result.buffer.join(), '1,2,3,4,5'); + assert.equal(result.ended, true); }); test('toStream', async () => { diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 600df87cfca..543cb460bf2 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -81,6 +81,27 @@ suite('Strings', () => { assertCompareIgnoreCase('O', '/'); }); + test('compareIgnoreCase (substring)', () => { + + function assertCompareIgnoreCase(a: string, b: string, aStart: number, aEnd: number, bStart: number, bEnd: number, recurse = true): void { + let actual = strings.compareSubstringIgnoreCase(a, b, aStart, aEnd, bStart, bEnd); + actual = actual > 0 ? 1 : actual < 0 ? -1 : actual; + + let expected = strings.compare(a.toLowerCase().substring(aStart, aEnd), b.toLowerCase().substring(bStart, bEnd)); + expected = expected > 0 ? 1 : expected < 0 ? -1 : expected; + assert.equal(actual, expected, `${a} <> ${b}`); + + if (recurse) { + assertCompareIgnoreCase(b, a, bStart, bEnd, aStart, aEnd, false); + } + } + + assertCompareIgnoreCase('', '', 0, 0, 0, 0); + assertCompareIgnoreCase('abc', 'ABC', 0, 1, 0, 1); + assertCompareIgnoreCase('abc', 'Aabc', 0, 3, 1, 4); + assertCompareIgnoreCase('abcABc', 'ABcd', 3, 6, 0, 4); + }); + test('format', () => { assert.strictEqual(strings.format('Foo Bar'), 'Foo Bar'); assert.strictEqual(strings.format('Foo {0} Bar'), 'Foo {0} Bar'); @@ -92,15 +113,6 @@ suite('Strings', () => { assert.strictEqual(strings.format('Foo {0} Bar. {1}', '(foo)', '.test'), 'Foo (foo) Bar. .test'); }); - test('overlap', () => { - assert.equal(strings.overlap('foobar', 'arr, I am a priate'), 2); - assert.equal(strings.overlap('no', 'overlap'), 1); - assert.equal(strings.overlap('no', '0verlap'), 0); - assert.equal(strings.overlap('nothing', ''), 0); - assert.equal(strings.overlap('', 'nothing'), 0); - assert.equal(strings.overlap('full', 'full'), 4); - assert.equal(strings.overlap('full', 'fulloverlap'), 4); - }); test('lcut', () => { assert.strictEqual(strings.lcut('foo bar', 0), ''); assert.strictEqual(strings.lcut('foo bar', 1), 'bar'); @@ -113,13 +125,6 @@ suite('Strings', () => { assert.strictEqual(strings.lcut('a', 10), 'a'); }); - test('pad', () => { - assert.strictEqual(strings.pad(1, 0), '1'); - assert.strictEqual(strings.pad(1, 1), '1'); - assert.strictEqual(strings.pad(1, 2), '01'); - assert.strictEqual(strings.pad(0, 2), '00'); - }); - test('escape', () => { assert.strictEqual(strings.escape(''), ''); assert.strictEqual(strings.escape('foo'), 'foo'); @@ -128,28 +133,6 @@ suite('Strings', () => { assert.strictEqual(strings.escape('Hello'), '<foo>Hello</foo>'); }); - test('startsWith', () => { - assert(strings.startsWith('foo', 'f')); - assert(strings.startsWith('foo', 'fo')); - assert(strings.startsWith('foo', 'foo')); - assert(!strings.startsWith('foo', 'o')); - assert(!strings.startsWith('', 'f')); - assert(strings.startsWith('foo', '')); - assert(strings.startsWith('', '')); - }); - - test('endsWith', () => { - assert(strings.endsWith('foo', 'o')); - assert(strings.endsWith('foo', 'oo')); - assert(strings.endsWith('foo', 'foo')); - assert(strings.endsWith('foo bar foo', 'foo')); - assert(!strings.endsWith('foo', 'f')); - assert(!strings.endsWith('', 'f')); - assert(strings.endsWith('foo', '')); - assert(strings.endsWith('', '')); - assert(strings.endsWith('/', '/')); - }); - test('ltrim', () => { assert.strictEqual(strings.ltrim('foo', 'f'), 'oo'); assert.strictEqual(strings.ltrim('foo', 'o'), 'foo'); @@ -193,13 +176,6 @@ suite('Strings', () => { assert.strictEqual(' '.trim(), ''); }); - test('repeat', () => { - assert.strictEqual(strings.repeat(' ', 4), ' '); - assert.strictEqual(strings.repeat(' ', 1), ' '); - assert.strictEqual(strings.repeat(' ', 0), ''); - assert.strictEqual(strings.repeat('abc', 2), 'abcabc'); - }); - test('lastNonWhitespaceIndex', () => { assert.strictEqual(strings.lastNonWhitespaceIndex('abc \t \t '), 2); assert.strictEqual(strings.lastNonWhitespaceIndex('abc'), 2); @@ -404,61 +380,6 @@ suite('Strings', () => { assert.equal(strings.getNLines('foo', 0), ''); }); - test('removeAccents', function () { - assert.equal(strings.removeAccents('joào'), 'joao'); - assert.equal(strings.removeAccents('joáo'), 'joao'); - assert.equal(strings.removeAccents('joâo'), 'joao'); - assert.equal(strings.removeAccents('joäo'), 'joao'); - // assert.equal(strings.removeAccents('joæo'), 'joao'); // not an accent - assert.equal(strings.removeAccents('joão'), 'joao'); - assert.equal(strings.removeAccents('joåo'), 'joao'); - assert.equal(strings.removeAccents('joåo'), 'joao'); - assert.equal(strings.removeAccents('joāo'), 'joao'); - - assert.equal(strings.removeAccents('fôo'), 'foo'); - assert.equal(strings.removeAccents('föo'), 'foo'); - assert.equal(strings.removeAccents('fòo'), 'foo'); - assert.equal(strings.removeAccents('fóo'), 'foo'); - // assert.equal(strings.removeAccents('fœo'), 'foo'); - // assert.equal(strings.removeAccents('føo'), 'foo'); - assert.equal(strings.removeAccents('fōo'), 'foo'); - assert.equal(strings.removeAccents('fõo'), 'foo'); - - assert.equal(strings.removeAccents('andrè'), 'andre'); - assert.equal(strings.removeAccents('andré'), 'andre'); - assert.equal(strings.removeAccents('andrê'), 'andre'); - assert.equal(strings.removeAccents('andrë'), 'andre'); - assert.equal(strings.removeAccents('andrē'), 'andre'); - assert.equal(strings.removeAccents('andrė'), 'andre'); - assert.equal(strings.removeAccents('andrę'), 'andre'); - - assert.equal(strings.removeAccents('hvîc'), 'hvic'); - assert.equal(strings.removeAccents('hvïc'), 'hvic'); - assert.equal(strings.removeAccents('hvíc'), 'hvic'); - assert.equal(strings.removeAccents('hvīc'), 'hvic'); - assert.equal(strings.removeAccents('hvįc'), 'hvic'); - assert.equal(strings.removeAccents('hvìc'), 'hvic'); - - assert.equal(strings.removeAccents('ûdo'), 'udo'); - assert.equal(strings.removeAccents('üdo'), 'udo'); - assert.equal(strings.removeAccents('ùdo'), 'udo'); - assert.equal(strings.removeAccents('údo'), 'udo'); - assert.equal(strings.removeAccents('ūdo'), 'udo'); - - assert.equal(strings.removeAccents('heÿ'), 'hey'); - - // assert.equal(strings.removeAccents('gruß'), 'grus'); - assert.equal(strings.removeAccents('gruś'), 'grus'); - assert.equal(strings.removeAccents('gruš'), 'grus'); - - assert.equal(strings.removeAccents('çool'), 'cool'); - assert.equal(strings.removeAccents('ćool'), 'cool'); - assert.equal(strings.removeAccents('čool'), 'cool'); - - assert.equal(strings.removeAccents('ñice'), 'nice'); - assert.equal(strings.removeAccents('ńice'), 'nice'); - }); - test('encodeUTF8', function () { function assertEncodeUTF8(str: string, expected: number[]): void { const actual = strings.encodeUTF8(str); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 6b35d6afc9c..bc4e409c02d 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -404,7 +404,7 @@ suite('URI', () => { path = 'foo/bar'; assert.equal(URI.file(path).path, '/foo/bar'); path = './foo/bar'; - assert.equal(URI.file(path).path, '/./foo/bar'); // todo@joh missing normalization + assert.equal(URI.file(path).path, '/./foo/bar'); // missing normalization const fileUri1 = URI.parse(`file:foo/bar`); assert.equal(fileUri1.path, '/foo/bar'); @@ -503,4 +503,68 @@ suite('URI', () => { // } // console.profileEnd(); }); + function assertJoined(base: string, fragment: string, expected: string, checkWithUrl: boolean = true) { + const baseUri = URI.parse(base); + const newUri = URI.joinPath(baseUri, fragment); + const actual = newUri.toString(true); + assert.equal(actual, expected); + + if (checkWithUrl) { + const actualUrl = new URL(fragment, base).href; + assert.equal(actualUrl, expected, 'DIFFERENT from URL'); + } + } + test('URI#joinPath', function () { + + assertJoined(('file:///foo/'), '../../bazz', 'file:///bazz'); + assertJoined(('file:///foo'), '../../bazz', 'file:///bazz'); + assertJoined(('file:///foo'), '../../bazz', 'file:///bazz'); + assertJoined(('file:///foo/bar/'), './bazz', 'file:///foo/bar/bazz'); + assertJoined(('file:///foo/bar'), './bazz', 'file:///foo/bar/bazz', false); + assertJoined(('file:///foo/bar'), 'bazz', 'file:///foo/bar/bazz', false); + + // "auto-path" scheme + assertJoined(('file:'), 'bazz', 'file:///bazz'); + assertJoined(('http://domain'), 'bazz', 'http://domain/bazz'); + assertJoined(('https://domain'), 'bazz', 'https://domain/bazz'); + assertJoined(('http:'), 'bazz', 'http:/bazz', false); + assertJoined(('https:'), 'bazz', 'https:/bazz', false); + + // no "auto-path" scheme with and w/o paths + assertJoined(('foo:/'), 'bazz', 'foo:/bazz'); + assertJoined(('foo://bar/'), 'bazz', 'foo://bar/bazz'); + + // no "auto-path" + no path -> error + assert.throws(() => assertJoined(('foo:'), 'bazz', '')); + assert.throws(() => new URL('bazz', 'foo:')); + assert.throws(() => assertJoined(('foo://bar'), 'bazz', '')); + // assert.throws(() => new URL('bazz', 'foo://bar')); Edge, Chrome => THROW, Firefox, Safari => foo://bar/bazz + }); + + test('URI#joinPath (posix)', function () { + if (isWindows) { + this.skip(); + } + assertJoined(('file:///c:/foo/'), '../../bazz', 'file:///bazz', false); + assertJoined(('file://server/share/c:/'), '../../bazz', 'file://server/bazz', false); + assertJoined(('file://server/share/c:'), '../../bazz', 'file://server/bazz', false); + + assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/bazz', false); // Firefox -> Different, Edge, Chrome, Safar -> OK + assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/bazz', false); // Firefox -> Different, Edge, Chrome, Safar -> OK + }); + + test('URI#joinPath (windows)', function () { + if (!isWindows) { + this.skip(); + } + assertJoined(('file:///c:/foo/'), '../../bazz', 'file:///c:/bazz', false); + assertJoined(('file://server/share/c:/'), '../../bazz', 'file://server/share/bazz', false); + assertJoined(('file://server/share/c:'), '../../bazz', 'file://server/share/bazz', false); + + assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/foo/bazz', false); + assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/foo/bazz', false); + + //https://github.com/microsoft/vscode/issues/93831 + assertJoined('file:///c:/foo/bar', './other/foo.img', 'file:///c:/foo/bar/other/foo.img', false); + }); }); diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 26b225a1fa9..a5ebd13e53e 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -25,36 +25,33 @@ export class DeferredPromise { } public complete(value: T) { - return new Promise(resolve => { - process.nextTick(() => { - this.completeCallback(value); - resolve(); - }); + return new Promise(resolve => { + this.completeCallback(value); + resolve(); }); } public error(err: any) { - return new Promise(resolve => { - process.nextTick(() => { - this.errorCallback(err); - resolve(); - }); + return new Promise(resolve => { + this.errorCallback(err); + resolve(); }); } public cancel() { - process.nextTick(() => { + new Promise(resolve => { this.errorCallback(canceled()); + resolve(); }); } } export function toResource(this: any, path: string) { if (isWindows) { - return URI.file(join('C:\\', Buffer.from(this.test.fullTitle()).toString('base64'), path)); + return URI.file(join('C:\\', btoa(this.test.fullTitle()), path)); } - return URI.file(join('/', Buffer.from(this.test.fullTitle()).toString('base64'), path)); + return URI.file(join('/', btoa(this.test.fullTitle()), path)); } export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void { @@ -68,7 +65,3 @@ export function testRepeat(n: number, description: string, callback: (this: any, test(`${description} (iteration ${i})`, callback); } } - -export function testRepeatOnly(n: number, description: string, callback: (this: any, done: MochaDone) => any): void { - suite.only('repeat', () => testRepeat(n, description, callback)); -} diff --git a/src/vs/base/test/common/uuid.test.ts b/src/vs/base/test/common/uuid.test.ts index ef2cd78be28..ce07ab9cb19 100644 --- a/src/vs/base/test/common/uuid.test.ts +++ b/src/vs/base/test/common/uuid.test.ts @@ -7,16 +7,17 @@ import * as uuid from 'vs/base/common/uuid'; suite('UUID', () => { test('generation', () => { - const asHex = uuid.v4().asHex(); + const asHex = uuid.generateUuid(); assert.equal(asHex.length, 36); assert.equal(asHex[14], '4'); assert.ok(asHex[19] === '8' || asHex[19] === '9' || asHex[19] === 'a' || asHex[19] === 'b'); }); - test('parse', () => { - const id = uuid.v4(); - const asHext = id.asHex(); - const id2 = uuid.parse(asHext); - assert.equal(id.asHex(), id2.asHex()); + test('self-check', function () { + const t1 = Date.now(); + while (Date.now() - t1 < 50) { + const value = uuid.generateUuid(); + assert.ok(uuid.isUUID(value)); + } }); }); diff --git a/src/vs/base/test/node/buffer.test.ts b/src/vs/base/test/node/buffer.test.ts index 69dfb89e8a1..4c7d0ba3a2f 100644 --- a/src/vs/base/test/node/buffer.test.ts +++ b/src/vs/base/test/node/buffer.test.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, newWriteableBufferStream } from 'vs/base/common/buffer'; +import { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, newWriteableBufferStream, bufferedStreamToBuffer } from 'vs/base/common/buffer'; import { timeout } from 'vs/base/common/async'; +import { peekStream } from 'vs/base/common/stream'; suite('Buffer', () => { @@ -29,6 +30,13 @@ suite('Buffer', () => { assert.equal((await streamToBuffer(stream)).toString(), content); }); + test('bufferedStreamToBuffer', async () => { + const content = 'Hello World'; + const stream = await peekStream(bufferToStream(VSBuffer.fromString(content)), 1); + + assert.equal((await bufferedStreamToBuffer(stream)).toString(), content); + }); + test('bufferWriteableStream - basics (no error)', async () => { const stream = newWriteableBufferStream(); diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts deleted file mode 100644 index 3fa8f78de68..00000000000 --- a/src/vs/base/test/node/config.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as os from 'os'; - -import * as path from 'vs/base/common/path'; -import * as fs from 'fs'; -import * as uuid from 'vs/base/common/uuid'; -import { ConfigWatcher } from 'vs/base/node/config'; -import { testFile } from 'vs/base/test/node/utils'; - -suite('Config', () => { - - test('defaults', () => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'config', id); - const testFile = path.join(newDir, 'config.json'); - - let watcher = new ConfigWatcher<{}>(testFile); - - let config = watcher.getConfig(); - assert.ok(config); - assert.equal(Object.keys(config), 0); - - watcher.dispose(); - - let watcher2 = new ConfigWatcher(testFile, { defaultConfig: ['foo'], onError: console.error }); - - let config2 = watcher2.getConfig(); - assert.ok(Array.isArray(config2)); - assert.equal(config2.length, 1); - - watcher.dispose(); - }); - - test('getConfig / getValue', function () { - return testFile('config', 'config.json').then(res => { - fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - - let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile); - - let config = watcher.getConfig(); - assert.ok(config); - assert.equal(config.foo, 'bar'); - assert.ok(!watcher.hasParseErrors); - - watcher.dispose(); - - return res.cleanUp(); - }); - }); - - test('getConfig / getValue - broken JSON', function () { - return testFile('config', 'config.json').then(res => { - fs.writeFileSync(res.testFile, '// my comment\n "foo": "bar ... '); - - let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile); - - let config = watcher.getConfig(); - assert.ok(config); - assert.ok(!config.foo); - - assert.ok(watcher.hasParseErrors); - - watcher.dispose(); - - return res.cleanUp(); - }); - }); - - // test('watching', function (done) { - // this.timeout(10000); // watching is timing intense - - // testFile('config', 'config.json').then(res => { - // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - - // let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile); - // watcher.getConfig(); // ensure we are in sync - - // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }'); - - // watcher.onDidUpdateConfiguration(event => { - // assert.ok(event); - // assert.equal(event.config.foo, 'changed'); - // assert.equal(watcher.getValue('foo'), 'changed'); - - // watcher.dispose(); - - // res.cleanUp().then(done, done); - // }); - // }, done); - // }); - - // test('watching also works when file created later', function (done) { - // this.timeout(10000); // watching is timing intense - - // testFile('config', 'config.json').then(res => { - // let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile); - // watcher.getConfig(); // ensure we are in sync - - // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }'); - - // watcher.onDidUpdateConfiguration(event => { - // assert.ok(event); - // assert.equal(event.config.foo, 'changed'); - // assert.equal(watcher.getValue('foo'), 'changed'); - - // watcher.dispose(); - - // res.cleanUp().then(done, done); - // }); - // }, done); - // }); - - // test('watching detects the config file getting deleted', function (done) { - // this.timeout(10000); // watching is timing intense - - // testFile('config', 'config.json').then(res => { - // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - - // let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile); - // watcher.getConfig(); // ensure we are in sync - - // watcher.onDidUpdateConfiguration(event => { - // assert.ok(event); - - // watcher.dispose(); - - // res.cleanUp().then(done, done); - // }); - - // fs.unlinkSync(res.testFile); - // }, done); - // }); - - test('reload', function (done) { - testFile('config', 'config.json').then(res => { - fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - - let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: { foo: 'bar' } }); - watcher.getConfig(); // ensure we are in sync - - fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }'); - - // still old values because change is not bubbling yet - assert.equal(watcher.getConfig().foo, 'bar'); - - // force a load from disk - watcher.reload(config => { - assert.equal(config.foo, 'changed'); - assert.equal(watcher.getConfig().foo, 'changed'); - - watcher.dispose(); - - res.cleanUp().then(done, done); - }); - }, done); - }); -}); \ No newline at end of file diff --git a/src/vs/base/test/node/crypto.test.ts b/src/vs/base/test/node/crypto.test.ts new file mode 100644 index 00000000000..ad8dc4fa5af --- /dev/null +++ b/src/vs/base/test/node/crypto.test.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { checksum } from 'vs/base/node/crypto'; +import { generateUuid } from 'vs/base/common/uuid'; +import { join } from 'vs/base/common/path'; +import { tmpdir } from 'os'; +import { mkdirp, rimraf, RimRafMode, writeFile } from 'vs/base/node/pfs'; + +suite('Crypto', () => { + + test('checksum', async () => { + const id = generateUuid(); + const testDir = join(tmpdir(), 'vsctests', id); + const testFile = join(testDir, 'checksum.txt'); + + await mkdirp(testDir); + + await writeFile(testFile, 'Hello World'); + + await checksum(testFile, '0a4d55a8d778e5022fab701977c5d840bbc486d0'); + + await rimraf(testDir, RimRafMode.MOVE); + }); +}); diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index f7e8dfd1356..2c0288e3897 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -239,10 +239,7 @@ suite('Glob', () => { assertGlobMatch(p, 'some/folder/project.json'); assertNoGlobMatch(p, 'some/folder/file_project.json'); assertNoGlobMatch(p, 'some/folder/fileproject.json'); - // assertNoGlobMatch(p, '/rrproject.json'); TODO@ben this still fails if T1-3 are disabled assertNoGlobMatch(p, 'some/rrproject.json'); - // assertNoGlobMatch(p, 'rrproject.json'); - // assertNoGlobMatch(p, '\\rrproject.json'); assertNoGlobMatch(p, 'some\\rrproject.json'); p = 'test/**'; diff --git a/src/vs/base/test/common/path.test.ts b/src/vs/base/test/node/path.test.ts similarity index 98% rename from src/vs/base/test/common/path.test.ts rename to src/vs/base/test/node/path.test.ts index f5230c4a35c..3150f8d6094 100644 --- a/src/vs/base/test/common/path.test.ts +++ b/src/vs/base/test/node/path.test.ts @@ -164,8 +164,7 @@ suite('Paths (Node Implementation)', () => { os = 'posix'; } const message = - `path.${os}.join(${test[0].map(JSON.stringify).join(',')})\n expect=${ - JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + `path.${os}.join(${test[0].map(JSON.stringify).join(',')})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; if (actual !== expected && actualAlt !== expected) { failures.push(`\n${message}`); } @@ -176,8 +175,8 @@ suite('Paths (Node Implementation)', () => { }); test('dirname', () => { - assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-11), - isWindows ? 'test\\common' : 'test/common'); + assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-9), + isWindows ? 'test\\node' : 'test/node'); assert.strictEqual(path.posix.dirname('/a/b/'), '/a'); assert.strictEqual(path.posix.dirname('/a/b'), '/a'); @@ -319,8 +318,7 @@ suite('Paths (Node Implementation)', () => { os = 'posix'; } const actual = extname(input); - const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${ - JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; if (actual !== expected) { failures.push(`\n${message}`); } @@ -328,8 +326,7 @@ suite('Paths (Node Implementation)', () => { { const input = `C:${test[0].replace(slashRE, '\\')}`; const actual = path.win32.extname(input); - const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${ - JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; if (actual !== expected) { failures.push(`\n${message}`); } @@ -401,9 +398,9 @@ suite('Paths (Node Implementation)', () => { ]; resolveTests.forEach((test) => { const resolve = test[0]; - //@ts-ignore + //@ts-expect-error test[1].forEach((test) => { - //@ts-ignore + //@ts-expect-error const actual = resolve.apply(null, test[0]); let actualAlt; const os = resolve === path.win32.resolve ? 'win32' : 'posix'; @@ -416,8 +413,7 @@ suite('Paths (Node Implementation)', () => { const expected = test[1]; const message = - `path.${os}.resolve(${test[0].map(JSON.stringify).join(',')})\n expect=${ - JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + `path.${os}.resolve(${test[0].map(JSON.stringify).join(',')})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; if (actual !== expected && actualAlt !== expected) { failures.push(`\n${message}`); } @@ -579,15 +575,13 @@ suite('Paths (Node Implementation)', () => { ]; relativeTests.forEach((test) => { const relative = test[0]; - //@ts-ignore + //@ts-expect-error test[1].forEach((test) => { - //@ts-ignore + //@ts-expect-error const actual = relative(test[0], test[1]); const expected = test[2]; const os = relative === path.win32.relative ? 'win32' : 'posix'; - const message = `path.${os}.relative(${ - test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${ - JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + const message = `path.${os}.relative(${test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; if (actual !== expected) { failures.push(`\n${message}`); } diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index aa4ca198eb0..fd324076b26 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -7,47 +7,14 @@ import * as assert from 'assert'; import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as fs from 'fs'; -import { Readable } from 'stream'; import * as uuid from 'vs/base/common/uuid'; import * as pfs from 'vs/base/node/pfs'; import { timeout } from 'vs/base/common/async'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { isWindows, isLinux } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; import { canNormalize } from 'vs/base/common/normalization'; import { VSBuffer } from 'vs/base/common/buffer'; -const chunkSize = 64 * 1024; -const readError = 'Error while reading'; -function toReadable(value: string, throwError?: boolean): Readable { - const totalChunks = Math.ceil(value.length / chunkSize); - const stringChunks: string[] = []; - - for (let i = 0, j = 0; i < totalChunks; ++i, j += chunkSize) { - stringChunks[i] = value.substr(j, chunkSize); - } - - let counter = 0; - return new Readable({ - read: function () { - if (throwError) { - this.emit('error', new Error(readError)); - } - - let res!: string; - let canPush = true; - while (canPush && (res = stringChunks[counter++])) { - canPush = this.push(res); - } - - // EOS - if (!res) { - this.push(null); - } - }, - encoding: 'utf8' - }); -} - suite('PFS', function () { // Given issues such as https://github.com/microsoft/vscode/issues/84066 @@ -257,7 +224,6 @@ suite('PFS', function () { } catch (error) { assert.fail(error); - throw error; } }); @@ -334,7 +300,7 @@ suite('PFS', function () { test('stat link', async () => { if (isWindows) { - return Promise.resolve(); // Symlinks are not the same on win, and we can not create them programitically without admin privileges + return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges } const id1 = uuid.generateUuid(); @@ -349,14 +315,38 @@ suite('PFS', function () { fs.symlinkSync(directory, symbolicLink); let statAndIsLink = await pfs.statLink(directory); - assert.ok(!statAndIsLink!.isSymbolicLink); + assert.ok(!statAndIsLink?.symbolicLink); statAndIsLink = await pfs.statLink(symbolicLink); - assert.ok(statAndIsLink!.isSymbolicLink); + assert.ok(statAndIsLink?.symbolicLink); + assert.ok(!statAndIsLink?.symbolicLink?.dangling); pfs.rimrafSync(directory); }); + test('stat link (non existing target)', async () => { + if (isWindows) { + return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges + } + + const id1 = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id1); + const directory = path.join(parentDir, 'pfs', id1); + + const id2 = uuid.generateUuid(); + const symbolicLink = path.join(parentDir, 'pfs', id2); + + await pfs.mkdirp(directory, 493); + + fs.symlinkSync(directory, symbolicLink); + + pfs.rimrafSync(directory); + + const statAndIsLink = await pfs.statLink(symbolicLink); + assert.ok(statAndIsLink?.symbolicLink); + assert.ok(statAndIsLink?.symbolicLink?.dangling); + }); + test('readdir', async () => { if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) { const id = uuid.generateUuid(); @@ -420,17 +410,10 @@ suite('PFS', function () { return testWriteFileAndFlush(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData); }); - test('writeFile (stream)', async () => { - const smallData = 'Hello World'; - const bigData = (new Array(100 * 1024)).join('Large String\n'); - - return testWriteFileAndFlush(toReadable(smallData), smallData, toReadable(bigData), bigData); - }); - async function testWriteFileAndFlush( - smallData: string | Buffer | NodeJS.ReadableStream | Uint8Array, + smallData: string | Buffer | Uint8Array, smallDataValue: string, - bigData: string | Buffer | NodeJS.ReadableStream | Uint8Array, + bigData: string | Buffer | Uint8Array, bigDataValue: string ): Promise { const id = uuid.generateUuid(); @@ -450,22 +433,6 @@ suite('PFS', function () { await pfs.rimraf(parentDir); } - test('writeFile (file stream)', async () => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - await pfs.mkdirp(newDir, 493); - assert.ok(fs.existsSync(newDir)); - - await pfs.writeFile(testFile, fs.createReadStream(sourceFile)); - assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString()); - - await pfs.rimraf(parentDir); - }); - test('writeFile (string, error handling)', async () => { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); @@ -490,118 +457,6 @@ suite('PFS', function () { await pfs.rimraf(parentDir); }); - test('writeFile (stream, error handling EISDIR)', async () => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - await pfs.mkdirp(newDir, 493); - - assert.ok(fs.existsSync(newDir)); - - fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - - const readable = toReadable('Hello World'); - - let expectedError: Error | undefined; - try { - await pfs.writeFile(testFile, readable); - } catch (error) { - expectedError = error; - } - - if (!expectedError || (expectedError).code !== 'EISDIR') { - throw new Error('Expected EISDIR error for writing to folder but got: ' + (expectedError ? (expectedError).code : 'no error')); - } - - // verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542) - assert.equal(readable.read(), 'Hello World'); - - await pfs.rimraf(parentDir); - }); - - test('writeFile (stream, error handling READERROR)', async () => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - await pfs.mkdirp(newDir, 493); - assert.ok(fs.existsSync(newDir)); - - let expectedError: Error | undefined; - try { - await pfs.writeFile(testFile, toReadable('Hello World', true /* throw error */)); - } catch (error) { - expectedError = error; - } - - if (!expectedError || expectedError.message !== readError) { - throw new Error('Expected error for writing to folder'); - } - - await pfs.rimraf(parentDir); - }); - - test('writeFile (stream, error handling EACCES)', async () => { - if (isLinux) { - return Promise.resolve(); // somehow this test fails on Linux in our TFS builds - } - - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - await pfs.mkdirp(newDir, 493); - - assert.ok(fs.existsSync(newDir)); - - fs.writeFileSync(testFile, ''); - fs.chmodSync(testFile, 33060); // make readonly - - let expectedError: Error | undefined; - try { - await pfs.writeFile(testFile, toReadable('Hello World')); - } catch (error) { - expectedError = error; - } - - if (!expectedError || !((expectedError).code !== 'EACCES' || (expectedError).code !== 'EPERM')) { - throw new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (expectedError ? (expectedError).code : 'no error')); - } - - await pfs.rimraf(parentDir); - }); - - test('writeFile (file stream, error handling)', async () => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - await pfs.mkdirp(newDir, 493); - - assert.ok(fs.existsSync(newDir)); - - fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - - let expectedError: Error | undefined; - try { - await pfs.writeFile(testFile, fs.createReadStream(sourceFile)); - } catch (error) { - expectedError = error; - } - - if (!expectedError) { - throw new Error('Expected error for writing to folder'); - } - - await pfs.rimraf(parentDir); - }); - test('writeFileSync', async () => { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts deleted file mode 100644 index 5ba6f6e2472..00000000000 --- a/src/vs/base/test/node/utils.ts +++ /dev/null @@ -1,28 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { generateUuid } from 'vs/base/common/uuid'; -import { join } from 'vs/base/common/path'; -import { tmpdir } from 'os'; -import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs'; - -export interface ITestFileResult { - testFile: string; - cleanUp: () => Promise; -} - -export function testFile(folder: string, file: string): Promise { - const id = generateUuid(); - const parentDir = join(tmpdir(), 'vsctests', id); - const newDir = join(parentDir, folder, id); - const testFile = join(newDir, file); - - return mkdirp(newDir, 493).then(() => { - return { - testFile, - cleanUp: () => rimraf(parentDir, RimRafMode.MOVE) - }; - }); -} diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index 45f3b41a457..51faabdf9d3 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -19,7 +19,7 @@ function getWorker(workerId: string, label: string): Worker | Promise { // ESM-comment-begin if (typeof require === 'function') { // check if the JS lives on a different origin - const workerMain = require.toUrl('./' + workerId); + const workerMain = require.toUrl('./' + workerId); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321 const workerUrl = getWorkerBootstrapUrl(workerMain, label); return new Worker(workerUrl, { name: label }); } @@ -28,18 +28,22 @@ function getWorker(workerId: string, label: string): Worker | Promise { } // ESM-comment-begin -export function getWorkerBootstrapUrl(scriptPath: string, label: string): string { - if (/^(http:)|(https:)|(file:)/.test(scriptPath)) { +export function getWorkerBootstrapUrl(scriptPath: string, label: string, forceDataUri: boolean = false): string { + if (forceDataUri || /^((http:)|(https:)|(file:))/.test(scriptPath)) { const currentUrl = String(window.location); const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); - if (scriptPath.substring(0, currentOrigin.length) !== currentOrigin) { + if (forceDataUri || scriptPath.substring(0, currentOrigin.length) !== currentOrigin) { // this is the cross-origin case // i.e. the webpage is running at a different origin than where the scripts are loaded from const myPath = 'vs/base/worker/defaultWorkerFactory.js'; - const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); + const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321 const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${scriptPath}');/*${label}*/`; - const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; - return url; + if (forceDataUri) { + const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; + return url; + } + const blob = new Blob([js], { type: 'application/javascript' }); + return URL.createObjectURL(blob); } } return scriptPath + '#' + label; diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 22bc88f1c00..71c6724e984 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -14,7 +14,8 @@ require.config({ baseUrl: monacoBaseUrl, - catchError: true + catchError: true, + createTrustedScriptURL: (value: string) => value, }); let loadCode = function (moduleId: string) { diff --git a/src/vs/code/browser/workbench/callback.html b/src/vs/code/browser/workbench/callback.html index da6c907b666..9aec539bb55 100644 --- a/src/vs/code/browser/workbench/callback.html +++ b/src/vs/code/browser/workbench/callback.html @@ -35,7 +35,7 @@ display: flex; flex-direction: column; color: white; - font-family: "Segoe UI", "Helvetica Neue", "Helvetica", Arial, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif; background-color: #373277; } diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index af6317d035f..c3c259cfa83 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -2,6 +2,10 @@ + @@ -10,8 +14,11 @@ - - + + + + + @@ -23,23 +30,28 @@ + diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 8a6a9f54e67..300ceb2fbb8 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -2,6 +2,10 @@ + @@ -10,16 +14,14 @@ - - + + - - @@ -27,21 +29,27 @@ + diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index a599f5a7ebf..0ef8b9dc814 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IHomeIndicator, IProductQualityChangeHandler, ISettingsSyncOptions } from 'vs/workbench/workbench.web.api'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { streamToBuffer } from 'vs/base/common/buffer'; @@ -12,6 +14,27 @@ import { request } from 'vs/base/parts/request/browser/request'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; import { isEqual } from 'vs/base/common/resources'; import { isStandalone } from 'vs/base/browser/browser'; +import { localize } from 'vs/nls'; +import { Schemas } from 'vs/base/common/network'; +import product from 'vs/platform/product/common/product'; + +function doCreateUri(path: string, queryValues: Map): URI { + let query: string | undefined = undefined; + + if (queryValues) { + let index = 0; + queryValues.forEach((value, key) => { + if (!query) { + query = ''; + } + + const prefix = (index++ === 0) ? '' : '&'; + query += `${prefix}${key}=${encodeURIComponent(value)}`; + }); + } + + return URI.parse(window.location.href).with({ path, query }); +} interface ICredential { service: string; @@ -23,6 +46,32 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider'; + private readonly authService: string | undefined; + + constructor() { + let authSessionInfo: { readonly id: string, readonly accessToken: string, readonly providerId: string, readonly canSignOut?: boolean, readonly scopes: string[][] } | undefined; + const authSessionElement = document.getElementById('vscode-workbench-auth-session'); + const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; + if (authSessionElementAttribute) { + try { + authSessionInfo = JSON.parse(authSessionElementAttribute); + } catch (error) { /* Invalid session is passed. Ignore. */ } + } + + if (authSessionInfo) { + // Settings Sync Entry + this.setPassword(`${product.urlProtocol}.login`, 'account', JSON.stringify(authSessionInfo)); + + // Auth extension Entry + this.authService = `${product.urlProtocol}-${authSessionInfo.providerId}.login`; + this.setPassword(this.authService, 'account', JSON.stringify(authSessionInfo.scopes.map(scopes => ({ + id: authSessionInfo!.id, + scopes, + accessToken: authSessionInfo!.accessToken + })))); + } + } + private _credentials: ICredential[] | undefined; private get credentials(): ICredential[] { if (!this._credentials) { @@ -64,14 +113,39 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { } async setPassword(service: string, account: string, password: string): Promise { - this.deletePassword(service, account); + this.doDeletePassword(service, account); this.credentials.push({ service, account, password }); this.save(); + + try { + if (password && service === this.authService) { + const value = JSON.parse(password); + if (Array.isArray(value) && value.length === 0) { + await this.logout(service); + } + } + } catch (error) { + console.log(error); + } } async deletePassword(service: string, account: string): Promise { + const result = await this.doDeletePassword(service, account); + + if (result && service === this.authService) { + try { + await this.logout(service); + } catch (error) { + console.log(error); + } + } + + return result; + } + + private async doDeletePassword(service: string, account: string): Promise { let found = false; this._credentials = this.credentials.filter(credential => { @@ -100,6 +174,16 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { .filter(credential => credential.service === service) .map(({ account, password }) => ({ account, password })); } + + private async logout(service: string): Promise { + const queryValues: Map = new Map(); + queryValues.set('logout', String(true)); + queryValues.set('service', service); + + await request({ + url: doCreateUri('/auth/logout', queryValues).toString(true) + }, CancellationToken.None); + } } class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider { @@ -116,8 +200,8 @@ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvi FRAGMENT: 'vscode-fragment' }; - private readonly _onCallback: Emitter = this._register(new Emitter()); - readonly onCallback: Event = this._onCallback.event; + private readonly _onCallback = this._register(new Emitter()); + readonly onCallback = this._onCallback.event; create(options?: Partial): URI { const queryValues: Map = new Map(); @@ -150,7 +234,7 @@ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvi // Start to poll on the callback being fired this.periodicFetchCallback(requestId, Date.now()); - return this.doCreateUri('/callback', queryValues); + return doCreateUri('/callback', queryValues); } private async periodicFetchCallback(requestId: string, startTime: number): Promise { @@ -160,7 +244,7 @@ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvi queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); const result = await request({ - url: this.doCreateUri('/fetch-callback', queryValues).toString(true) + url: doCreateUri('/fetch-callback', queryValues).toString(true) }, CancellationToken.None); // Check for callback results @@ -181,23 +265,6 @@ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvi } } - private doCreateUri(path: string, queryValues: Map): URI { - let query: string | undefined = undefined; - - if (queryValues) { - let index = 0; - queryValues.forEach((value, key) => { - if (!query) { - query = ''; - } - - const prefix = (index++ === 0) ? '' : '&'; - query += `${prefix}${key}=${encodeURIComponent(value)}`; - }); - } - - return URI.parse(window.location.href).with({ path, query }); - } } class WorkspaceProvider implements IWorkspaceProvider { @@ -273,6 +340,59 @@ class WorkspaceProvider implements IWorkspaceProvider { return false; } + + hasRemote(): boolean { + if (this.workspace) { + if (isFolderToOpen(this.workspace)) { + return this.workspace.folderUri.scheme === Schemas.vscodeRemote; + } + + if (isWorkspaceToOpen(this.workspace)) { + return this.workspace.workspaceUri.scheme === Schemas.vscodeRemote; + } + } + + return true; + } +} + +class WindowIndicator implements IWindowIndicator { + + readonly onDidChange = Event.None; + + readonly label: string; + readonly tooltip: string; + readonly command: string | undefined; + + constructor(workspace: IWorkspace) { + let repositoryOwner: string | undefined = undefined; + let repositoryName: string | undefined = undefined; + + if (workspace) { + let uri: URI | undefined = undefined; + if (isFolderToOpen(workspace)) { + uri = workspace.folderUri; + } else if (isWorkspaceToOpen(workspace)) { + uri = workspace.workspaceUri; + } + + if (uri?.scheme === 'github' || uri?.scheme === 'codespace') { + [repositoryOwner, repositoryName] = uri.authority.split('+'); + } + } + + // Repo + if (repositoryName && repositoryOwner) { + this.label = localize('playgroundLabelRepository', "$(remote) VS Code Web Playground: {0}/{1}", repositoryOwner, repositoryName); + this.tooltip = localize('playgroundRepositoryTooltip', "VS Code Web Playground: {0}/{1}", repositoryOwner, repositoryName); + } + + // No Repo + else { + this.label = localize('playgroundLabel', "$(remote) VS Code Web Playground"); + this.tooltip = localize('playgroundTooltip', "VS Code Web Playground"); + } + } } (function () { @@ -322,7 +442,11 @@ class WorkspaceProvider implements IWorkspaceProvider { // Payload case WorkspaceProvider.QUERY_PARAM_PAYLOAD: - payload = JSON.parse(value); + try { + payload = JSON.parse(value); + } catch (error) { + console.error(error); // possible invalid JSON + } break; } }); @@ -338,10 +462,63 @@ class WorkspaceProvider implements IWorkspaceProvider { } } + // Workspace Provider + const workspaceProvider = new WorkspaceProvider(workspace, payload); + + // Home Indicator + const homeIndicator: IHomeIndicator = { + href: 'https://github.com/microsoft/vscode', + icon: 'code', + title: localize('home', "Home") + }; + + // Window indicator (unless connected to a remote) + let windowIndicator: WindowIndicator | undefined = undefined; + if (!workspaceProvider.hasRemote()) { + windowIndicator = new WindowIndicator(workspace); + } + + // Product Quality Change Handler + const productQualityChangeHandler: IProductQualityChangeHandler = (quality) => { + let queryString = `quality=${quality}`; + + // Save all other query params we might have + const query = new URL(document.location.href).searchParams; + query.forEach((value, key) => { + if (key !== 'quality') { + queryString += `&${key}=${value}`; + } + }); + + window.location.href = `${window.location.origin}?${queryString}`; + }; + + // settings sync options + const settingsSyncOptions: ISettingsSyncOptions | undefined = config.settingsSyncOptions ? { + enabled: config.settingsSyncOptions.enabled, + enablementHandler: (enablement) => { + let queryString = `settingsSync=${enablement ? 'true' : 'false'}`; + + // Save all other query params we might have + const query = new URL(document.location.href).searchParams; + query.forEach((value, key) => { + if (key !== 'settingsSync') { + queryString += `&${key}=${value}`; + } + }); + + window.location.href = `${window.location.origin}?${queryString}`; + } + } : undefined; + // Finally create workbench create(document.body, { ...config, - workspaceProvider: new WorkspaceProvider(workspace, payload), + settingsSyncOptions, + homeIndicator, + windowIndicator, + productQualityChangeHandler, + workspaceProvider, urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider() }); diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js index 221bea21443..9b1ee16680e 100644 --- a/src/vs/code/buildfile.js +++ b/src/vs/code/buildfile.js @@ -22,10 +22,9 @@ exports.collectModules = function () { createModuleDescription('vs/code/electron-main/main', []), createModuleDescription('vs/code/node/cli', []), createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), - createModuleDescription('vs/code/electron-browser/issue/issueReporterMain', []), + createModuleDescription('vs/code/electron-sandbox/issue/issueReporterMain', []), createModuleDescription('vs/code/electron-browser/sharedProcess/sharedProcessMain', []), - createModuleDescription('vs/code/electron-browser/issue/issueReporterMain', []), createModuleDescription('vs/platform/driver/node/driver', []), - createModuleDescription('vs/code/electron-browser/processExplorer/processExplorerMain', []) + createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain', []) ]; }; diff --git a/src/vs/code/electron-browser/issue/issueReporter.html b/src/vs/code/electron-browser/issue/issueReporter.html deleted file mode 100644 index 695de78a4cb..00000000000 --- a/src/vs/code/electron-browser/issue/issueReporter.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/vs/code/electron-browser/processExplorer/media/collapsed.svg b/src/vs/code/electron-browser/processExplorer/media/collapsed.svg deleted file mode 100644 index 3a63808c358..00000000000 --- a/src/vs/code/electron-browser/processExplorer/media/collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/code/electron-browser/processExplorer/media/expanded.svg b/src/vs/code/electron-browser/processExplorer/media/expanded.svg deleted file mode 100644 index 75f73adbb02..00000000000 --- a/src/vs/code/electron-browser/processExplorer/media/expanded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css deleted file mode 100644 index 69b0b9a35c9..00000000000 --- a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css +++ /dev/null @@ -1,102 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -html { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; - font-size: 13px; -} - -html:lang(zh-Hans) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; -} - -html:lang(zh-Hant) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; -} - -html:lang(ja) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; -} - -html:lang(ko) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; -} - -body { - margin: 0; - padding: 0; - height: 100%; - width: 100%; - user-select: none; - color: #cccccc; -} - -.cpu { - width: 45px; -} - -.pid { - width: 50px -} - -.memory { - width: 90px; -} - -.process-item { - line-height: 22px; -} - -table { - border-collapse: collapse; - width: 100%; - table-layout: fixed; -} -th[scope='col'] { - vertical-align: bottom; - border-bottom: 1px solid #cccccc; - padding: .5rem; - border-top: 1px solid #cccccc; - cursor: default; -} -td { - padding: .25rem; - vertical-align: top; - cursor: default; -} - -.centered { - text-align: center; -} - -.nameLabel{ - text-align: left; -} - -.data { - white-space: pre; - padding-left: .5rem; - font-weight: normal; - text-align: left; - height: 22px; -} - -.error { - padding-left: 20px; - white-space: nowrap; -} - -tbody > tr:hover { - background-color: #2A2D2E; -} - -.hidden { - display: none; -} - -img { - width: 16px; - margin-right: 4px; -} diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.html b/src/vs/code/electron-browser/processExplorer/processExplorer.html deleted file mode 100644 index 3ef3be23f8f..00000000000 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - -
    - - - - - - \ No newline at end of file diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts deleted file mode 100644 index 09ccca9b823..00000000000 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ /dev/null @@ -1,419 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/processExplorer'; -import { webFrame, ipcRenderer, clipboard } from 'electron'; -import { repeat } from 'vs/base/common/strings'; -import { totalmem } from 'os'; -import product from 'vs/platform/product/common/product'; -import { localize } from 'vs/nls'; -import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/node/issue'; -import * as browser from 'vs/base/browser/browser'; -import * as platform from 'vs/base/common/platform'; -import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; -import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu'; -import { ProcessItem } from 'vs/base/common/processes'; -import { addDisposableListener } from 'vs/base/browser/dom'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; - -let mapPidToWindowTitle = new Map(); - -const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; -const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; -const listeners = new DisposableStore(); -const collapsedStateCache: Map = new Map(); -let lastRequestTime: number; - -interface FormattedProcessItem { - cpu: number; - memory: number; - pid: string; - name: string; - formattedName: string; - cmd: string; -} - -function getProcessList(rootProcess: ProcessItem, isLocal: boolean): FormattedProcessItem[] { - const processes: FormattedProcessItem[] = []; - - if (rootProcess) { - getProcessItem(processes, rootProcess, 0, isLocal); - } - - return processes; -} - -function getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean): void { - const isRoot = (indent === 0); - - const MB = 1024 * 1024; - - let name = item.name; - if (isRoot) { - name = isLocal ? `${product.applicationName} main` : 'remote agent'; - } - - if (name === 'window') { - const windowTitle = mapPidToWindowTitle.get(item.pid); - name = windowTitle !== undefined ? `${name} (${mapPidToWindowTitle.get(item.pid)})` : name; - } - - // Format name with indent - const formattedName = isRoot ? name : `${repeat(' ', indent)} ${name}`; - const memory = process.platform === 'win32' ? item.mem : (totalmem() * (item.mem / 100)); - processes.push({ - cpu: item.load, - memory: (memory / MB), - pid: item.pid.toFixed(0), - name, - formattedName, - cmd: item.cmd - }); - - // Recurse into children if any - if (Array.isArray(item.children)) { - item.children.forEach(child => getProcessItem(processes, child, indent + 1, isLocal)); - } -} - -function isDebuggable(cmd: string): boolean { - const matches = DEBUG_FLAGS_PATTERN.exec(cmd); - return (matches && matches.length >= 2) || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0; -} - -function attachTo(item: FormattedProcessItem) { - const config: any = { - type: 'node', - request: 'attach', - name: `process ${item.pid}` - }; - - let matches = DEBUG_FLAGS_PATTERN.exec(item.cmd); - if (matches && matches.length >= 2) { - // attach via port - if (matches.length === 4 && matches[3]) { - config.port = parseInt(matches[3]); - } - config.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; - } else { - // no port -> try to attach via pid (send SIGUSR1) - config.processId = String(item.pid); - } - - // a debug-port=n or inspect-port=n overrides the port - matches = DEBUG_PORT_PATTERN.exec(item.cmd); - if (matches && matches.length === 3) { - // override port - config.port = parseInt(matches[2]); - } - - ipcRenderer.send('vscode:workbenchCommand', { id: 'debug.startFromConfig', from: 'processExplorer', args: [config] }); -} - -function getProcessIdWithHighestProperty(processList: any[], propertyName: string) { - let max = 0; - let maxProcessId; - processList.forEach(process => { - if (process[propertyName] > max) { - max = process[propertyName]; - maxProcessId = process.pid; - } - }); - - return maxProcessId; -} - -function updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: HTMLImageElement, sectionName: string) { - if (shouldExpand) { - body.classList.remove('hidden'); - collapsedStateCache.set(sectionName, false); - twistie.src = './media/expanded.svg'; - } else { - body.classList.add('hidden'); - collapsedStateCache.set(sectionName, true); - twistie.src = './media/collapsed.svg'; - } -} - -function renderProcessFetchError(sectionName: string, errorMessage: string) { - const container = document.getElementById('process-list'); - if (!container) { - return; - } - - const body = document.createElement('tbody'); - - renderProcessGroupHeader(sectionName, body, container); - - const errorRow = document.createElement('tr'); - const data = document.createElement('td'); - data.textContent = errorMessage; - data.className = 'error'; - data.colSpan = 4; - errorRow.appendChild(data); - - body.appendChild(errorRow); - container.appendChild(body); -} - -function renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) { - const headerRow = document.createElement('tr'); - const data = document.createElement('td'); - data.textContent = sectionName; - data.colSpan = 4; - headerRow.appendChild(data); - - const twistie = document.createElement('img'); - updateSectionCollapsedState(!collapsedStateCache.get(sectionName), body, twistie, sectionName); - data.prepend(twistie); - - listeners.add(addDisposableListener(data, 'click', (e) => { - const isHidden = body.classList.contains('hidden'); - updateSectionCollapsedState(isHidden, body, twistie, sectionName); - })); - - container.appendChild(headerRow); -} - -function renderTableSection(sectionName: string, processList: FormattedProcessItem[], renderManySections: boolean, sectionIsLocal: boolean): void { - const container = document.getElementById('process-list'); - if (!container) { - return; - } - - const highestCPUProcess = getProcessIdWithHighestProperty(processList, 'cpu'); - const highestMemoryProcess = getProcessIdWithHighestProperty(processList, 'memory'); - - const body = document.createElement('tbody'); - - if (renderManySections) { - renderProcessGroupHeader(sectionName, body, container); - } - - processList.forEach(p => { - const row = document.createElement('tr'); - row.id = p.pid.toString(); - - const cpu = document.createElement('td'); - p.pid === highestCPUProcess - ? cpu.classList.add('centered', 'highest') - : cpu.classList.add('centered'); - cpu.textContent = p.cpu.toFixed(0); - - const memory = document.createElement('td'); - p.pid === highestMemoryProcess - ? memory.classList.add('centered', 'highest') - : memory.classList.add('centered'); - memory.textContent = p.memory.toFixed(0); - - const pid = document.createElement('td'); - pid.classList.add('centered'); - pid.textContent = p.pid; - - const name = document.createElement('th'); - name.scope = 'row'; - name.classList.add('data'); - name.title = p.cmd; - name.textContent = p.formattedName; - - row.append(cpu, memory, pid, name); - - listeners.add(addDisposableListener(row, 'contextmenu', (e) => { - showContextMenu(e, p, sectionIsLocal); - })); - - body.appendChild(row); - }); - - container.appendChild(body); -} - -function updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]): void { - const container = document.getElementById('process-list'); - if (!container) { - return; - } - - container.innerHTML = ''; - listeners.clear(); - - const tableHead = document.createElement('thead'); - tableHead.innerHTML = ` - ${localize('cpu', "CPU %")} - ${localize('memory', "Memory (MB)")} - ${localize('pid', "pid")} - ${localize('name', "Name")} - `; - - container.append(tableHead); - - const hasMultipleMachines = Object.keys(processLists).length > 1; - processLists.forEach((remote, i) => { - const isLocal = i === 0; - if (isRemoteDiagnosticError(remote.rootProcess)) { - renderProcessFetchError(remote.name, remote.rootProcess.errorMessage); - } else { - renderTableSection(remote.name, getProcessList(remote.rootProcess, isLocal), hasMultipleMachines, isLocal); - } - }); -} - -function applyStyles(styles: ProcessExplorerStyles): void { - const styleTag = document.createElement('style'); - const content: string[] = []; - - if (styles.hoverBackground) { - content.push(`tbody > tr:hover, table > tr:hover { background-color: ${styles.hoverBackground}; }`); - } - - if (styles.hoverForeground) { - content.push(`tbody > tr:hover, table > tr:hover { color: ${styles.hoverForeground}; }`); - } - - if (styles.highlightForeground) { - content.push(`.highest { color: ${styles.highlightForeground}; }`); - } - - styleTag.innerHTML = content.join('\n'); - if (document.head) { - document.head.appendChild(styleTag); - } - if (styles.color) { - document.body.style.color = styles.color; - } -} - -function applyZoom(zoomLevel: number): void { - webFrame.setZoomLevel(zoomLevel); - browser.setZoomFactor(webFrame.getZoomFactor()); - // See https://github.com/Microsoft/vscode/issues/26151 - // Cannot be trusted because the webFrame might take some time - // until it really applies the new zoom level - browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); -} - -function showContextMenu(e: MouseEvent, item: FormattedProcessItem, isLocal: boolean) { - e.preventDefault(); - - const items: IContextMenuItem[] = []; - const pid = Number(item.pid); - - if (isLocal) { - items.push({ - label: localize('killProcess', "Kill Process"), - click() { - process.kill(pid, 'SIGTERM'); - } - }); - - items.push({ - label: localize('forceKillProcess', "Force Kill Process"), - click() { - process.kill(pid, 'SIGKILL'); - } - }); - - items.push({ - type: 'separator' - }); - } - - items.push({ - label: localize('copy', "Copy"), - click() { - const row = document.getElementById(pid.toString()); - if (row) { - clipboard.writeText(row.innerText); - } - } - }); - - items.push({ - label: localize('copyAll', "Copy All"), - click() { - const processList = document.getElementById('process-list'); - if (processList) { - clipboard.writeText(processList.innerText); - } - } - }); - - if (item && isLocal && isDebuggable(item.cmd)) { - items.push({ - type: 'separator' - }); - - items.push({ - label: localize('debug', "Debug"), - click() { - attachTo(item); - } - }); - } - - popup(items); -} - -function requestProcessList(totalWaitTime: number): void { - setTimeout(() => { - const nextRequestTime = Date.now(); - const waited = totalWaitTime + nextRequestTime - lastRequestTime; - lastRequestTime = nextRequestTime; - - // Wait at least a second between requests. - if (waited > 1000) { - ipcRenderer.send('windowsInfoRequest'); - ipcRenderer.send('vscode:listProcesses'); - } else { - requestProcessList(waited); - } - }, 200); -} - -function createCloseListener(): void { - // Cmd/Ctrl + w closes process explorer - window.addEventListener('keydown', e => { - const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; - if (cmdOrCtrlKey && e.keyCode === 87) { - ipcRenderer.send('vscode:closeProcessExplorer'); - } - }); -} - -export function startup(data: ProcessExplorerData): void { - applyStyles(data.styles); - applyZoom(data.zoomLevel); - createCloseListener(); - - // Map window process pids to titles, annotate process names with this when rendering to distinguish between them - ipcRenderer.on('vscode:windowsInfoResponse', (_event: unknown, windows: any[]) => { - mapPidToWindowTitle = new Map(); - windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title)); - }); - - ipcRenderer.on('vscode:listProcessesResponse', (_event: Event, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => { - updateProcessInfo(processRoots); - requestProcessList(0); - }); - - lastRequestTime = Date.now(); - ipcRenderer.send('windowsInfoRequest'); - ipcRenderer.send('vscode:listProcesses'); - - document.onkeydown = (e: KeyboardEvent) => { - const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; - - // Cmd/Ctrl + zooms in - if (cmdOrCtrlKey && e.keyCode === 187) { - applyZoom(webFrame.getZoomLevel() + 1); - } - - // Cmd/Ctrl - zooms out - if (cmdOrCtrlKey && e.keyCode === 189) { - applyZoom(webFrame.getZoomLevel() - 1); - } - }; -} diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 0e79bc81179..e060bcc6a01 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -5,13 +5,12 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; - import { IStringDictionary } from 'vs/base/common/collections'; import product from 'vs/platform/product/common/product'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ILogService } from 'vs/platform/log/common/log'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; interface ExtensionEntry { version: string; @@ -33,7 +32,7 @@ interface LanguagePackFile { export class LanguagePackCachedDataCleaner extends Disposable { constructor( - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @INativeEnvironmentService private readonly _environmentService: INativeEnvironmentService, @ILogService private readonly _logService: ILogService ) { super(); @@ -102,4 +101,4 @@ export class LanguagePackCachedDataCleaner extends Disposable { } })); } -} \ No newline at end of file +} diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts index c3a758470fc..a9808801094 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -7,7 +7,6 @@ import { basename, dirname, join } from 'vs/base/common/path'; import { onUnexpectedError } from 'vs/base/common/errors'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { readdir, rimraf, stat } from 'vs/base/node/pfs'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; export class NodeCachedDataCleaner { @@ -19,7 +18,7 @@ export class NodeCachedDataCleaner { private readonly _disposables = new DisposableStore(); constructor( - @IEnvironmentService private readonly _environmentService: IEnvironmentService + private readonly nodeCachedDataDir: string | undefined ) { this._manageCachedDataSoon(); } @@ -32,14 +31,14 @@ export class NodeCachedDataCleaner { // Cached data is stored as user data and we run a cleanup task everytime // the editor starts. The strategy is to delete all files that are older than // 3 months (1 week respectively) - if (!this._environmentService.nodeCachedDataDir) { + if (!this.nodeCachedDataDir) { return; } // The folder which contains folders of cached data. Each of these folder is per // version - const nodeCachedDataRootDir = dirname(this._environmentService.nodeCachedDataDir); - const nodeCachedDataCurrent = basename(this._environmentService.nodeCachedDataDir); + const nodeCachedDataRootDir = dirname(this.nodeCachedDataDir); + const nodeCachedDataCurrent = basename(this.nodeCachedDataDir); let handle: NodeJS.Timeout | undefined = setTimeout(() => { handle = undefined; diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index a3899b854fb..66b8d79b5fa 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { join } from 'vs/base/common/path'; import { readdir, readFile, rimraf } from 'vs/base/node/pfs'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -16,7 +16,8 @@ export class StorageDataCleaner extends Disposable { private static readonly NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService + private readonly backupWorkspacesPath: string, + @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService ) { super(); @@ -27,14 +28,17 @@ export class StorageDataCleaner extends Disposable { let handle: NodeJS.Timeout | undefined = setTimeout(() => { handle = undefined; - // Leverage the backup workspace file to find out which empty workspace is currently in use to - // determine which empty workspace storage can safely be deleted - readFile(this.environmentService.backupWorkspacesPath, 'utf8').then(contents => { - const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat; - const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder); + (async () => { + try { + // Leverage the backup workspace file to find out which empty workspace is currently in use to + // determine which empty workspace storage can safely be deleted + const contents = await readFile(this.backupWorkspacesPath, 'utf8'); - // Read all workspace storage folders that exist - return readdir(this.environmentService.workspaceStorageHome).then(storageFolders => { + const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat; + const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder); + + // Read all workspace storage folders that exist + const storageFolders = await readdir(this.environmentService.workspaceStorageHome.fsPath); const deletes: Promise[] = []; storageFolders.forEach(storageFolder => { @@ -43,13 +47,15 @@ export class StorageDataCleaner extends Disposable { } if (emptyWorkspaces.indexOf(storageFolder) === -1) { - deletes.push(rimraf(join(this.environmentService.workspaceStorageHome, storageFolder))); + deletes.push(rimraf(join(this.environmentService.workspaceStorageHome.fsPath, storageFolder))); } }); - return Promise.all(deletes); - }); - }).then(null, onUnexpectedError); + await Promise.all(deletes); + } catch (error) { + onUnexpectedError(error); + } + })(); }, 30 * 1000); this._register(toDisposable(() => { diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.html b/src/vs/code/electron-browser/sharedProcess/sharedProcess.html index 26890a9fc6b..07fd9bd0478 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.html +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.html @@ -11,7 +11,12 @@ Shared Process + + + + + - \ No newline at end of file + diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js index 2ecd5f650a8..8874e87208a 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js @@ -6,15 +6,40 @@ //@ts-check 'use strict'; -const bootstrap = require('../../../../bootstrap'); -const bootstrapWindow = require('../../../../bootstrap-window'); +(function () { + const bootstrap = bootstrapLib(); + const bootstrapWindow = bootstrapWindowLib(); -// Avoid Monkey Patches from Application Insights -bootstrap.avoidMonkeyPatchFromAppInsights(); + // Avoid Monkey Patches from Application Insights + bootstrap.avoidMonkeyPatchFromAppInsights(); -bootstrapWindow.load(['vs/code/electron-browser/sharedProcess/sharedProcessMain'], function (sharedProcess, configuration) { - sharedProcess.startup({ - machineId: configuration.machineId, - windowId: configuration.windowId + // Load shared process into window + bootstrapWindow.load(['vs/code/electron-browser/sharedProcess/sharedProcessMain'], function (sharedProcess, configuration) { + sharedProcess.startup({ + machineId: configuration.machineId, + windowId: configuration.windowId + }); }); -}); + + + //#region Globals + + /** + * @returns {{ avoidMonkeyPatchFromAppInsights: () => void; }} + */ + function bootstrapLib() { + // @ts-ignore (defined in bootstrap.js) + return window.MonacoBootstrap; + } + + /** + * @returns {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options?: object) => unknown }} + */ + function bootstrapWindowLib() { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; + } + + //#endregion + +}()); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index a28a1ea8d6c..88f3869f8a3 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -10,24 +10,24 @@ import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ExtensionManagementChannel, GlobalExtensionEnablementServiceClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { ExtensionManagementChannel, ExtensionTipsChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/browser/requestService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; +import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; -import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker'; -import { ipcRenderer } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/log'; import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; @@ -35,33 +35,43 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza import { combinedDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; -import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel, StaticRouter, createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/common/ipc'; import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner'; import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; -import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; -import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; -import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAuthTokenServiceChannel, UserDataAutoSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel, StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; -import { UserDataAutoSync } from 'vs/platform/userDataSync/electron-browser/userDataAutoSync'; -import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; -import { UserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataAuthTokenService'; +import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService'; +import { NativeStorageService } from 'vs/platform/storage/node/storageService'; +import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; +import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; +import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; +import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; +import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; +import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; +import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; +import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; +import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -74,16 +84,22 @@ export function startup(configuration: ISharedProcessConfiguration) { interface ISharedProcessInitData { sharedIPCHandle: string; - args: ParsedArgs; + args: NativeParsedArgs; logLevel: LogLevel; + nodeCachedDataDir?: string; + backupWorkspacesPath: string; } const eventPrefix = 'monacoworkbench'; class MainProcessService implements IMainProcessService { - constructor(private server: Server, private mainRouter: StaticRouter) { } - _serviceBrand: undefined; + constructor( + private server: Server, + private mainRouter: StaticRouter + ) { } + + declare readonly _serviceBrand: undefined; getChannel(channelName: string): IChannel { return this.server.getChannel(channelName, this.mainRouter); @@ -101,11 +117,11 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const onExit = () => disposables.dispose(); process.once('exit', onExit); - ipcRenderer.once('handshake:goodbye', onExit); + ipcRenderer.once('vscode:electron-main->shared-process=exit', onExit); disposables.add(server); - const environmentService = new EnvironmentService(initData.args, process.execPath); + const environmentService = new NativeEnvironmentService(initData.args); const mainRouter = new StaticRouter(ctx => ctx === 'main'); const loggerClient = new LoggerChannelClient(server.getChannel('logger', mainRouter)); @@ -116,55 +132,62 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const mainProcessService = new MainProcessService(server, mainRouter); services.set(IMainProcessService, mainProcessService); - const configurationService = new ConfigurationService(environmentService.settingsResource); + // Files + const fileService = new FileService(logService); + services.set(IFileService, fileService); + disposables.add(fileService); + const diskFileSystemProvider = new DiskFileSystemProvider(logService); + disposables.add(diskFileSystemProvider); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + + // Configuration + const configurationService = new ConfigurationService(environmentService.settingsResource, fileService); disposables.add(configurationService); await configurationService.initialize(); + // Storage + const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService); + await storageService.initialize(); + services.set(IStorageService, storageService); + disposables.add(toDisposable(() => storageService.flush())); + + services.set(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService)); services.set(IEnvironmentService, environmentService); + services.set(INativeEnvironmentService, environmentService); + services.set(IProductService, { _serviceBrand: undefined, ...product }); services.set(ILogService, logService); services.set(IConfigurationService, configurationService); services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(ILoggerService, new SyncDescriptor(LoggerService)); - - const electronService = createChannelSender(mainProcessService.getChannel('electron'), { context: configuration.windowId }); - services.set(IElectronService, electronService); - - const activeWindowManager = new ActiveWindowManager(electronService); + const nativeHostService = createChannelSender(mainProcessService.getChannel('nativeHost'), { context: configuration.windowId }); + services.set(INativeHostService, nativeHostService); + const activeWindowManager = new ActiveWindowManager(nativeHostService); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); - // Files - const fileService = new FileService(logService); - services.set(IFileService, fileService); - disposables.add(fileService); - - const diskFileSystemProvider = new DiskFileSystemProvider(logService); - disposables.add(diskFileSystemProvider); - fileService.registerProvider(Schemas.file, diskFileSystemProvider); - services.set(IDownloadService, new SyncDescriptor(DownloadService)); + services.set(IExtensionRecommendationNotificationService, new ExtensionRecommendationNotificationServiceChannelClient(server.getChannel('IExtensionRecommendationNotificationService', activeWindowRouter))); const instantiationService = new InstantiationService(services); let telemetryService: ITelemetryService; instantiationService.invokeFunction(accessor => { const services = new ServiceCollection(); - const environmentService = accessor.get(IEnvironmentService); - const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; - const telemetryLogService = new FollowerLogService(loggerClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel)); - telemetryLogService.info('The below are logs for every telemetry event sent from VS Code once the log level is set to trace.'); - telemetryLogService.info('==========================================================='); + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; - let appInsightsAppender: ITelemetryAppender | null = NullAppender; - if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + let telemetryAppender: ITelemetryAppender = NullAppender; + if (!extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) { + telemetryAppender = new TelemetryLogAppender(accessor.get(ILoggerService), environmentService); if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { - appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); + const appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey); disposables.add(toDisposable(() => appInsightsAppender!.flush())); // Ensure the AI appender is disposed so that it flushes remaining data + telemetryAppender = combinedAppender(appInsightsAppender, telemetryAppender); } const config: ITelemetryServiceConfig = { - appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), + appender: telemetryAppender, commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath), + sendErrorTelemetry: true, piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] }; @@ -174,20 +197,29 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat telemetryService = NullTelemetryService; services.set(ITelemetryService, NullTelemetryService); } - server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); + server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(telemetryAppender)); + + const storageKeysSyncRegistryService = accessor.get(IStorageKeysSyncRegistryService); + const storageKeysSyncChannel = new StorageKeysSyncRegistryChannel(storageKeysSyncRegistryService); + server.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); + services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); - services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); - services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService)); + services.set(IUserDataSyncAccountService, new SyncDescriptor(UserDataSyncAccountService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); - services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter))); - services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', activeWindowRouter))); + services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); + services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService)); + services.set(IIgnoredExtensionsManagementService, new SyncDescriptor(IgnoredExtensionsManagementService)); + services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService)); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); - services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser)); + services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService)); + services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService)); + services.set(IUserDataAutoSyncEnablementService, new SyncDescriptor(UserDataAutoSyncEnablementService)); + services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); registerConfiguration(); @@ -204,22 +236,30 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat server.registerChannel('localizations', localizationsChannel); const diagnosticsService = accessor.get(IDiagnosticsService); - const diagnosticsChannel = new DiagnosticsChannel(diagnosticsService); + const diagnosticsChannel = createChannelReceiver(diagnosticsService); server.registerChannel('diagnostics', diagnosticsChannel); - const authTokenService = accessor.get(IUserDataAuthTokenService); - const authTokenChannel = new UserDataAuthTokenServiceChannel(authTokenService); - server.registerChannel('authToken', authTokenChannel); + const extensionTipsService = accessor.get(IExtensionTipsService); + const extensionTipsChannel = new ExtensionTipsChannel(extensionTipsService); + server.registerChannel('extensionTipsService', extensionTipsChannel); - const settingsSyncService = accessor.get(ISettingsSyncService); - const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService); - server.registerChannel('settingsSync', settingsSyncChannel); + const userDataSyncMachinesService = accessor.get(IUserDataSyncMachinesService); + const userDataSyncMachineChannel = new UserDataSyncMachinesServiceChannel(userDataSyncMachinesService); + server.registerChannel('userDataSyncMachines', userDataSyncMachineChannel); + + const authTokenService = accessor.get(IUserDataSyncAccountService); + const authTokenChannel = new UserDataSyncAccountServiceChannel(authTokenService); + server.registerChannel('userDataSyncAccount', authTokenChannel); + + const userDataSyncStoreManagementService = accessor.get(IUserDataSyncStoreManagementService); + const userDataSyncStoreManagementChannel = new UserDataSyncStoreManagementServiceChannel(userDataSyncStoreManagementService); + server.registerChannel('userDataSyncStoreManagement', userDataSyncStoreManagementChannel); const userDataSyncService = accessor.get(IUserDataSyncService); - const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService); + const userDataSyncChannel = new UserDataSyncChannel(server, userDataSyncService, logService); server.registerChannel('userDataSync', userDataSyncChannel); - const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSync); + const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSyncService); const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync); server.registerChannel('userDataAutoSync', userDataAutoSyncChannel); @@ -229,9 +269,9 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat (localizationsService as LocalizationsService).update(); // cache clean ups disposables.add(combinedDisposable( - instantiationService2.createInstance(NodeCachedDataCleaner), + new NodeCachedDataCleaner(initData.nodeCachedDataDir), instantiationService2.createInstance(LanguagePackCachedDataCleaner), - instantiationService2.createInstance(StorageDataCleaner), + instantiationService2.createInstance(StorageDataCleaner, initData.backupWorkspacesPath), instantiationService2.createInstance(LogsDataCleaner), userDataAutoSync )); @@ -275,13 +315,20 @@ function setupIPC(hook: string): Promise { } async function handshake(configuration: ISharedProcessConfiguration): Promise { + + // receive payload from electron-main to start things const data = await new Promise(c => { - ipcRenderer.once('handshake:hey there', (_: any, r: ISharedProcessInitData) => c(r)); - ipcRenderer.send('handshake:hello'); + ipcRenderer.once('vscode:electron-main->shared-process=payload', (event: unknown, r: ISharedProcessInitData) => c(r)); + + // tell electron-main we are ready to receive payload + ipcRenderer.send('vscode:shared-process->electron-main=ready-for-payload'); }); + // await IPC connection and signal this back to electron-main const server = await setupIPC(data.sharedIPCHandle); + ipcRenderer.send('vscode:shared-process->electron-main=ipc-ready'); + // await initialization and signal this back to electron-main await main(server, data, configuration); - ipcRenderer.send('handshake:im ready'); + ipcRenderer.send('vscode:shared-process->electron-main=init-done'); } diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index 693082bb9ed..40737461d29 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -3,11 +3,16 @@ - + - + + + + + + diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 022394edd75..3dd6691d3a0 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -3,152 +3,189 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + //@ts-check 'use strict'; -const perf = require('../../../base/common/performance'); -perf.mark('renderer/started'); +(function () { -const bootstrapWindow = require('../../../../bootstrap-window'); + // Add a perf entry right from the top + const perf = perfLib(); + perf.mark('renderer/started'); -// Setup shell environment -process['lazyEnv'] = getLazyEnv(); + // Load environment in parallel to workbench loading to avoid waterfall + const bootstrapWindow = bootstrapWindowLib(); + const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved(); -// Load workbench main JS, CSS and NLS all in parallel. This is an -// optimization to prevent a waterfall of loading to happen, because -// we know for a fact that workbench.desktop.main will depend on -// the related CSS and NLS counterparts. -bootstrapWindow.load([ - 'vs/workbench/workbench.desktop.main', - 'vs/nls!vs/workbench/workbench.desktop.main', - 'vs/css!vs/workbench/workbench.desktop.main' -], - function (workbench, configuration) { - perf.mark('didLoadWorkbenchMain'); + // Load workbench main JS, CSS and NLS all in parallel. This is an + // optimization to prevent a waterfall of loading to happen, because + // we know for a fact that workbench.desktop.main will depend on + // the related CSS and NLS counterparts. + bootstrapWindow.load([ + 'vs/workbench/workbench.desktop.main', + 'vs/nls!vs/workbench/workbench.desktop.main', + 'vs/css!vs/workbench/workbench.desktop.main' + ], + async function (workbench, configuration) { + + // Mark start of workbench + perf.mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); + + // Wait for process environment being fully resolved + await whenEnvResolved; - return process['lazyEnv'].then(function () { perf.mark('main/startup'); // @ts-ignore return require('vs/workbench/electron-browser/desktop.main').main(configuration); - }); - }, { - removeDeveloperKeybindingsAfterLoad: true, - canModifyDOM: function (windowConfig) { - showPartsSplash(windowConfig); - }, - beforeLoaderConfig: function (windowConfig, loaderConfig) { - loaderConfig.recordStats = true; - }, - beforeRequire: function () { - perf.mark('willLoadWorkbenchMain'); - } -}); - -/** - * @param {{ - * partsSplashPath?: string, - * highContrast?: boolean, - * extensionDevelopmentPath?: string[], - * folderUri?: object, - * workspace?: object - * }} configuration - */ -function showPartsSplash(configuration) { - perf.mark('willShowPartsSplash'); - - let data; - if (typeof configuration.partsSplashPath === 'string') { - try { - data = JSON.parse(require('fs').readFileSync(configuration.partsSplashPath, 'utf8')); - } catch (e) { - // ignore + }, + { + removeDeveloperKeybindingsAfterLoad: true, + canModifyDOM: function (windowConfig) { + showPartsSplash(windowConfig); + }, + beforeLoaderConfig: function (windowConfig, loaderConfig) { + loaderConfig.recordStats = true; + }, + beforeRequire: function () { + perf.mark('willLoadWorkbenchMain'); + } } + ); + + + //region Helpers + + function perfLib() { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + } + }; } - // high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts - if (data && configuration.highContrast && data.baseTheme !== 'hc-black') { - data = undefined; + /** + * @returns {{ + * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * }} + */ + function bootstrapWindowLib() { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; } - // developing an extension -> ignore stored layouts - if (data && configuration.extensionDevelopmentPath) { - data.layoutInfo = undefined; - } + /** + * @param {{ + * partsSplashPath?: string, + * colorScheme: ('light' | 'dark' | 'hc'), + * autoDetectHighContrast?: boolean, + * extensionDevelopmentPath?: string[], + * folderUri?: object, + * workspace?: object + * }} configuration + */ + function showPartsSplash(configuration) { + perf.mark('willShowPartsSplash'); - // minimal color configuration (works with or without persisted data) - const baseTheme = data ? data.baseTheme : configuration.highContrast ? 'hc-black' : 'vs-dark'; - const shellBackground = data ? data.colorInfo.editorBackground : configuration.highContrast ? '#000000' : '#1E1E1E'; - const shellForeground = data ? data.colorInfo.foreground : configuration.highContrast ? '#FFFFFF' : '#CCCCCC'; - const style = document.createElement('style'); - style.className = 'initialShellColors'; - document.head.appendChild(style); - document.body.className = baseTheme; - style.innerHTML = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; - - if (data && data.layoutInfo) { - // restore parts if possible (we might not always store layout info) - const { id, layoutInfo, colorInfo } = data; - const splash = document.createElement('div'); - splash.id = id; - - if (layoutInfo.windowBorder) { - splash.style.position = 'relative'; - splash.style.height = 'calc(100vh - 2px)'; - splash.style.width = 'calc(100vw - 2px)'; - splash.style.border = '1px solid var(--window-border-color)'; - splash.style.setProperty('--window-border-color', colorInfo.windowBorder); - - if (layoutInfo.windowBorderRadius) { - splash.style.borderRadius = layoutInfo.windowBorderRadius; + let data; + if (typeof configuration.partsSplashPath === 'string') { + try { + data = JSON.parse(require.__$__nodeRequire('fs').readFileSync(configuration.partsSplashPath, 'utf8')); + } catch (e) { + // ignore } } - // ensure there is enough space - layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth)); - - if (configuration.folderUri || configuration.workspace) { - // folder or workspace -> status bar color, sidebar - splash.innerHTML = ` -
    -
    -
    -
    - `; - } else { - // empty -> speical status bar color, no sidebar - splash.innerHTML = ` -
    -
    -
    - `; + // high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts + const isHighContrast = configuration.colorScheme === 'hc' /* ColorScheme.HIGH_CONTRAST */ && configuration.autoDetectHighContrast; + if (data && isHighContrast && data.baseTheme !== 'hc-black') { + data = undefined; } - document.body.appendChild(splash); + + // developing an extension -> ignore stored layouts + if (data && configuration.extensionDevelopmentPath) { + data.layoutInfo = undefined; + } + + // minimal color configuration (works with or without persisted data) + let baseTheme, shellBackground, shellForeground; + if (data) { + baseTheme = data.baseTheme; + shellBackground = data.colorInfo.editorBackground; + shellForeground = data.colorInfo.foreground; + } else if (isHighContrast) { + baseTheme = 'hc-black'; + shellBackground = '#000000'; + shellForeground = '#FFFFFF'; + } else { + baseTheme = 'vs-dark'; + shellBackground = '#1E1E1E'; + shellForeground = '#CCCCCC'; + } + const style = document.createElement('style'); + style.className = 'initialShellColors'; + document.head.appendChild(style); + style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; + + if (data && data.layoutInfo) { + // restore parts if possible (we might not always store layout info) + const { id, layoutInfo, colorInfo } = data; + const splash = document.createElement('div'); + splash.id = id; + splash.className = baseTheme; + + if (layoutInfo.windowBorder) { + splash.style.position = 'relative'; + splash.style.height = 'calc(100vh - 2px)'; + splash.style.width = 'calc(100vw - 2px)'; + splash.style.border = '1px solid var(--window-border-color)'; + splash.style.setProperty('--window-border-color', colorInfo.windowBorder); + + if (layoutInfo.windowBorderRadius) { + splash.style.borderRadius = layoutInfo.windowBorderRadius; + } + } + + // ensure there is enough space + layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth)); + + // part: title + const titleDiv = document.createElement('div'); + titleDiv.setAttribute('style', `position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground}; -webkit-app-region: drag;`); + splash.appendChild(titleDiv); + + // part: activity bar + const activityDiv = document.createElement('div'); + activityDiv.setAttribute('style', `position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};`); + splash.appendChild(activityDiv); + + // part: side bar (only when opening workspace/folder) + if (configuration.folderUri || configuration.workspace) { + // folder or workspace -> status bar color, sidebar + const sideDiv = document.createElement('div'); + sideDiv.setAttribute('style', `position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: ${layoutInfo.activityBarWidth}px; width: ${layoutInfo.sideBarWidth}px; background-color: ${colorInfo.sideBarBackground};`); + splash.appendChild(sideDiv); + } + + // part: statusbar + const statusDiv = document.createElement('div'); + statusDiv.setAttribute('style', `position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${configuration.folderUri || configuration.workspace ? colorInfo.statusBarBackground : colorInfo.statusBarNoFolderBackground};`); + splash.appendChild(statusDiv); + + document.body.appendChild(splash); + } + + perf.mark('didShowPartsSplash'); } - perf.mark('didShowPartsSplash'); -} - -/** - * @returns {Promise} - */ -function getLazyEnv() { - // @ts-ignore - const ipc = require('electron').ipcRenderer; - - return new Promise(function (resolve) { - const handle = setTimeout(function () { - resolve(); - console.warn('renderer did not receive lazyEnv in time'); - }, 10000); - - ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { - clearTimeout(handle); - bootstrapWindow.assign(process.env, shellEnv); - // @ts-ignore - resolve(process.env); - }); - - ipc.send('vscode:fetchShellEnv'); - }); -} + //#endregion + +}()); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 712d6f05c5b..6e753f45e78 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, IpcMainEvent, BrowserWindow } from 'electron'; +import { app, ipcMain as ipc, systemPreferences, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron'; import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService'; -import { OpenContext, IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker'; +import { IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; import { IUpdateService } from 'vs/platform/update/common/update'; @@ -22,19 +22,19 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; +import { IURLService } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryService, machineIdKey } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; +import { getDelayedChannel, StaticRouter, createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; +import { ProxyAuthHandler2 } from 'vs/code/electron-main/auth2'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { URI } from 'vs/base/common/uri'; @@ -44,57 +44,56 @@ import { getMachineId } from 'vs/base/node/id'; import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32'; import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux'; import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin'; -import { IIssueService } from 'vs/platform/issue/node/issue'; -import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainService'; +import { IssueMainService, IIssueMainService } from 'vs/platform/issue/electron-main/issueMainService'; import { LoggerChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { IMenubarService } from 'vs/platform/menubar/node/menubar'; -import { MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; +import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; -import { homedir } from 'os'; -import { join, sep } from 'vs/base/common/path'; +import { sep, posix } from 'vs/base/common/path'; +import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; -import { startsWith } from 'vs/base/common/strings'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; -import { URLService } from 'vs/platform/url/node/urlService'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; import { WorkspacesMainService, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { statSync } from 'fs'; -import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; -import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; +import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc'; +import { INativeHostMainService, NativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService'; import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; -import { assign } from 'vs/base/common/objects'; import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; +import { coalesce } from 'vs/base/common/arrays'; +import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels'; +import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { stripComments } from 'vs/base/common/json'; +import { generateUuid } from 'vs/base/common/uuid'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { EncryptionMainService, IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; +import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; export class CodeApplication extends Disposable { - - private static readonly MACHINE_ID_KEY = 'telemetry.machineId'; - private static readonly TRUE_MACHINE_ID_KEY = 'telemetry.trueMachineId'; - private windowsMainService: IWindowsMainService | undefined; private dialogMainService: IDialogMainService | undefined; + private nativeHostMainService: INativeHostMainService | undefined; constructor( private readonly mainIpcServer: Server, private readonly userEnv: IProcessEnvironment, @IInstantiationService private readonly instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStateService private readonly stateService: IStateService @@ -117,28 +116,27 @@ export class CodeApplication extends Disposable { // Contextmenu via IPC support registerContextMenuListener(); - app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); - } + // Accessibility change event + app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => { + this.windowsMainService?.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); }); - app.on('activate', (event: Event, hasVisibleWindows: boolean) => { - this.logService.trace('App#activate'); + // macOS dock activate + app.on('activate', (event, hasVisibleWindows) => { + this.logService.trace('app#activate'); // Mac only event: open new window when we get activated - if (!hasVisibleWindows && this.windowsMainService) { - this.windowsMainService.openEmptyWindow(OpenContext.DOCK); + if (!hasVisibleWindows) { + this.windowsMainService?.openEmptyWindow({ context: OpenContext.DOCK }); } }); - // Security related measures (https://electronjs.org/docs/tutorial/security) + //#region Security related measures (https://electronjs.org/docs/tutorial/security) // // !!! DO NOT CHANGE without consulting the documentation !!! // - // app.on('remote-get-guest-web-contents', event => event.preventDefault()); // TODO@Ben TODO@Matt revisit this need for app.on('remote-require', (event, sender, module) => { - this.logService.trace('App#on(remote-require): prevented'); + this.logService.trace('app#on(remote-require): prevented'); event.preventDefault(); }); @@ -168,22 +166,23 @@ export class CodeApplication extends Disposable { event.preventDefault(); }); - app.on('web-contents-created', (_event: Event, contents) => { - contents.on('will-attach-webview', (event: Event, webPreferences, params) => { + app.on('web-contents-created', (event, contents) => { + contents.on('will-attach-webview', (event, webPreferences, params) => { const isValidWebviewSource = (source: string | undefined): boolean => { if (!source) { return false; } - if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') { - return true; + const uri = URI.parse(source); + if (uri.scheme === Schemas.vscodeWebview) { + return uri.path === '/index.html' || uri.path === '/electron-browser/index.html'; } - const srcUri = URI.parse(source).fsPath.toLowerCase(); + const srcUri = uri.fsPath.toLowerCase(); const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase(); - return startsWith(srcUri, rootUri + sep); + return srcUri.startsWith(rootUri + sep); }; // Ensure defaults @@ -196,7 +195,7 @@ export class CodeApplication extends Disposable { return; } - delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553 + delete (webPreferences as { preloadURL: string | undefined }).preloadURL; // https://github.com/electron/electron/issues/21553 // Otherwise prevent loading this.logService.error('webContents#web-contents-created: Prevented webview attach'); @@ -210,17 +209,27 @@ export class CodeApplication extends Disposable { event.preventDefault(); }); - contents.on('new-window', (event: Event, url: string) => { + contents.on('new-window', (event, url) => { event.preventDefault(); // prevent code that wants to open links - shell.openExternal(url); + this.nativeHostMainService?.openExternal(undefined, url); + }); + + session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => { + return callback(false); + }); + + session.defaultSession.setPermissionCheckHandler((webContents, permission /* 'media' */) => { + return false; }); }); + //#endregion + let macOpenFileURIs: IWindowOpenable[] = []; let runningTimeout: NodeJS.Timeout | null = null; - app.on('open-file', (event: Event, path: string) => { - this.logService.trace('App#open-file: ', path); + app.on('open-file', (event, path) => { + this.logService.trace('app#open-file: ', path); event.preventDefault(); // Keep in array because more might come! @@ -234,32 +243,21 @@ export class CodeApplication extends Disposable { // Handle paths delayed in case more are coming! runningTimeout = setTimeout(() => { - if (this.windowsMainService) { - this.windowsMainService.open({ - context: OpenContext.DOCK /* can also be opening from finder while app is running */, - cli: this.environmentService.args, - urisToOpen: macOpenFileURIs, - gotoLineMode: false, - preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ - }); + this.windowsMainService?.open({ + context: OpenContext.DOCK /* can also be opening from finder while app is running */, + cli: this.environmentService.args, + urisToOpen: macOpenFileURIs, + gotoLineMode: false, + preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ + }); - macOpenFileURIs = []; - runningTimeout = null; - } + macOpenFileURIs = []; + runningTimeout = null; }, 100); }); app.on('new-window-for-tab', () => { - if (this.windowsMainService) { - this.windowsMainService.openEmptyWindow(OpenContext.DESKTOP); //macOS native tab "+" button - } - }); - - ipc.on('vscode:exit', (event: Event, code: number) => { - this.logService.trace('IPC#vscode:exit', code); - - this.dispose(); - this.lifecycleMainService.kill(code); + this.windowsMainService?.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button }); ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => { @@ -267,6 +265,7 @@ export class CodeApplication extends Disposable { try { const shellEnv = await getShellEnvironment(this.logService, this.environmentService); + if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', shellEnv); } @@ -288,19 +287,10 @@ export class CodeApplication extends Disposable { (async () => { await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); - // After waking up from sleep (after window opened) - powerMonitor.on('resume', () => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:osResume', undefined); - } - }); - // Keyboard layout changes (after window opened) const nativeKeymap = await import('native-keymap'); nativeKeymap.onDidChangeKeyboardLayout(() => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); - } + this.windowsMainService?.sendToAll('vscode:keyboardLayoutChanged'); }); })(); } @@ -315,9 +305,7 @@ export class CodeApplication extends Disposable { }; // handle on client side - if (this.windowsMainService) { - this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); - } + this.windowsMainService?.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); } this.logService.error(`[uncaught exception in main]: ${err}`); @@ -345,7 +333,7 @@ export class CodeApplication extends Disposable { // "com.microsoft.", which breaks native tabs for VS Code when using this // identifier (from the official build). // Explicitly opt out of the patch here before creating any windows. - // See: https://github.com/Microsoft/vscode/issues/35361#issuecomment-399794085 + // See: https://github.com/microsoft/vscode/issues/35361#issuecomment-399794085 try { if (isMacintosh && this.configurationService.getValue('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) { systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any); @@ -359,22 +347,29 @@ export class CodeApplication extends Disposable { // Resolve unique machine ID this.logService.trace('Resolving machine identifier...'); - const { machineId, trueMachineId } = await this.resolveMachineId(); - this.logService.trace(`Resolved machine identifier: ${machineId} (trueMachineId: ${trueMachineId})`); + const machineId = await this.resolveMachineId(); + this.logService.trace(`Resolved machine identifier: ${machineId}`); // Spawn shared process after the first window has opened and 3s have passed const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv); - const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main')); + const sharedProcessClient = sharedProcess.whenIpcReady().then(() => { + this.logService.trace('Shared process: IPC ready'); + + return connect(this.environmentService.sharedIPCHandle, 'main'); + }); + const sharedProcessReady = sharedProcess.whenReady().then(() => { + this.logService.trace('Shared process: init ready'); + + return sharedProcessClient; + }); this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { - const userEnv = await getShellEnvironment(this.logService, this.environmentService); - - sharedProcess.spawn(userEnv); + sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService)); }, 3000)).schedule(); }); // Services - const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessClient); + const appInstantiationService = await this.createServices(machineId, sharedProcess, sharedProcessReady); // Create driver if (this.environmentService.driverHandle) { @@ -384,14 +379,18 @@ export class CodeApplication extends Disposable { this._register(server); } - // Setup Auth Handler - this._register(new ProxyAuthHandler()); + // Setup Auth Handler (TODO@ben remove old auth handler eventually) + if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') !== true) { + this._register(new ProxyAuthHandler()); + } else { + this._register(appInstantiationService.createInstance(ProxyAuthHandler2)); + } // Open Windows const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient)); // Post Open Windows Tasks - this.afterWindowOpen(); + appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); // Tracing: Stop tracing after windows are ready if enabled if (this.environmentService.args.trace) { @@ -399,40 +398,23 @@ export class CodeApplication extends Disposable { } } - private async resolveMachineId(): Promise<{ machineId: string, trueMachineId?: string }> { + private async resolveMachineId(): Promise { // We cache the machineId for faster lookups on startup - // and resolve it only once initially if not cached - let machineId = this.stateService.getItem(CodeApplication.MACHINE_ID_KEY); - if (!machineId) { + // and resolve it only once initially if not cached or we need to replace the macOS iBridge device + let machineId = this.stateService.getItem(machineIdKey); + if (!machineId || (isMacintosh && machineId === '6c9d2bc8f91b89624add29c0abeae7fb42bf539fa1cdb2e3e57cd668fa9bcead')) { machineId = await getMachineId(); - this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId); + this.stateService.setItem(machineIdKey, machineId); } - // Check if machineId is hashed iBridge Device - let trueMachineId: string | undefined; - if (isMacintosh && machineId === '6c9d2bc8f91b89624add29c0abeae7fb42bf539fa1cdb2e3e57cd668fa9bcead') { - trueMachineId = this.stateService.getItem(CodeApplication.TRUE_MACHINE_ID_KEY); - if (!trueMachineId) { - trueMachineId = await getMachineId(); - - this.stateService.setItem(CodeApplication.TRUE_MACHINE_ID_KEY, trueMachineId); - } - } - - return { machineId, trueMachineId }; + return machineId; } - private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessClient: Promise>): Promise { + private async createServices(machineId: string, sharedProcess: SharedProcess, sharedProcessReady: Promise>): Promise { const services = new ServiceCollection(); - const fileService = this._register(new FileService(this.logService)); - services.set(IFileService, fileService); - - const diskFileSystemProvider = this._register(new DiskFileSystemProvider(this.logService)); - fileService.registerProvider(Schemas.file, diskFileSystemProvider); - switch (process.platform) { case 'win32': services.set(IUpdateService, new SyncDescriptor(Win32UpdateService)); @@ -455,14 +437,14 @@ export class CodeApplication extends Disposable { services.set(IDialogMainService, new SyncDescriptor(DialogMainService)); services.set(ISharedProcessMainService, new SyncDescriptor(SharedProcessMainService, [sharedProcess])); services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); + services.set(IDiagnosticsService, createChannelSender(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics'))))); - const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics'))); - services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel])); - - services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); - services.set(IElectronMainService, new SyncDescriptor(ElectronMainService)); + services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); + services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId])); + services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService)); + services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService)); services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService)); - services.set(IMenubarService, new SyncDescriptor(MenubarMainService)); + services.set(IMenubarMainService, new SyncDescriptor(MenubarMainService)); const storageMainService = new StorageMainService(this.logService, this.environmentService); services.set(IStorageMainService, storageMainService); @@ -472,16 +454,16 @@ export class CodeApplication extends Disposable { services.set(IBackupMainService, backupMainService); services.set(IWorkspacesHistoryMainService, new SyncDescriptor(WorkspacesHistoryMainService)); - services.set(IURLService, new SyncDescriptor(URLService)); + services.set(IURLService, new SyncDescriptor(NativeURLService)); services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); // Telemetry if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender'))); - const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)); + const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); + const appender = new TelemetryAppenderClient(channel); const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; - const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, trueMachineId }; + const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { @@ -505,17 +487,15 @@ export class CodeApplication extends Disposable { recordingStopped = true; // only once - const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`)); + const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); if (!timeout) { - if (this.dialogMainService) { - this.dialogMainService.showMessageBox({ - type: 'info', - message: localize('trace.message', "Successfully created trace."), - detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), - buttons: [localize('trace.ok', "Ok")] - }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); - } + this.dialogMainService?.showMessageBox({ + type: 'info', + message: localize('trace.message', "Successfully created trace."), + detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), + buttons: [localize('trace.ok', "OK")] + }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); } else { this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); } @@ -543,14 +523,18 @@ export class CodeApplication extends Disposable { const updateChannel = new UpdateChannel(updateService); electronIpcServer.registerChannel('update', updateChannel); - const issueService = accessor.get(IIssueService); - const issueChannel = createChannelReceiver(issueService); + const issueMainService = accessor.get(IIssueMainService); + const issueChannel = createChannelReceiver(issueMainService); electronIpcServer.registerChannel('issue', issueChannel); - const electronMainService = accessor.get(IElectronMainService); - const electronChannel = createChannelReceiver(electronMainService); - electronIpcServer.registerChannel('electron', electronChannel); - sharedProcessClient.then(client => client.registerChannel('electron', electronChannel)); + const encryptionMainService = accessor.get(IEncryptionMainService); + const encryptionChannel = createChannelReceiver(encryptionMainService); + electronIpcServer.registerChannel('encryption', encryptionChannel); + + const nativeHostMainService = this.nativeHostMainService = accessor.get(INativeHostMainService); + const nativeHostChannel = createChannelReceiver(this.nativeHostMainService); + electronIpcServer.registerChannel('nativeHost', nativeHostChannel); + sharedProcessClient.then(client => client.registerChannel('nativeHost', nativeHostChannel)); const sharedProcessMainService = accessor.get(ISharedProcessMainService); const sharedProcessChannel = createChannelReceiver(sharedProcessMainService); @@ -560,25 +544,29 @@ export class CodeApplication extends Disposable { const workspacesChannel = createChannelReceiver(workspacesService); electronIpcServer.registerChannel('workspaces', workspacesChannel); - const menubarService = accessor.get(IMenubarService); - const menubarChannel = createChannelReceiver(menubarService); + const menubarMainService = accessor.get(IMenubarMainService); + const menubarChannel = createChannelReceiver(menubarMainService); electronIpcServer.registerChannel('menubar', menubarChannel); const urlService = accessor.get(IURLService); const urlChannel = createChannelReceiver(urlService); electronIpcServer.registerChannel('url', urlChannel); + const webviewManagerService = accessor.get(IWebviewManagerService); + const webviewChannel = createChannelReceiver(webviewManagerService); + electronIpcServer.registerChannel('webview', webviewChannel); + const storageMainService = accessor.get(IStorageMainService); const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService)); electronIpcServer.registerChannel('storage', storageChannel); + sharedProcessClient.then(client => client.registerChannel('storage', storageChannel)); const loggerChannel = new LoggerChannel(accessor.get(ILogService)); electronIpcServer.registerChannel('logger', loggerChannel); sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel)); - const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); - // ExtensionHost Debug broadcast service + const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ElectronExtensionHostDebugBroadcastChannel(windowsMainService)); // Signal phase: ready (services set) @@ -587,63 +575,98 @@ export class CodeApplication extends Disposable { // Propagate to clients this.dialogMainService = accessor.get(IDialogMainService); + // Check for initial URLs to handle from protocol link invocations + const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; + const pendingProtocolLinksToHandle = coalesce([ + + // Windows/Linux: protocol handler invokes CLI with --open-url + ...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [], + + // macOS: open-url events + ...((global).getOpenUrls() || []) as string[] + ].map(pendingUrlToHandle => { + try { + return URI.parse(pendingUrlToHandle); + } catch (error) { + return undefined; + } + })).filter(pendingUriToHandle => { + // if URI should be blocked, filter it out + if (this.shouldBlockURI(pendingUriToHandle)) { + return false; + } + + // filter out any protocol link that wants to open as window so that + // we open the right set of windows on startup and not restore the + // previous workspace too. + const windowOpenable = this.getWindowOpenableFromProtocolLink(pendingUriToHandle); + if (windowOpenable) { + pendingWindowOpenablesFromProtocolLinks.push(windowOpenable); + + return false; + } + + return true; + }); + // Create a URL handler to open file URIs in the active window - const environmentService = accessor.get(IEnvironmentService); + const app = this; + const environmentService = this.environmentService; urlService.registerHandler({ - async handleURL(uri: URI, options?: IOpenURLOptions): Promise { + async handleURL(uri: URI): Promise { + // if URI should be blocked, behave as if it's handled + if (app.shouldBlockURI(uri)) { + return true; + } - // Catch file URLs - if (uri.authority === Schemas.file && !!uri.path) { - const cli = assign(Object.create(null), environmentService.args); - - // hey Ben, we need to convert this `code://file` URI into a `file://` URI - const urisToOpen = [{ fileUri: URI.file(uri.fsPath) }]; - - windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true }); + // Check for URIs to open in window + const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri); + if (windowOpenableFromProtocolLink) { + windowsMainService.open({ + context: OpenContext.API, + cli: { ...environmentService.args }, + urisToOpen: [windowOpenableFromProtocolLink], + gotoLineMode: true + }); return true; } + // If we have not yet handled the URI and we have no window opened (macOS only) + // we first open a window and then try to open that URI within that window + if (isMacintosh && windowsMainService.getWindowCount() === 0) { + const [window] = windowsMainService.open({ + context: OpenContext.API, + cli: { ...environmentService.args }, + forceEmpty: true, + gotoLineMode: true + }); + + await window.ready(); + + return urlService.open(uri); + } + return false; } }); // Create a URL handler which forwards to the last active window - const activeWindowManager = new ActiveWindowManager(electronMainService); + const activeWindowManager = new ActiveWindowManager({ + onDidOpenWindow: nativeHostMainService.onDidOpenWindow, + onDidFocusWindow: nativeHostMainService.onDidFocusWindow, + getActiveWindowId: () => nativeHostMainService.getActiveWindowId(-1) + }); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter); const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter); - const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel); - - // On Mac, Code can be running without any open windows, so we must create a window to handle urls, - // if there is none - if (isMacintosh) { - urlService.registerHandler({ - async handleURL(uri: URI, options?: IOpenURLOptions): Promise { - if (windowsMainService.getWindowCount() === 0) { - const cli = { ...environmentService.args }; - const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true, gotoLineMode: true }); - - await window.ready(); - - return urlService.open(uri); - } - - return false; - } - }); - } - - // Register the multiple URL handler - urlService.registerHandler(multiplexURLHandler); + urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel)); // Watch Electron URLs and forward them to the UrlService - const args = this.environmentService.args; - const urls = args['open-url'] ? args._urls : []; - const urlListener = new ElectronURLListener(urls || [], urlService, windowsMainService, this.environmentService); - this._register(urlListener); + this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentService)); // Open our first window + const args = this.environmentService.args; const macOpenFiles: string[] = (global).macOpenFiles; const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; const hasCliArgs = args._.length; @@ -652,7 +675,20 @@ export class CodeApplication extends Disposable { const noRecentEntry = args['skip-add-to-recently-opened'] === true; const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined; - // new window if "-n" was used without paths + // check for a pending window to open from URI + // e.g. when running code with --open-uri from + // a protocol handler + if (pendingWindowOpenablesFromProtocolLinks.length > 0) { + return windowsMainService.open({ + context, + cli: args, + urisToOpen: pendingWindowOpenablesFromProtocolLinks, + gotoLineMode: true, + initialStartup: true + }); + } + + // new window if "-n" if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { return windowsMainService.open({ context, @@ -673,7 +709,6 @@ export class CodeApplication extends Disposable { urisToOpen: macOpenFiles.map(file => this.getWindowOpenableFromPathSync(file)), noRecentEntry, waitMarkerFileURI, - gotoLineMode: false, initialStartup: true }); } @@ -691,6 +726,63 @@ export class CodeApplication extends Disposable { }); } + private shouldBlockURI(uri: URI): boolean { + if (uri.authority === Schemas.file && isWindows) { + const res = dialog.showMessageBoxSync({ + title: product.nameLong, + type: 'question', + buttons: [ + mnemonicButtonLabel(localize({ key: 'open', comment: ['&& denotes a mnemonic'] }, "&&Yes")), + mnemonicButtonLabel(localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&No")), + ], + cancelId: 1, + message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath), product.nameShort), + detail: localize('confirmOpenDetail', "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'"), + noLink: true + }); + + if (res === 1) { + return true; + } + } + + return false; + } + + private getWindowOpenableFromProtocolLink(uri: URI): IWindowOpenable | undefined { + if (!uri.path) { + return undefined; + } + + // File path + if (uri.authority === Schemas.file) { + // we configure as fileUri, but later validation will + // make sure to open as folder or workspace if possible + return { fileUri: URI.file(uri.fsPath) }; + } + + // Remote path + else if (uri.authority === Schemas.vscodeRemote) { + // Example conversion: + // From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco + // To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco + const secondSlash = uri.path.indexOf(posix.sep, 1 /* skip over the leading slash */); + if (secondSlash !== -1) { + const authority = uri.path.substring(1, secondSlash); + const path = uri.path.substring(secondSlash); + const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query: uri.query, fragment: uri.fragment }); + + if (hasWorkspaceFileExtension(path)) { + return { workspaceUri: remoteUri }; + } else { + return { folderUri: remoteUri }; + } + } + } + + return undefined; + } + private getWindowOpenableFromPathSync(path: string): IWindowOpenable { try { const fileStat = statSync(path); @@ -708,13 +800,50 @@ export class CodeApplication extends Disposable { return { fileUri: URI.file(path) }; } - private afterWindowOpen(): void { + private async afterWindowOpen(accessor: ServicesAccessor): Promise { // Signal phase: after window open this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; // Remote Authorities this.handleRemoteAuthorities(); + + // Initialize update service + const updateService = accessor.get(IUpdateService); + if (updateService instanceof Win32UpdateService || updateService instanceof LinuxUpdateService || updateService instanceof DarwinUpdateService) { + updateService.initialize(); + } + + // If enable-crash-reporter argv is undefined then this is a fresh start, + // based on telemetry.enableCrashreporter settings, generate a UUID which + // will be used as crash reporter id and also update the json file. + try { + const fileService = accessor.get(IFileService); + const argvContent = await fileService.readFile(this.environmentService.argvResource); + const argvString = argvContent.value.toString(); + const argvJSON = JSON.parse(stripComments(argvString)); + if (argvJSON['enable-crash-reporter'] === undefined) { + const enableCrashReporter = this.configurationService.getValue('telemetry.enableCrashReporter') ?? true; + const additionalArgvContent = [ + '', + ' // Allows to disable crash reporting.', + ' // Should restart the app if the value is changed.', + ` "enable-crash-reporter": ${enableCrashReporter},`, + '', + ' // Unique id used for correlating crash reports sent from this instance.', + ' // Do not edit this value.', + ` "crash-reporter-id": "${generateUuid()}"`, + '}' + ]; + const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n')); + await fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); + } + } catch (error) { + this.logService.error(error); + } + + // Start to fetch shell environment after window has opened + getShellEnvironment(this.logService, this.environmentService); } private handleRemoteAuthorities(): void { @@ -726,28 +855,3 @@ export class CodeApplication extends Disposable { }); } } - -class ElectronExtensionHostDebugBroadcastChannel extends ExtensionHostDebugBroadcastChannel { - - constructor(private windowsMainService: IWindowsMainService) { - super(); - } - - call(ctx: TContext, command: string, arg?: any): Promise { - if (command === 'openExtensionDevelopmentHostWindow') { - const env = arg[1]; - const pargs = parseArgs(arg[0], OPTIONS); - const extDevPaths = pargs.extensionDevelopmentPath; - if (extDevPaths) { - this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, { - context: OpenContext.API, - cli: pargs, - userEnv: Object.keys(env).length > 0 ? env : undefined - }); - } - return Promise.resolve(); - } else { - return super.call(ctx, command, arg); - } - } -} diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index 45229b3bdd8..b4096018623 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -6,7 +6,8 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; -import { BrowserWindow, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron'; +import { FileAccess } from 'vs/base/common/network'; +import { BrowserWindow, BrowserWindowConstructorOptions, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron'; type LoginEvent = { event: ElectronEvent; @@ -23,7 +24,7 @@ type Credentials = { export class ProxyAuthHandler extends Disposable { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private retryCount = 0; @@ -49,17 +50,22 @@ export class ProxyAuthHandler extends Disposable { event.preventDefault(); - const opts: any = { + const opts: BrowserWindowConstructorOptions = { alwaysOnTop: true, skipTaskbar: true, resizable: false, width: 450, - height: 220, + height: 225, show: true, title: 'VS Code', webPreferences: { - nodeIntegration: true, - webviewTag: true + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, + sandbox: true, + contextIsolation: true, + enableWebSQL: false, + enableRemoteModule: false, + spellcheck: false, + devTools: false } }; @@ -70,24 +76,27 @@ export class ProxyAuthHandler extends Disposable { } const win = new BrowserWindow(opts); - const config = {}; - const baseUrl = require.toUrl('vs/code/electron-browser/proxy/auth.html'); - const url = `${baseUrl}?config=${encodeURIComponent(JSON.stringify(config))}`; + const windowUrl = FileAccess.asBrowserUri('vs/code/electron-sandbox/proxy/auth.html', require); const proxyUrl = `${authInfo.host}:${authInfo.port}`; const title = localize('authRequire', "Proxy Authentication Required"); const message = localize('proxyauth', "The proxy {0} requires authentication.", proxyUrl); - const data = { title, message }; - const javascript = 'promptForCredentials(' + JSON.stringify(data) + ')'; const onWindowClose = () => cb('', ''); win.on('close', onWindowClose); win.setMenu(null); - win.loadURL(url); - win.webContents.executeJavaScript(javascript, true).then(({ username, password }: Credentials) => { - cb(username, password); - win.removeListener('close', onWindowClose); - win.close(); + win.webContents.on('did-finish-load', () => { + const data = { title, message }; + win.webContents.send('vscode:openProxyAuthDialog', data); }); + win.webContents.on('ipc-message', (event, channel, credentials: Credentials) => { + if (channel === 'vscode:proxyAuthResponse') { + const { username, password } = credentials; + cb(username, password); + win.removeListener('close', onWindowClose); + win.close(); + } + }); + win.loadURL(windowUrl.toString(true)); } } diff --git a/src/vs/code/electron-main/auth2.ts b/src/vs/code/electron-main/auth2.ts new file mode 100644 index 00000000000..1bce044577e --- /dev/null +++ b/src/vs/code/electron-main/auth2.ts @@ -0,0 +1,241 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; +import { app, AuthInfo, WebContents, Event as ElectronEvent, AuthenticationResponseDetails } from 'electron'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService'; +import { IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; +import { generateUuid } from 'vs/base/common/uuid'; +import product from 'vs/platform/product/common/product'; + +interface ElectronAuthenticationResponseDetails extends AuthenticationResponseDetails { + firstAuthAttempt?: boolean; // https://github.com/electron/electron/blob/84a42a050e7d45225e69df5bd2d2bf9f1037ea41/shell/browser/login_handler.cc#L70 +} + +type LoginEvent = { + event: ElectronEvent; + authInfo: AuthInfo; + req: ElectronAuthenticationResponseDetails; + + callback: (username?: string, password?: string) => void; +}; + +type Credentials = { + username: string; + password: string; +}; + +enum ProxyAuthState { + + /** + * Initial state: we will try to use stored credentials + * first to reply to the auth challenge. + */ + Initial = 1, + + /** + * We used stored credentials and are still challenged, + * so we will show a login dialog next. + */ + StoredCredentialsUsed, + + /** + * Finally, if we showed a login dialog already, we will + * not show any more login dialogs until restart to reduce + * the UI noise. + */ + LoginDialogShown +} + +export class ProxyAuthHandler2 extends Disposable { + + private static PROXY_CREDENTIALS_SERVICE_KEY = `${product.urlProtocol}.proxy-credentials`; + + private pendingProxyResolve: Promise | undefined = undefined; + + private state = ProxyAuthState.Initial; + + private sessionCredentials: Credentials | undefined = undefined; + + constructor( + @ILogService private readonly logService: ILogService, + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, + @INativeHostMainService private readonly nativeHostMainService: INativeHostMainService, + @IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + const onLogin = Event.fromNodeEventEmitter(app, 'login', (event: ElectronEvent, webContents: WebContents, req: ElectronAuthenticationResponseDetails, authInfo: AuthInfo, callback) => ({ event, webContents, req, authInfo, callback })); + this._register(onLogin(this.onLogin, this)); + } + + private async onLogin({ event, authInfo, req, callback }: LoginEvent): Promise { + if (!authInfo.isProxy) { + return; // only for proxy + } + + if (!this.pendingProxyResolve && this.state === ProxyAuthState.LoginDialogShown && req.firstAuthAttempt) { + this.logService.trace('auth#onLogin (proxy) - exit - proxy dialog already shown'); + + return; // only one dialog per session at max (except when firstAuthAttempt: false which indicates a login problem) + } + + // Signal we handle this event on our own, otherwise + // Electron will ignore our provided credentials. + event.preventDefault(); + + let credentials: Credentials | undefined = undefined; + if (!this.pendingProxyResolve) { + this.logService.trace('auth#onLogin (proxy) - no pending proxy handling found, starting new'); + + this.pendingProxyResolve = this.resolveProxyCredentials(authInfo); + try { + credentials = await this.pendingProxyResolve; + } finally { + this.pendingProxyResolve = undefined; + } + } else { + this.logService.trace('auth#onLogin (proxy) - pending proxy handling found'); + + credentials = await this.pendingProxyResolve; + } + + // According to Electron docs, it is fine to call back without + // username or password to signal that the authentication was handled + // by us, even though without having credentials received: + // + // > If `callback` is called without a username or password, the authentication + // > request will be cancelled and the authentication error will be returned to the + // > page. + callback(credentials?.username, credentials?.password); + } + + private async resolveProxyCredentials(authInfo: AuthInfo): Promise { + this.logService.trace('auth#resolveProxyCredentials (proxy) - enter'); + + try { + const credentials = await this.doResolveProxyCredentials(authInfo); + if (credentials) { + this.logService.trace('auth#resolveProxyCredentials (proxy) - got credentials'); + + return credentials; + } else { + this.logService.trace('auth#resolveProxyCredentials (proxy) - did not get credentials'); + } + } finally { + this.logService.trace('auth#resolveProxyCredentials (proxy) - exit'); + } + + return undefined; + } + + private async doResolveProxyCredentials(authInfo: AuthInfo): Promise { + this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo); + + // Compute a hash over the authentication info to be used + // with the credentials store to return the right credentials + // given the properties of the auth request + // (see https://github.com/microsoft/vscode/issues/109497) + const authInfoHash = String(hash({ scheme: authInfo.scheme, host: authInfo.host, port: authInfo.port })); + + // Find any previously stored credentials + let storedUsername: string | undefined = undefined; + let storedPassword: string | undefined = undefined; + try { + const encryptedSerializedProxyCredentials = await this.nativeHostMainService.getPassword(undefined, ProxyAuthHandler2.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); + if (encryptedSerializedProxyCredentials) { + const credentials: Credentials = JSON.parse(await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials)); + + storedUsername = credentials.username; + storedPassword = credentials.password; + } + } catch (error) { + this.logService.error(error); // handle errors by asking user for login via dialog + } + + // Reply with stored credentials unless we used them already. + // In that case we need to show a login dialog again because + // they seem invalid. + if (this.state !== ProxyAuthState.StoredCredentialsUsed && typeof storedUsername === 'string' && typeof storedPassword === 'string') { + this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - found stored credentials to use'); + this.state = ProxyAuthState.StoredCredentialsUsed; + + return { username: storedUsername, password: storedPassword }; + } + + // Find suitable window to show dialog: prefer to show it in the + // active window because any other network request will wait on + // the credentials and we want the user to present the dialog. + const window = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); + if (!window) { + this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - no opened window found to show dialog in'); + + return undefined; // unexpected + } + + this.logService.trace(`auth#doResolveProxyCredentials (proxy) - asking window ${window.id} to handle proxy login`); + + // Open proxy dialog + const payload = { + authInfo, + username: this.sessionCredentials?.username ?? storedUsername, // prefer to show already used username (if any) over stored + password: this.sessionCredentials?.password ?? storedPassword, // prefer to show already used password (if any) over stored + replyChannel: `vscode:proxyAuthResponse:${generateUuid()}` + }; + window.sendWhenReady('vscode:openProxyAuthenticationDialog', payload); + this.state = ProxyAuthState.LoginDialogShown; + + // Handle reply + const loginDialogCredentials = await new Promise(resolve => { + const proxyAuthResponseHandler = async (event: ElectronEvent, channel: string, reply: Credentials & { remember: boolean } | undefined /* canceled */) => { + if (channel === payload.replyChannel) { + this.logService.trace(`auth#doResolveProxyCredentials - exit - received credentials from window ${window.id}`); + window.win.webContents.off('ipc-message', proxyAuthResponseHandler); + + // We got credentials from the window + if (reply) { + const credentials: Credentials = { username: reply.username, password: reply.password }; + + // Update stored credentials based on `remember` flag + try { + if (reply.remember) { + const encryptedSerializedCredentials = await this.encryptionMainService.encrypt(JSON.stringify(credentials)); + await this.nativeHostMainService.setPassword(undefined, ProxyAuthHandler2.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash, encryptedSerializedCredentials); + } else { + await this.nativeHostMainService.deletePassword(undefined, ProxyAuthHandler2.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); + } + } catch (error) { + this.logService.error(error); // handle gracefully + } + + resolve({ username: credentials.username, password: credentials.password }); + } + + // We did not get any credentials from the window (e.g. cancelled) + else { + resolve(undefined); + } + } + }; + + window.win.webContents.on('ipc-message', proxyAuthResponseHandler); + }); + + // Remember credentials for the session in case + // the credentials are wrong and we show the dialog + // again + this.sessionCredentials = loginDialogCredentials; + + return loginDialogCredentials; + } +} diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index bb05eddc6ce..1b6ce0e6802 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -5,16 +5,15 @@ import 'vs/platform/update/common/update.config.contribution'; import { app, dialog } from 'electron'; -import { assign } from 'vs/base/common/objects'; +import * as fs from 'fs'; import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; import product from 'vs/platform/product/common/product'; import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; import { mkdirp } from 'vs/base/node/pfs'; -import { validatePaths } from 'vs/code/node/paths'; import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { Server, serve, connect, XDG_RUNTIME_DIR } from 'vs/base/parts/ipc/node/ipc.net'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -23,13 +22,12 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log'; import { StateService } from 'vs/platform/state/node/stateService'; import { IStateService } from 'vs/platform/state/node/state'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService'; -import * as fs from 'fs'; import { CodeApplication } from 'vs/code/electron-main/app'; import { localize } from 'vs/nls'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -41,7 +39,20 @@ import { Client } from 'vs/base/parts/ipc/common/ipc.net'; import { once } from 'vs/base/common/functional'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/node/signService'; -import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; +import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { Schemas } from 'vs/base/common/network'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { TunnelService } from 'vs/platform/remote/node/tunnelService'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IPathWithLineAndColumn, isValidBasename, parseLineAndColumnAware, sanitizeFilePath } from 'vs/base/common/extpath'; +import { isNumber } from 'vs/base/common/types'; +import { rtrim, trim } from 'vs/base/common/strings'; +import { basename, resolve } from 'vs/base/common/path'; +import { coalesce, distinct } from 'vs/base/common/arrays'; +import { EnvironmentMainService, IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; class ExpectedError extends Error { readonly isExpected = true; @@ -56,10 +67,10 @@ class CodeMain { setUnexpectedErrorHandler(err => console.error(err)); // Parse arguments - let args: ParsedArgs; + let args: NativeParsedArgs; try { args = parseMainProcessArgv(process.argv); - args = validatePaths(args); + args = this.validatePaths(args); } catch (err) { console.error(err.message); app.exit(1); @@ -86,19 +97,18 @@ class CodeMain { this.startup(args); } - private async startup(args: ParsedArgs): Promise { + private async startup(args: NativeParsedArgs): Promise { // We need to buffer the spdlog logs until we are sure // we are the only instance running, otherwise we'll have concurrent - // log file access on Windows (https://github.com/Microsoft/vscode/issues/41218) + // log file access on Windows (https://github.com/microsoft/vscode/issues/41218) const bufferLogService = new BufferLogService(); - const [instantiationService, instanceEnvironment] = this.createServices(args, bufferLogService); + const [instantiationService, instanceEnvironment, environmentService] = this.createServices(args, bufferLogService); try { // Init services await instantiationService.invokeFunction(async accessor => { - const environmentService = accessor.get(IEnvironmentService); const configurationService = accessor.get(IConfigurationService); const stateService = accessor.get(IStateService); @@ -115,15 +125,18 @@ class CodeMain { // Startup await instantiationService.invokeFunction(async accessor => { - const environmentService = accessor.get(IEnvironmentService); const logService = accessor.get(ILogService); const lifecycleMainService = accessor.get(ILifecycleMainService); + const fileService = accessor.get(IFileService); const configurationService = accessor.get(IConfigurationService); - const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, true); + const mainIpcServer = await this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, true); bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel()); - once(lifecycleMainService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose()); + once(lifecycleMainService.onWillShutdown)(() => { + fileService.dispose(); + (configurationService as ConfigurationService).dispose(); + }); return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); }); @@ -132,37 +145,45 @@ class CodeMain { } } - private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment] { + private createServices(args: NativeParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService] { const services = new ServiceCollection(); - const environmentService = new EnvironmentService(args, process.execPath); + const environmentService = new EnvironmentMainService(args); const instanceEnvironment = this.patchEnvironment(environmentService); // Patch `process.env` with the instance's environment services.set(IEnvironmentService, environmentService); + services.set(IEnvironmentMainService, environmentService); const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]); process.once('exit', () => logService.dispose()); services.set(ILogService, logService); - services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource)); + const fileService = new FileService(logService); + services.set(IFileService, fileService); + const diskFileSystemProvider = new DiskFileSystemProvider(logService); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + + services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource, fileService)); services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService)); services.set(IStateService, new SyncDescriptor(StateService)); services.set(IRequestService, new SyncDescriptor(RequestMainService)); services.set(IThemeMainService, new SyncDescriptor(ThemeMainService)); services.set(ISignService, new SyncDescriptor(SignService)); + services.set(IProductService, { _serviceBrand: undefined, ...product }); + services.set(ITunnelService, new SyncDescriptor(TunnelService)); - return [new InstantiationService(services, true), instanceEnvironment]; + return [new InstantiationService(services, true), instanceEnvironment, environmentService]; } - private initServices(environmentService: IEnvironmentService, configurationService: ConfigurationService, stateService: StateService): Promise { + private initServices(environmentService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise { // Environment service (paths) const environmentServiceInitialization = Promise.all([ environmentService.extensionsPath, environmentService.nodeCachedDataDir, environmentService.logsPath, - environmentService.globalStorageHome, - environmentService.workspaceStorageHome, - environmentService.backupHome.fsPath + environmentService.globalStorageHome.fsPath, + environmentService.workspaceStorageHome.fsPath, + environmentService.backupHome ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); // Configuration service @@ -174,24 +195,24 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private patchEnvironment(environmentService: IEnvironmentService): IProcessEnvironment { + private patchEnvironment(environmentService: IEnvironmentMainService): IProcessEnvironment { const instanceEnvironment: IProcessEnvironment = { VSCODE_IPC_HOOK: environmentService.mainIPCHandle }; - ['VSCODE_NLS_CONFIG', 'VSCODE_LOGS', 'VSCODE_PORTABLE'].forEach(key => { + ['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => { const value = process.env[key]; if (typeof value === 'string') { instanceEnvironment[key] = value; } }); - assign(process.env, instanceEnvironment); + Object.assign(process.env, instanceEnvironment); return instanceEnvironment; } - private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { + private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { // Try to setup a server for running. If that succeeds it means // we are the first instance to startup. Otherwise it is likely @@ -213,11 +234,6 @@ class CodeMain { throw error; } - // Since we are the second instance, we do not want to show the dock - if (isMacintosh) { - app.dock.hide(); - } - // there's a running instance, let's connect to it let client: Client; try { @@ -247,7 +263,7 @@ class CodeMain { throw error; } - return this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, false); + return this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, false); } // Tests from CLI require to be the only instance currently @@ -263,7 +279,7 @@ class CodeMain { // Skip this if we are running with --wait where it is expected that we wait for a while. // Also skip when gathering diagnostics (--status) which can take a longer time. let startupWarningDialogHandle: NodeJS.Timeout | undefined = undefined; - if (!environmentService.wait && !environmentService.status) { + if (!args.wait && !args.status) { startupWarningDialogHandle = setTimeout(() => { this.showStartupWarningDialog( localize('secondInstanceNoResponse', "Another instance of {0} is running but not responding", product.nameShort), @@ -275,12 +291,13 @@ class CodeMain { const launchService = createChannelSender(client.getChannel('launch'), { disableMarshalling: true }); // Process Info - if (environmentService.args.status) { - return instantiationService.invokeFunction(async accessor => { + if (args.status) { + return instantiationService.invokeFunction(async () => { + // Create a diagnostic service connected to the existing shared process const sharedProcessClient = await connect(environmentService.sharedIPCHandle, 'main'); const diagnosticsChannel = sharedProcessClient.getChannel('diagnostics'); - const diagnosticsService = new DiagnosticsService(diagnosticsChannel); + const diagnosticsService = createChannelSender(diagnosticsChannel); const mainProcessInfo = await launchService.getMainProcessInfo(); const remoteDiagnostics = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true }); const diagnostics = await diagnosticsService.getDiagnostics(mainProcessInfo, remoteDiagnostics); @@ -297,7 +314,7 @@ class CodeMain { // Send environment over... logService.trace('Sending env to running instance...'); - await launchService.start(environmentService.args, process.env as IProcessEnvironment); + await launchService.start(args, process.env as IProcessEnvironment); // Cleanup client.dispose(); @@ -311,17 +328,12 @@ class CodeMain { } // Print --status usage info - if (environmentService.args.status) { + if (args.status) { logService.warn('Warning: The --status argument can only be used if Code is already running. Please run it again after Code has started.'); throw new ExpectedError('Terminating...'); } - // dock might be hidden at this case due to a retry - if (isMacintosh) { - app.dock.show(); - } - // Set the VSCODE_PID variable here when we are sure we are the first // instance to startup. Otherwise we would wrongly overwrite the PID process.env['VSCODE_PID'] = String(process.pid); @@ -329,7 +341,7 @@ class CodeMain { return server; } - private handleStartupDataDirError(environmentService: IEnvironmentService, error: NodeJS.ErrnoException): void { + private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { const directories = [environmentService.userDataPath]; @@ -337,8 +349,8 @@ class CodeMain { directories.push(environmentService.extensionsPath); } - if (xdgRuntimeDir) { - directories.push(xdgRuntimeDir); + if (XDG_RUNTIME_DIR) { + directories.push(XDG_RUNTIME_DIR); } this.showStartupWarningDialog( @@ -349,7 +361,10 @@ class CodeMain { } private showStartupWarningDialog(message: string, detail: string): void { - dialog.showMessageBox({ + // use sync variant here because we likely exit after this method + // due to startup issues and otherwise the dialog seems to disappear + // https://github.com/microsoft/vscode/issues/104493 + dialog.showMessageBoxSync({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], @@ -397,6 +412,100 @@ class CodeMain { lifecycleMainService.kill(exitCode); } + + //#region Helpers + + private validatePaths(args: NativeParsedArgs): NativeParsedArgs { + + // Track URLs if they're going to be used + if (args['open-url']) { + args._urls = args._; + args._ = []; + } + + // Normalize paths and watch out for goto line mode + if (!args['remote']) { + const paths = this.doValidatePaths(args._, args.goto); + args._ = paths; + } + + return args; + } + + private doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { + const cwd = process.env['VSCODE_CWD'] || process.cwd(); + const result = args.map(arg => { + let pathCandidate = String(arg); + + let parsedPath: IPathWithLineAndColumn | undefined = undefined; + if (gotoLineMode) { + parsedPath = parseLineAndColumnAware(pathCandidate); + pathCandidate = parsedPath.path; + } + + if (pathCandidate) { + pathCandidate = this.preparePath(cwd, pathCandidate); + } + + const sanitizedFilePath = sanitizeFilePath(pathCandidate, cwd); + + const filePathBasename = basename(sanitizedFilePath); + if (filePathBasename /* can be empty if code is opened on root */ && !isValidBasename(filePathBasename)) { + return null; // do not allow invalid file names + } + + if (gotoLineMode && parsedPath) { + parsedPath.path = sanitizedFilePath; + + return this.toPath(parsedPath); + } + + return sanitizedFilePath; + }); + + const caseInsensitive = isWindows || isMacintosh; + const distinctPaths = distinct(result, path => path && caseInsensitive ? path.toLowerCase() : (path || '')); + + return coalesce(distinctPaths); + } + + private preparePath(cwd: string, path: string): string { + + // Trim trailing quotes + if (isWindows) { + path = rtrim(path, '"'); // https://github.com/microsoft/vscode/issues/1498 + } + + // Trim whitespaces + path = trim(trim(path, ' '), '\t'); + + if (isWindows) { + + // Resolve the path against cwd if it is relative + path = resolve(cwd, path); + + // Trim trailing '.' chars on Windows to prevent invalid file names + path = rtrim(path, '.'); + } + + return path; + } + + private toPath(pathWithLineAndCol: IPathWithLineAndColumn): string { + const segments = [pathWithLineAndCol.path]; + + if (isNumber(pathWithLineAndCol.line)) { + segments.push(String(pathWithLineAndCol.line)); + } + + if (isNumber(pathWithLineAndCol.column)) { + segments.push(String(pathWithLineAndCol.column)); + } + + return segments.join(':'); + } + + //#endregion } // Main Startup diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index fc716c82be1..c513c3c6c4b 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assign } from 'vs/base/common/objects'; import { memoize } from 'vs/base/common/decorators'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { BrowserWindow, ipcMain, WebContents, Event as ElectronEvent } from 'electron'; import { ISharedProcess } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; import { Barrier } from 'vs/base/common/async'; @@ -14,6 +13,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { FileAccess } from 'vs/base/common/network'; export class SharedProcess implements ISharedProcess { @@ -21,37 +21,49 @@ export class SharedProcess implements ISharedProcess { private window: BrowserWindow | null = null; + private readonly _whenReady: Promise; + constructor( private readonly machineId: string, private userEnv: NodeJS.ProcessEnv, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @IThemeMainService private readonly themeMainService: IThemeMainService - ) { } + ) { + // overall ready promise when shared process signals initialization is done + this._whenReady = new Promise(c => ipcMain.once('vscode:shared-process->electron-main=init-done', () => c(undefined))); + } @memoize - private get _whenReady(): Promise { + private get _whenIpcReady(): Promise { this.window = new BrowserWindow({ show: false, backgroundColor: this.themeMainService.getBackgroundColor(), webPreferences: { - images: false, + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, nodeIntegration: true, + enableWebSQL: false, + enableRemoteModule: false, + spellcheck: false, + nativeWindowOpen: true, + images: false, webgl: false, disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer } }); - const config = assign({ + const config = { appRoot: this.environmentService.appRoot, machineId: this.machineId, nodeCachedDataDir: this.environmentService.nodeCachedDataDir, userEnv: this.userEnv, windowId: this.window.id - }); + }; - const url = `${require.toUrl('vs/code/electron-browser/sharedProcess/sharedProcess.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; - this.window.loadURL(url); + const windowUrl = FileAccess + .asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }); + this.window.loadURL(windowUrl.toString(true)); // Prevent the window from dying const onClose = (e: ElectronEvent) => { @@ -98,16 +110,21 @@ export class SharedProcess implements ISharedProcess { }); return new Promise(c => { - const onHello = Event.once(Event.fromNodeEventEmitter(ipcMain, 'handshake:hello', ({ sender }: { sender: WebContents }) => sender)); - disposables.add(onHello(sender => { - sender.send('handshake:hey there', { + // send payload once shared process is ready to receive it + disposables.add(Event.once(Event.fromNodeEventEmitter(ipcMain, 'vscode:shared-process->electron-main=ready-for-payload', ({ sender }: { sender: WebContents }) => sender))(sender => { + sender.send('vscode:electron-main->shared-process=payload', { sharedIPCHandle: this.environmentService.sharedIPCHandle, args: this.environmentService.args, - logLevel: this.logService.getLevel() + logLevel: this.logService.getLevel(), + backupWorkspacesPath: this.environmentService.backupWorkspacesPath, + nodeCachedDataDir: this.environmentService.nodeCachedDataDir }); - disposables.add(toDisposable(() => sender.send('handshake:goodbye'))); - ipcMain.once('handshake:im ready', () => c(undefined)); + // signal exit to shared process when we get disposed + disposables.add(toDisposable(() => sender.send('vscode:electron-main->shared-process=exit'))); + + // complete IPC-ready promise when shared process signals this to us + ipcMain.once('vscode:shared-process->electron-main=ipc-ready', () => c(undefined)); })); }); } @@ -122,6 +139,11 @@ export class SharedProcess implements ISharedProcess { await this._whenReady; } + async whenIpcReady(): Promise { + await this.barrier.wait(); + await this._whenIpcReady; + } + toggle(): void { if (!this.window || this.window.isVisible()) { this.hide(); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 1f1c08a58f1..d52bfe9d6f3 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -6,15 +6,16 @@ import * as path from 'vs/base/common/path'; import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; -import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, Details } from 'electron'; +import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import product from 'vs/platform/product/common/product'; -import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -25,15 +26,15 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { endsWith } from 'vs/base/common/strings'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; - -const RUN_TEXTMATE_IN_WORKER = false; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; +import { ByteSize, IFileService } from 'vs/platform/files/common/files'; +import { FileAccess, Schemas } from 'vs/base/common/network'; export interface IWindowCreationOptions { state: IWindowState; @@ -58,21 +59,44 @@ const enum WindowError { CRASHED = 2 } +const enum ReadyState { + + /** + * This window has not loaded any HTML yet + */ + NONE, + + /** + * This window is loading HTML + */ + LOADING, + + /** + * This window is navigating to another HTML + */ + NAVIGATING, + + /** + * This window is done loading HTML + */ + READY +} + export class CodeWindow extends Disposable implements ICodeWindow { - private static readonly MIN_WIDTH = 600; - private static readonly MIN_HEIGHT = 270; - - private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 - - private readonly _onClose = this._register(new Emitter()); - readonly onClose: CommonEvent = this._onClose.event; - - private readonly _onDestroy = this._register(new Emitter()); - readonly onDestroy: CommonEvent = this._onDestroy.event; + private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 private readonly _onLoad = this._register(new Emitter()); - readonly onLoad: CommonEvent = this._onLoad.event; + readonly onLoad = this._onLoad.event; + + private readonly _onReady = this._register(new Emitter()); + readonly onReady = this._onReady.event; + + private readonly _onClose = this._register(new Emitter()); + readonly onClose = this._onClose.event; + + private readonly _onDestroy = this._register(new Emitter()); + readonly onDestroy = this._onDestroy.event; private hiddenTitleBarStyle: boolean | undefined; private showTimeoutHandle: NodeJS.Timeout | undefined; @@ -80,27 +104,34 @@ export class CodeWindow extends Disposable implements ICodeWindow { private _readyState: ReadyState; private windowState: IWindowState; private currentMenuBarVisibility: MenuBarVisibility | undefined; + private representedFilename: string | undefined; + private documentEdited: boolean | undefined; private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[]; - private pendingLoadConfig?: IWindowConfiguration; + private pendingLoadConfig?: INativeWindowConfiguration; private marketplaceHeadersPromise: Promise; private readonly touchBarGroups: TouchBarSegmentedControl[]; + private currentHttpProxy?: string; + private currentNoProxy?: string; + constructor( config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, @IFileService private readonly fileService: IFileService, + @IStorageMainService private readonly storageService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IDialogMainService private readonly dialogMainService: IDialogMainService + @IDialogMainService private readonly dialogMainService: IDialogMainService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService ) { super(); @@ -119,20 +150,38 @@ export class CodeWindow extends Disposable implements ICodeWindow { // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); + const windowConfig = this.configurationService.getValue('window'); + const options: BrowserWindowConstructorOptions = { width: this.windowState.width, height: this.windowState.height, x: this.windowState.x, y: this.windowState.y, backgroundColor: this.themeMainService.getBackgroundColor(), - minWidth: CodeWindow.MIN_WIDTH, - minHeight: CodeWindow.MIN_HEIGHT, + minWidth: WindowMinimumSize.WIDTH, + minHeight: WindowMinimumSize.HEIGHT, show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { - nodeIntegration: true, - nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER, - webviewTag: true + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, + enableWebSQL: false, + enableRemoteModule: false, + spellcheck: false, + nativeWindowOpen: true, + webviewTag: true, + zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel), + ...this.environmentService.sandbox ? + + // Sandbox + { + sandbox: true, + contextIsolation: true + } : + + // No Sandbox + { + nodeIntegration: true + } } }; @@ -145,8 +194,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { options.icon = path.join(this.environmentService.appRoot, 'resources/win32/code_150x150.png'); } - const windowConfig = this.configurationService.getValue('window'); - if (isMacintosh && !this.useNativeFullScreen()) { options.fullscreenable = false; // enables simple fullscreen mode } @@ -177,6 +224,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win = new BrowserWindow(options); this._id = this._win.id; + // Open devtools if instructed from command line args + if (this.environmentService.args['open-devtools'] === true) { + this._win.webContents.openDevTools(); + } + if (isMacintosh && useCustomTitleStyle) { this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any } @@ -223,14 +275,18 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.createTouchBar(); // Request handling - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService); + const that = this; + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService, { + get(key) { return that.storageService.get(key); }, + store(key, value) { that.storageService.store(key, value); } + }); // Eventing this.registerListeners(); } - private currentConfig: IWindowConfiguration | undefined; - get config(): IWindowConfiguration | undefined { return this.currentConfig; } + private currentConfig: INativeWindowConfiguration | undefined; + get config(): INativeWindowConfiguration | undefined { return this.currentConfig; } private _id: number; get id(): number { return this._id; } @@ -244,6 +300,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); } + get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; } + setRepresentedFilename(filename: string): void { if (isMacintosh) { this.win.setRepresentedFilename(filename); @@ -260,7 +318,34 @@ export class CodeWindow extends Disposable implements ICodeWindow { return this.representedFilename; } - focus(): void { + setDocumentEdited(edited: boolean): void { + if (isMacintosh) { + this._win.setDocumentEdited(edited); + } + + this.documentEdited = edited; + } + + isDocumentEdited(): boolean { + if (isMacintosh) { + return this._win.isDocumentEdited(); + } + + return !!this.documentEdited; + } + + focus(options?: { force: boolean }): void { + // macOS: Electron > 7.x changed its behaviour to not + // bring the application to the foreground when a window + // is focused programmatically. Only via `app.focus` and + // the option `steal: true` can you get the previous + // behaviour back. The only reason to use this option is + // when a window is getting focused while the application + // is not in the foreground. + if (isMacintosh && options?.force) { + app.focus({ steal: true }); + } + if (!this._win) { return; } @@ -289,6 +374,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { while (this.whenReadyCallbacks.length) { this.whenReadyCallbacks.pop()!(this); } + + // Events + this._onReady.fire(); } ready(): Promise { @@ -324,7 +412,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private registerListeners(): void { // Crashes & Unrsponsive - this._win.webContents.on('crashed', () => this.onWindowError(WindowError.CRASHED)); + this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details)); this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE)); // Window close @@ -338,7 +426,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => { if (details.url.indexOf('.svg') > 0) { const uri = URI.parse(details.url); - if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg')) { + if (uri && !uri.scheme.match(/file/i) && uri.path.endsWith('.svg')) { return callback({ cancel: true }); } } @@ -354,7 +442,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { return callback({ cancel: true }); } - return callback({ cancel: false, responseHeaders }); + return callback({ cancel: false }); }); // Remember that we loaded @@ -374,22 +462,34 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._lastFocusTime = Date.now(); }); - // Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround - // we need to detect when display metrics change or displays are added/removed and toggle the - // fullscreen manually. if (isMacintosh) { - const simpleFullScreenScheduler = this._register(new RunOnceScheduler(() => { + const displayChangedScheduler = this._register(new RunOnceScheduler(() => { if (!this._win) { return; // disposed } + // Notify renderers about displays changed + this.sendWhenReady('vscode:displayChanged'); + + // Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround + // we need to detect when display metrics change or displays are added/removed and toggle the + // fullscreen manually. if (!this.useNativeFullScreen() && this.isFullScreen) { this.setFullScreen(false); this.setFullScreen(true); } }, 100)); - const displayChangedListener = () => simpleFullScreenScheduler.schedule(); + const displayChangedListener = (event: Event, display: Display, changedMetrics?: string[]) => { + if (Array.isArray(changedMetrics) && changedMetrics.length === 1 && changedMetrics[0] === 'workArea') { + // Electron will emit 'display-metrics-changed' events even when actually + // going fullscreen, because the dock hides. However, we do not want to + // react on this event as there is no change in display bounds. + return; + } + + displayChangedScheduler.schedule(); + }; screen.on('display-metrics-changed', displayChangedListener); this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayChangedListener))); @@ -441,12 +541,23 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => - this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record }))); + this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }))); } - private onWindowError(error: WindowError): void { - this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); + private onWindowError(error: WindowError.UNRESPONSIVE): void; + private onWindowError(error: WindowError.CRASHED, details: Details): void; + private onWindowError(error: WindowError, details?: Details): void { + this.logService.error(error === WindowError.CRASHED ? `[VS Code]: renderer process crashed (detail: ${details?.reason})` : '[VS Code]: detected unresponsive'); + // If we run extension tests from CLI, showing a dialog is not + // very helpful in this case. Rather, we bring down the test run + // to signal back a failing run. + if (this.isExtensionDevelopmentTestFromCli) { + this.lifecycleMainService.kill(1); + return; + } + + // Telemetry type WindowErrorClassification = { type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; }; @@ -458,7 +569,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Unresponsive if (error === WindowError.UNRESPONSIVE) { if (this.isExtensionDevelopmentHost || this.isExtensionTestHost || (this._win && this._win.webContents && this._win.webContents.isDevToolsOpened())) { - // TODO@Ben Workaround for https://github.com/Microsoft/vscode/issues/56994 + // TODO@Ben Workaround for https://github.com/microsoft/vscode/issues/56994 // In certain cases the window can report unresponsiveness because a breakpoint was hit // and the process is stopped executing. The most typical cases are: // - devtools are opened and debugging happens @@ -490,11 +601,18 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Crashed else { + let message: string; + if (details && details.reason !== 'crashed') { + message = nls.localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details?.reason); + } else { + message = nls.localize('appCrashed', "The window has crashed", details?.reason); + } + this.dialogMainService.showMessageBox({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(nls.localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(nls.localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], - message: nls.localize('appCrashed', "The window has crashed"), + message, detail: nls.localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true }, this._win).then(result => { @@ -531,6 +649,27 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.currentMenuBarVisibility = newMenuBarVisibility; this.setMenuBarVisibility(newMenuBarVisibility); } + // Do not set to empty configuration at startup if setting is empty to not override configuration through CLI options: + const env = process.env; + let newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim() + || (env.https_proxy || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.HTTP_PROXY || '').trim() // Not standardized. + || undefined; + if (newHttpProxy?.endsWith('/')) { + newHttpProxy = newHttpProxy.substr(0, newHttpProxy.length - 1); + } + const newNoProxy = (env.no_proxy || env.NO_PROXY || '').trim() || undefined; // Not standardized. + if ((newHttpProxy || '').indexOf('@') === -1 && (newHttpProxy !== this.currentHttpProxy || newNoProxy !== this.currentNoProxy)) { + this.currentHttpProxy = newHttpProxy; + this.currentNoProxy = newNoProxy; + const proxyRules = newHttpProxy || ''; + const proxyBypassRules = newNoProxy ? `${newNoProxy},` : ''; + this.logService.trace(`Setting proxy to '${proxyRules}', bypassing '${proxyBypassRules}'`); + this._win.webContents.session.setProxy({ + proxyRules, + proxyBypassRules, + pacScript: '', + }); + } } addTabbedWindow(window: ICodeWindow): void { @@ -539,7 +678,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { + load(config: INativeWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work @@ -558,15 +697,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Add disable-extensions to the config, but do not preserve it on currentConfig or // pendingLoadConfig so that it is applied only on this load - const configuration = objects.assign({}, config); + const configuration = { ...config }; if (disableExtensions !== undefined) { configuration['disable-extensions'] = disableExtensions; } // Clear Document Edited if needed - if (isMacintosh && this._win.isDocumentEdited()) { + if (this.isDocumentEdited()) { if (!isReload || !this.backupMainService.isHotExitEnabled()) { - this._win.setDocumentEdited(false); + this.setDocumentEdited(false); } } @@ -589,7 +728,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.showTimeoutHandle = setTimeout(() => { if (this._win && !this._win.isVisible() && !this._win.isMinimized()) { this._win.show(); - this._win.focus(); + this.focus({ force: true }); this._win.webContents.openDevTools(); } }, 10000); @@ -599,7 +738,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._onLoad.fire(); } - reload(configurationIn?: IWindowConfiguration, cli?: ParsedArgs): void { + reload(configurationIn?: INativeWindowConfiguration, cli?: NativeParsedArgs): void { // If config is not provided, copy our current one const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig); @@ -626,7 +765,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.load(configuration, true, disableExtensions); } - private getUrl(windowConfiguration: IWindowConfiguration): string { + private getUrl(windowConfiguration: INativeWindowConfiguration): string { // Set window ID windowConfiguration.windowId = this._win.id; @@ -644,11 +783,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.fullscreen = this.isFullScreen; // Set Accessibility Config - let autoDetectHighContrast = true; - if (windowConfig?.autoDetectHighContrast === false) { - autoDetectHighContrast = false; - } - windowConfiguration.highContrast = isWindows && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme; + windowConfiguration.colorScheme = { + dark: nativeTheme.shouldUseDarkColors, + highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors + }; + windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related @@ -662,11 +801,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv, OPTIONS); - const config = objects.assign(environment, windowConfiguration); + const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown }; for (const key in config) { - const configValue = (config as any)[key]; + const configValue = config[key]; if (configValue === undefined || configValue === null || configValue === '' || configValue === false) { - delete (config as any)[key]; // only send over properties that have a true value + delete config[key]; // only send over properties that have a true value } } @@ -689,7 +828,17 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private doGetUrl(config: object): string { - return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; + let workbench: string; + if (this.environmentService.sandbox) { + workbench = 'vs/code/electron-sandbox/workbench/workbench.html'; + } else { + workbench = 'vs/code/electron-browser/workbench/workbench.html'; + } + + return FileAccess + .asBrowserUri(workbench, require) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) + .toString(true); } serializeWindowState(): IWindowState { @@ -699,7 +848,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { // fullscreen gets special treatment if (this.isFullScreen) { - const display = screen.getDisplayMatching(this.getBounds()); + let display: Display | undefined; + try { + display = screen.getDisplayMatching(this.getBounds()); + } catch (error) { + // Electron has weird conditions under which it throws errors + // e.g. https://github.com/microsoft/vscode/issues/100334 when + // large numbers are passed in + } const defaultState = defaultWindowState(); @@ -710,7 +866,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Still carry over window dimensions from previous sessions // if we can compute it in fullscreen state. // does not seem possible in all cases on Linux for example - // (https://github.com/Microsoft/vscode/issues/58218) so we + // (https://github.com/microsoft/vscode/issues/58218) so we // fallback to the defaults in that case. width: this.windowState.width || defaultState.width, height: this.windowState.height || defaultState.height, @@ -853,7 +1009,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Multi Montior (fullscreen): try to find the previously used display if (state.display && state.mode === WindowMode.Fullscreen) { - const display = displays.filter(d => d.id === state.display)[0]; + const display = displays.find(d => d.id === state.display); if (display && typeof display.bounds?.x === 'number' && typeof display.bounds?.y === 'number') { this.logService.trace('window#validateWindowState: restoring fullscreen to previous display'); @@ -866,8 +1022,17 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Multi Monitor (non-fullscreen): ensure window is within display bounds - const display = screen.getDisplayMatching({ x: state.x, y: state.y, width: state.width, height: state.height }); - const displayWorkingArea = this.getWorkingArea(display); + let display: Display | undefined; + let displayWorkingArea: Rectangle | undefined; + try { + display = screen.getDisplayMatching({ x: state.x, y: state.y, width: state.width, height: state.height }); + displayWorkingArea = this.getWorkingArea(display); + } catch (error) { + // Electron has weird conditions under which it throws errors + // e.g. https://github.com/microsoft/vscode/issues/100334 when + // large numbers are passed in + } + if ( display && // we have a display matching the desired bounds displayWorkingArea && // we have valid working area bounds @@ -887,7 +1052,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private getWorkingArea(display: Display): Rectangle | undefined { // Prefer the working area of the display to account for taskbars on the - // desktop being positioned somewhere (https://github.com/Microsoft/vscode/issues/50830). + // desktop being positioned somewhere (https://github.com/microsoft/vscode/issues/50830). // // Linux X11 sessions sometimes report wrong display bounds, so we validate // the reported sizes are positive. @@ -989,7 +1154,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (visibility === 'hidden') { // for some weird reason that I have no explanation for, the menu bar is not hiding when calling - // this without timeout (see https://github.com/Microsoft/vscode/issues/19777). there seems to be + // this without timeout (see https://github.com/microsoft/vscode/issues/19777). there seems to be // a timing issue with us opening the first window and the menu bar getting created. somehow the // fact that we want to hide the menu without being able to bring it back via Alt key makes Electron // still show the menu. Unable to reproduce from a simple Hello World application though... @@ -1128,7 +1293,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] { const segments: ITouchBarSegment[] = items.map(item => { let icon: NativeImage | undefined; - if (item.icon && !ThemeIcon.isThemeIcon(item.icon) && item.icon?.dark?.scheme === 'file') { + if (item.icon && !ThemeIcon.isThemeIcon(item.icon) && item.icon?.dark?.scheme === Schemas.file) { icon = nativeImage.createFromPath(URI.revive(item.icon.dark).fsPath); if (icon.isEmpty()) { icon = undefined; diff --git a/src/vs/code/electron-sandbox/issue/issueReporter.html b/src/vs/code/electron-sandbox/issue/issueReporter.html new file mode 100644 index 00000000000..3a0cb4be742 --- /dev/null +++ b/src/vs/code/electron-sandbox/issue/issueReporter.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/code/electron-sandbox/issue/issueReporter.js b/src/vs/code/electron-sandbox/issue/issueReporter.js new file mode 100644 index 00000000000..2c3529754b3 --- /dev/null +++ b/src/vs/code/electron-sandbox/issue/issueReporter.js @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +(function () { + const bootstrapWindow = bootstrapWindowLib(); + + // Load issue reporter into window + bootstrapWindow.load(['vs/code/electron-sandbox/issue/issueReporterMain'], function (issueReporter, configuration) { + issueReporter.startup(configuration); + }, { forceEnableDeveloperKeybindings: true, disallowReloadKeybinding: true }); + + + //#region Globals + + /** + * @returns {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options?: object) => unknown }} + */ + function bootstrapWindowLib() { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; + } + + //#endregion +}()); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts similarity index 67% rename from src/vs/code/electron-browser/issue/issueReporterMain.ts rename to src/vs/code/electron-sandbox/issue/issueReporterMain.ts index 6b1bab2128b..f48450b2574 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -3,42 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { clipboard, ipcRenderer, shell, webFrame } from 'electron'; -import * as os from 'os'; -import * as browser from 'vs/base/browser/browser'; -import { $ } from 'vs/base/browser/dom'; +import 'vs/css!./media/issueReporter'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; +import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window'; +import { $, reset, safeInnerHtml, windowOpenNoOpener } from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; -import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; +import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import * as collections from 'vs/base/common/collections'; import { debounce } from 'vs/base/common/decorators'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { escape } from 'vs/base/common/strings'; -import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; -import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; -import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; -import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import 'vs/css!./media/issueReporter'; +import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; +import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-sandbox/issue/issueReporterModel'; +import BaseHtml from 'vs/code/electron-sandbox/issue/issueReporterPage'; import { localize } from 'vs/nls'; import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { ISettingsSearchIssueReporterData, IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/node/issue'; -import { getLogLevel, ILogService } from 'vs/platform/log/common/log'; -import { FollowerLogService, LoggerChannelClient } from 'vs/platform/log/common/logIpc'; -import { SpdLogService } from 'vs/platform/log/node/spdlogService'; -import product from 'vs/platform/product/common/product'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { combinedAppender, LogAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; +import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; const MAX_URL_LENGTH = 2045; @@ -50,12 +36,30 @@ interface SearchResult { } export interface IssueReporterConfiguration extends IWindowConfiguration { + windowId: number; + disableExtensions: boolean; data: IssueReporterData; features: IssueReporterFeatures; + os: { + type: string; + arch: string; + release: string; + }, + product: { + nameShort: string; + version: string; + commit: string | undefined; + date: string | undefined; + reportIssueUrl: string | undefined; + } } export function startup(configuration: IssueReporterConfiguration) { - document.body.innerHTML = BaseHtml(); + const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac'; + document.body.classList.add(platformClass); // used by our fonts + + safeInnerHtml(document.body, BaseHtml()); + const issueReporter = new IssueReporter(configuration); issueReporter.render(); document.body.style.display = 'block'; @@ -63,9 +67,7 @@ export function startup(configuration: IssueReporterConfiguration) { } export class IssueReporter extends Disposable { - private environmentService!: IEnvironmentService; - private telemetryService!: ITelemetryService; - private logService!: ILogService; + private nativeHostService!: INativeHostService; private readonly issueReporterModel: IssueReporterModel; private numberOfSearchResultsDisplayed = 0; private receivedSystemInfo = false; @@ -75,21 +77,23 @@ export class IssueReporter extends Disposable { private readonly previewButton!: Button; - constructor(configuration: IssueReporterConfiguration) { + constructor(private readonly configuration: IssueReporterConfiguration) { super(); this.initServices(configuration); const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; + + const targetExtension = configuration.data.extensionId ? configuration.data.enabledExtensions.find(extension => extension.id === configuration.data.extensionId) : undefined; this.issueReporterModel = new IssueReporterModel({ issueType: configuration.data.issueType || IssueType.Bug, versionInfo: { - vscodeVersion: `${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, - os: `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}` + vscodeVersion: `${configuration.product.nameShort} ${configuration.product.version} (${configuration.product.commit || 'Commit unknown'}, ${configuration.product.date || 'Date unknown'})`, + os: `${this.configuration.os.type} ${this.configuration.os.arch} ${this.configuration.os.release}${isSnap ? ' snap' : ''}` }, - extensionsDisabled: !!this.environmentService.disableExtensions, - fileOnExtension: configuration.data.extensionId ? true : undefined, - selectedExtension: configuration.data.extensionId ? configuration.data.enabledExtensions.filter(extension => extension.id === configuration.data.extensionId)[0] : undefined + extensionsDisabled: !!configuration.disableExtensions, + fileOnExtension: configuration.data.extensionId ? !targetExtension?.isBuiltin : undefined, + selectedExtension: targetExtension, }); const issueReporterElement = this.getElementById('issue-reporter'); @@ -97,8 +101,24 @@ export class IssueReporter extends Disposable { this.previewButton = new Button(issueReporterElement); } + const issueTitle = configuration.data.issueTitle; + if (issueTitle) { + const issueTitleElement = this.getElementById('issue-title'); + if (issueTitleElement) { + issueTitleElement.value = issueTitle; + } + } + + const issueBody = configuration.data.issueBody; + if (issueBody) { + const description = this.getElementById('description'); + if (description) { + description.value = issueBody; + this.issueReporterModel.update({ issueDescription: issueBody }); + } + } + ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_: unknown, info: Partial) => { - this.logService.trace('issueReporter: Received performance data'); this.issueReporterModel.update(info); this.receivedPerformanceInfo = true; @@ -109,7 +129,6 @@ export class IssueReporter extends Disposable { }); ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: SystemInfo) => { - this.logService.trace('issueReporter: Received system data'); this.issueReporterModel.update({ systemInfo: info }); this.receivedSystemInfo = true; @@ -121,7 +140,6 @@ export class IssueReporter extends Disposable { if (configuration.data.issueType === IssueType.PerformanceIssue) { ipcRenderer.send('vscode:issuePerformanceInfoRequest'); } - this.logService.trace('issueReporter: Sent data requests'); if (window.document.documentElement.lang !== 'en') { show(this.getElementById('english')); @@ -129,13 +147,9 @@ export class IssueReporter extends Disposable { this.setUpTypes(); this.setEventHandlers(); - this.applyZoom(configuration.data.zoomLevel); + applyZoom(configuration.data.zoomLevel); this.applyStyles(configuration.data.styles); this.handleExtensionData(configuration.data.enabledExtensions); - - if (configuration.data.issueType === IssueType.SettingsSearchIssue) { - this.handleSettingsSearchData(configuration.data); - } } render(): void { @@ -157,15 +171,6 @@ export class IssueReporter extends Disposable { } } - private applyZoom(zoomLevel: number) { - webFrame.setZoomLevel(zoomLevel); - browser.setZoomFactor(webFrame.getZoomFactor()); - // See https://github.com/Microsoft/vscode/issues/26151 - // Cannot be trusted because the webFrame might take some time - // until it really applies the new zoom level - browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); - } - private applyStyles(styles: IssueReporterStyles) { const styleTag = document.createElement('style'); const content: string[] = []; @@ -237,99 +242,35 @@ export class IssueReporter extends Disposable { content.push(`.monaco-text-button:not(.disabled):hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); } - styleTag.innerHTML = content.join('\n'); + styleTag.textContent = content.join('\n'); document.head.appendChild(styleTag); document.body.style.color = styles.color || ''; } private handleExtensionData(extensions: IssueReporterExtensionData[]) { - const { nonThemes, themes } = collections.groupBy(extensions, ext => { + const installedExtensions = extensions.filter(x => !x.isBuiltin); + const { nonThemes, themes } = collections.groupBy(installedExtensions, ext => { return ext.isTheme ? 'themes' : 'nonThemes'; }); const numberOfThemeExtesions = themes && themes.length; - this.issueReporterModel.update({ numberOfThemeExtesions, enabledNonThemeExtesions: nonThemes, allExtensions: extensions }); + this.issueReporterModel.update({ numberOfThemeExtesions, enabledNonThemeExtesions: nonThemes, allExtensions: installedExtensions }); this.updateExtensionTable(nonThemes, numberOfThemeExtesions); - if (this.environmentService.disableExtensions || extensions.length === 0) { + if (this.configuration.disableExtensions || installedExtensions.length === 0) { (this.getElementById('disableExtensions')).disabled = true; } - this.updateExtensionSelector(extensions); + this.updateExtensionSelector(installedExtensions); } - private handleSettingsSearchData(data: ISettingsSearchIssueReporterData): void { - this.issueReporterModel.update({ - actualSearchResults: data.actualSearchResults, - query: data.query, - filterResultCount: data.filterResultCount - }); - this.updateSearchedExtensionTable(data.enabledExtensions); - this.updateSettingsSearchDetails(data); - } - - private updateSettingsSearchDetails(data: ISettingsSearchIssueReporterData): void { - const target = document.querySelector('.block-settingsSearchResults .block-info'); - if (target) { - const details = ` -
    -
    Query: "${data.query}"
    -
    Literal match count: ${data.filterResultCount}
    -
    - `; - - let table = ` - - Setting - Extension - Score - `; - - data.actualSearchResults - .forEach(setting => { - table += ` - - ${setting.key} - ${setting.extensionId} - ${String(setting.score).slice(0, 5)} - `; - }); - - target.innerHTML = `${details}${table}
    `; - } - } - - private initServices(configuration: IWindowConfiguration): void { + private initServices(configuration: IssueReporterConfiguration): void { const serviceCollection = new ServiceCollection(); const mainProcessService = new MainProcessService(configuration.windowId); serviceCollection.set(IMainProcessService, mainProcessService); - this.environmentService = new EnvironmentService(configuration, configuration.execPath); - - const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService)); - const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger')); - this.logService = new FollowerLogService(loggerClient, logService); - - const sharedProcessService = createChannelSender(mainProcessService.getChannel('sharedProcess')); - - const sharedProcess = sharedProcessService.whenSharedProcessReady() - .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${configuration.windowId}`)); - - const instantiationService = new InstantiationService(serviceCollection, true); - if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); - const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)); - const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', product.version, configuration.machineId, product.msftInternalDomains, this.environmentService.installSourcePath); - const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; - const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; - - const telemetryService = instantiationService.createInstance(TelemetryService, config); - this._register(telemetryService); - - this.telemetryService = telemetryService; - } else { - this.telemetryService = NullTelemetryService; - } + this.nativeHostService = new NativeHostService(configuration.windowId, mainProcessService) as INativeHostService; + serviceCollection.set(INativeHostService, this.nativeHostService); } private setEventHandlers(): void { @@ -440,9 +381,9 @@ export class IssueReporter extends Disposable { sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled'); }); - this.addEventListener('extensionBugsLink', 'click', (e: MouseEvent) => { + this.addEventListener('extensionBugsLink', 'click', (e: Event) => { const url = (e.target).innerText; - shell.openExternal(url); + windowOpenNoOpener(url); }); this.addEventListener('disableExtensions', 'keydown', (e: Event) => { @@ -478,12 +419,12 @@ export class IssueReporter extends Disposable { // Cmd/Ctrl + zooms in if (cmdOrCtrlKey && e.keyCode === 187) { - this.applyZoom(webFrame.getZoomLevel() + 1); + zoomIn(); } // Cmd/Ctrl - zooms out if (cmdOrCtrlKey && e.keyCode === 189) { - this.applyZoom(webFrame.getZoomLevel() - 1); + zoomOut(); } // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac @@ -522,10 +463,6 @@ export class IssueReporter extends Disposable { return true; } - if (issueType === IssueType.SettingsSearchIssue) { - return true; - } - return false; } @@ -569,7 +506,7 @@ export class IssueReporter extends Disposable { private clearSearchResults(): void { const similarIssues = this.getElementById('similar-issues')!; - similarIssues.innerHTML = ''; + similarIssues.innerText = ''; this.numberOfSearchResultsDisplayed = 0; } @@ -580,7 +517,7 @@ export class IssueReporter extends Disposable { window.fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { response.json().then(result => { - similarIssues.innerHTML = ''; + similarIssues.innerText = ''; if (result && result.items) { this.displaySearchResults(result.items); } else { @@ -599,11 +536,11 @@ export class IssueReporter extends Disposable { }, timeToWait * 1000); } } - }).catch(e => { - this.logSearchError(e); + }).catch(_ => { + // Ignore }); - }).catch(e => { - this.logSearchError(e); + }).catch(_ => { + // Ignore }); } @@ -630,11 +567,11 @@ export class IssueReporter extends Disposable { } else { throw new Error('Unexpected response, no candidates property'); } - }).catch((error) => { - this.logSearchError(error); + }).catch(_ => { + // Ignore }); - }).catch((error) => { - this.logSearchError(error); + }).catch(_ => { + // Ignore }); } @@ -670,9 +607,9 @@ export class IssueReporter extends Disposable { issueState.appendChild(issueIcon); issueState.appendChild(issueStateLabel); - item = $('div.issue', {}, issueState, link); + item = $('div.issue', undefined, issueState, link); } else { - item = $('div.issue', {}, link); + item = $('div.issue', undefined, link); } issues.appendChild(item); @@ -687,33 +624,16 @@ export class IssueReporter extends Disposable { } } - private logSearchError(error: Error) { - this.logService.warn('issueReporter#search ', error.message); - type IssueReporterSearchErrorClassification = { - message: { classification: 'CallstackOrException', purpose: 'PerformanceAndHealth' } - }; - - type IssueReporterSearchError = { - message: string; - }; - this.telemetryService.publicLog2('issueReporterSearchError', { message: error.message }); - } - private setUpTypes(): void { - const makeOption = (issueType: IssueType, description: string) => ``; + const makeOption = (issueType: IssueType, description: string) => $('option', { 'value': issueType.valueOf() }, escape(description)); const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement; const { issueType } = this.issueReporterModel.getData(); - if (issueType === IssueType.SettingsSearchIssue) { - typeSelect.innerHTML = makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue")); - typeSelect.disabled = true; - } else { - typeSelect.innerHTML = [ - makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")), - makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")), - makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")) - ].join('\n'); - } + reset(typeSelect, + makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")), + makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")), + makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")) + ); typeSelect.value = issueType.toString(); @@ -731,13 +651,17 @@ export class IssueReporter extends Disposable { private setSourceOptions(): void { const sourceSelect = this.getElementById('issue-source')! as HTMLSelectElement; - const { issueType, fileOnExtension } = this.issueReporterModel.getData(); + const { issueType, fileOnExtension, selectedExtension } = this.issueReporterModel.getData(); let selected = sourceSelect.selectedIndex; - if (selected === -1 && fileOnExtension !== undefined) { - selected = fileOnExtension ? 2 : 1; + if (selected === -1) { + if (fileOnExtension !== undefined) { + selected = fileOnExtension ? 2 : 1; + } else if (selectedExtension?.isBuiltin) { + selected = 1; + } } - sourceSelect.innerHTML = ''; + sourceSelect.innerText = ''; if (issueType === IssueType.FeatureRequest) { sourceSelect.append(...[ this.makeOption('', localize('selectSource', "Select source"), true), @@ -798,9 +722,8 @@ export class IssueReporter extends Disposable { } else { show(extensionsBlock); } - - descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} *`; - descriptionSubtitle.innerHTML = localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); } else if (issueType === IssueType.PerformanceIssue) { show(blockContainer); show(systemBlock); @@ -814,33 +737,29 @@ export class IssueReporter extends Disposable { show(extensionsBlock); } - descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} *`; - descriptionSubtitle.innerHTML = localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); } else if (issueType === IssueType.FeatureRequest) { - descriptionTitle.innerHTML = `${localize('description', "Description")} *`; - descriptionSubtitle.innerHTML = localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); + reset(descriptionTitle, localize('description', "Description"), $('span.required-input', undefined, '*')); + reset(descriptionSubtitle, localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); show(problemSource); if (fileOnExtension) { show(extensionSelector); } - } else if (issueType === IssueType.SettingsSearchIssue) { - show(blockContainer); - show(searchedExtensionsBlock); - show(settingsSearchResultsBlock); - - descriptionTitle.innerHTML = `${localize('expectedResults', "Expected Results")} *`; - descriptionSubtitle.innerHTML = localize('settingsSearchResultsDescription', "Please list the results that you were expecting to see when you searched with this query. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."); } } private validateInput(inputId: string): boolean { const inputElement = (this.getElementById(inputId)); + const inputValidationMessage = this.getElementById(`${inputId}-empty-error`); if (!inputElement.value) { inputElement.classList.add('invalid-input'); + inputValidationMessage?.classList.remove('hidden'); return false; } else { inputElement.classList.remove('invalid-input'); + inputValidationMessage?.classList.add('hidden'); return true; } } @@ -888,15 +807,6 @@ export class IssueReporter extends Disposable { return false; } - type IssueReporterSubmitClassification = { - issueType: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - numSimilarIssuesDisplayed: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - }; - type IssueReporterSubmitEvent = { - issueType: any; - numSimilarIssuesDisplayed: number; - }; - this.telemetryService.publicLog2('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed }); this.hasBeenSubmitted = true; const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value); @@ -917,9 +827,9 @@ export class IssueReporter extends Disposable { private async writeToClipboard(baseUrl: string, issueBody: string): Promise { return new Promise((resolve, reject) => { - ipcRenderer.once('vscode:issueReporterClipboardResponse', (_: unknown, shouldWrite: boolean) => { + ipcRenderer.once('vscode:issueReporterClipboardResponse', async (event: unknown, shouldWrite: boolean) => { if (shouldWrite) { - clipboard.writeText(issueBody); + await this.nativeHostService.writeClipboardText(issueBody); resolve(baseUrl + `&body=${encodeURIComponent(localize('pasteData', "We have written the needed data into your clipboard because it was too large to send. Please paste."))}`); } else { reject(); @@ -945,7 +855,7 @@ export class IssueReporter extends Disposable { } private getIssueUrlWithTitle(issueTitle: string): string { - let repositoryUrl = product.reportIssueUrl; + let repositoryUrl = this.configuration.product.reportIssueUrl; if (this.issueReporterModel.fileOnExtension()) { const extensionGitHubUrl = this.getExtensionGitHubUrl(); if (extensionGitHubUrl) { @@ -953,47 +863,87 @@ export class IssueReporter extends Disposable { } } - const queryStringPrefix = product.reportIssueUrl && product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&'; + const queryStringPrefix = this.configuration.product.reportIssueUrl && this.configuration.product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&'; return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`; } private updateSystemInfo(state: IssueReporterModelData) { - const target = document.querySelector('.block-system .block-info'); + const target = document.querySelector('.block-system .block-info'); + if (target) { const systemInfo = state.systemInfo!; - let renderedData = ` - - - - - - - - -
    CPUs${systemInfo.cpus}
    GPU Status${Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('
    ')}
    Load (avg)${systemInfo.load}
    Memory (System)${systemInfo.memory}
    Process Argv${systemInfo.processArgs}
    Screen Reader${systemInfo.screenReader}
    VM${systemInfo.vmHint}
    `; + const renderedDataTable = $('table', undefined, + $('tr', undefined, + $('td', undefined, 'CPUs'), + $('td', undefined, systemInfo.cpus || ''), + ), + $('tr', undefined, + $('td', undefined, 'GPU Status' as string), + $('td', undefined, Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('\n')), + ), + $('tr', undefined, + $('td', undefined, 'Load (avg)' as string), + $('td', undefined, systemInfo.load || ''), + ), + $('tr', undefined, + $('td', undefined, 'Memory (System)' as string), + $('td', undefined, systemInfo.memory), + ), + $('tr', undefined, + $('td', undefined, 'Process Argv' as string), + $('td', undefined, systemInfo.processArgs), + ), + $('tr', undefined, + $('td', undefined, 'Screen Reader' as string), + $('td', undefined, systemInfo.screenReader), + ), + $('tr', undefined, + $('td', undefined, 'VM'), + $('td', undefined, systemInfo.vmHint), + ), + ); + reset(target, renderedDataTable); systemInfo.remoteData.forEach(remote => { + target.appendChild($('hr')); if (isRemoteDiagnosticError(remote)) { - renderedData += ` -
    - - - -
    Remote${remote.hostName}
    ${remote.errorMessage}
    `; + const remoteDataTable = $('table', undefined, + $('tr', undefined, + $('td', undefined, 'Remote'), + $('td', undefined, remote.hostName) + ), + $('tr', undefined, + $('td', undefined, ''), + $('td', undefined, remote.errorMessage) + ) + ); + target.appendChild(remoteDataTable); } else { - renderedData += ` -
    - - - - - - -
    Remote${remote.hostName}
    OS${remote.machineInfo.os}
    CPUs${remote.machineInfo.cpus}
    Memory (System)${remote.machineInfo.memory}
    VM${remote.machineInfo.vmHint}
    `; + const remoteDataTable = $('table', undefined, + $('tr', undefined, + $('td', undefined, 'Remote'), + $('td', undefined, remote.hostName) + ), + $('tr', undefined, + $('td', undefined, 'OS'), + $('td', undefined, remote.machineInfo.os) + ), + $('tr', undefined, + $('td', undefined, 'CPUs'), + $('td', undefined, remote.machineInfo.cpus || '') + ), + $('tr', undefined, + $('td', undefined, 'Memory (System)' as string), + $('td', undefined, remote.machineInfo.memory) + ), + $('tr', undefined, + $('td', undefined, 'VM'), + $('td', undefined, remote.machineInfo.vmHint) + ), + ); + target.appendChild(remoteDataTable); } }); - - target.innerHTML = renderedData; } } @@ -1025,15 +975,18 @@ export class IssueReporter extends Disposable { return 0; }); - const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData) => { + const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData): HTMLOptionElement => { const selected = selectedExtension && extension.id === selectedExtension.id; - return ``; + return $('option', { + 'value': extension.id, + 'selected': selected || '' + }, extension.name); }; const extensionsSelector = this.getElementById('extension-selector'); if (extensionsSelector) { const { selectedExtension } = this.issueReporterModel.getData(); - extensionsSelector.innerHTML = '' + extensionOptions.map(extension => makeOption(extension, selectedExtension)).join('\n'); + reset(extensionsSelector, $('option'), ...extensionOptions.map(extension => makeOption(extension, selectedExtension))); this.addEventListener('extension-selector', 'change', (e: Event) => { const selectedExtensionId = (e.target).value; @@ -1101,9 +1054,9 @@ export class IssueReporter extends Disposable { } private updateProcessInfo(state: IssueReporterModelData) { - const target = document.querySelector('.block-process .block-info'); + const target = document.querySelector('.block-process .block-info') as HTMLElement; if (target) { - target.innerHTML = `${state.processInfo}`; + reset(target, $('code', undefined, state.processInfo)); } } @@ -1112,10 +1065,10 @@ export class IssueReporter extends Disposable { } private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { - const target = document.querySelector('.block-extensions .block-info'); + const target = document.querySelector('.block-extensions .block-info'); if (target) { - if (this.environmentService.disableExtensions) { - target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); + if (this.configuration.disableExtensions) { + reset(target, localize('disabledExtensions', "Extensions are disabled")); return; } @@ -1123,46 +1076,27 @@ export class IssueReporter extends Disposable { extensions = extensions || []; if (!extensions.length) { - target.innerHTML = 'Extensions: none' + themeExclusionStr; + target.innerText = 'Extensions: none' + themeExclusionStr; return; } - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
    ${themeExclusionStr}`; + reset(target, this.getExtensionTableHtml(extensions), document.createTextNode(themeExclusionStr)); } } - private updateSearchedExtensionTable(extensions: IssueReporterExtensionData[]): void { - const target = document.querySelector('.block-searchedExtensions .block-info'); - if (target) { - if (!extensions.length) { - target.innerHTML = 'Extensions: none'; - return; - } - - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
    `; - } - } - - private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string { - let table = ` - - Extension - Author (truncated) - Version - `; - - table += extensions.map(extension => { - return ` - - ${extension.name} - ${extension.publisher.substr(0, 3)} - ${extension.version} - `; - }).join(''); - - return table; + private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): HTMLTableElement { + return $('table', undefined, + $('tr', undefined, + $('th', undefined, 'Extension'), + $('th', undefined, 'Author (truncated)' as string), + $('th', undefined, 'Version'), + ), + ...extensions.map(extension => $('tr', undefined, + $('td', undefined, extension.name), + $('td', undefined, extension.publisher.substr(0, 3)), + $('td', undefined, extension.version), + )) + ); } private openLink(event: MouseEvent): void { @@ -1170,13 +1104,12 @@ export class IssueReporter extends Disposable { event.stopPropagation(); // Exclude right click if (event.which < 3) { - shell.openExternal((event.target).href); - this.telemetryService.publicLog2('issueReporterViewSimilarIssue'); + windowOpenNoOpener((event.target).href); } } - private getElementById(elementId: string): HTMLElement | undefined { - const element = document.getElementById(elementId); + private getElementById(elementId: string): T | undefined { + const element = document.getElementById(elementId) as T | undefined; if (element) { return element; } else { diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts similarity index 82% rename from src/vs/code/electron-browser/issue/issueReporterModel.ts rename to src/vs/code/electron-sandbox/issue/issueReporterModel.ts index 6c7fa528e37..b529e48cd76 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assign } from 'vs/base/common/objects'; -import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; +import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; export interface IssueReporterData { @@ -49,7 +48,7 @@ export class IssueReporterModel { allExtensions: [] }; - this._data = initialData ? assign(defaultData, initialData) : defaultData; + this._data = initialData ? Object.assign(defaultData, initialData) : defaultData; } getData(): IssueReporterData { @@ -57,7 +56,7 @@ export class IssueReporterModel { } update(newData: Partial): void { - assign(this._data, newData); + Object.assign(this._data, newData); } serialize(): string { @@ -103,8 +102,6 @@ ${this.getInfos()} return 'Bug'; } else if (this._data.issueType === IssueType.PerformanceIssue) { return 'Performance Issue'; - } else if (this._data.issueType === IssueType.SettingsSearchIssue) { - return 'Settings Search Issue'; } else { return 'Feature Request'; } @@ -136,17 +133,6 @@ ${this.getInfos()} } } - if (this._data.issueType === IssueType.SettingsSearchIssue) { - if (this._data.includeSearchedExtensions) { - info += this.generateExtensionsMd(); - } - - if (this._data.includeSettingsSearchDetails) { - info += this.generateSettingSearchResultsMd(); - info += '\n' + this.generateSettingsSearchResultDetailsMd(); - } - } - return info; } @@ -168,6 +154,13 @@ ${this.getInfos()} |Screen Reader|${this._data.systemInfo.screenReader}| |VM|${this._data.systemInfo.vmHint}|`; + if (this._data.systemInfo.linuxEnv) { + md += `\n|DESKTOP_SESSION|${this._data.systemInfo.linuxEnv.desktopSession}| +|XDG_CURRENT_DESKTOP|${this._data.systemInfo.linuxEnv.xdgCurrentDesktop}| +|XDG_SESSION_DESKTOP|${this._data.systemInfo.linuxEnv.xdgSessionDesktop}| +|XDG_SESSION_TYPE|${this._data.systemInfo.linuxEnv.xdgSessionType}|`; + } + this._data.systemInfo.remoteData.forEach(remote => { if (isRemoteDiagnosticError(remote)) { md += `\n\n${remote.errorMessage}`; @@ -239,33 +232,4 @@ ${themeExclusionStr} `; } - - private generateSettingsSearchResultDetailsMd(): string { - return ` -Query: ${this._data.query} -Literal matches: ${this._data.filterResultCount}`; - } - - private generateSettingSearchResultsMd(): string { - if (!this._data.actualSearchResults) { - return ''; - } - - if (!this._data.actualSearchResults.length) { - return `No fuzzy results`; - } - - const tableHeader = `Setting|Extension|Score ----|---|---`; - const table = this._data.actualSearchResults.map(setting => { - return `${setting.key}|${setting.extensionId}|${String(setting.score).slice(0, 5)}`; - }).join('\n'); - - return `
    Results - -${tableHeader} -${table} - -
    `; - } -} \ No newline at end of file +} diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts similarity index 92% rename from src/vs/code/electron-browser/issue/issueReporterPage.ts rename to src/vs/code/electron-sandbox/issue/issueReporterPage.ts index 0b027996986..d7be46aebfa 100644 --- a/src/vs/code/electron-browser/issue/issueReporterPage.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts @@ -23,6 +23,7 @@ export default (): string => ` + @@ -35,7 +36,7 @@ export default (): string => ` @@ -43,6 +44,7 @@ export default (): string => `
    + @@ -61,6 +63,7 @@ export default (): string => `
    +
    diff --git a/src/vs/code/electron-browser/issue/media/issueReporter.css b/src/vs/code/electron-sandbox/issue/media/issueReporter.css similarity index 68% rename from src/vs/code/electron-browser/issue/media/issueReporter.css rename to src/vs/code/electron-sandbox/issue/media/issueReporter.css index eb4f53747a8..e3c6d8f355f 100644 --- a/src/vs/code/electron-browser/issue/media/issueReporter.css +++ b/src/vs/code/electron-sandbox/issue/media/issueReporter.css @@ -95,26 +95,30 @@ textarea, input, select { } html { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; color: #CCCCCC; height: 100%; } -html:lang(zh-Hans) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; -} +/* Font Families (with CJK support) */ -html:lang(zh-Hant) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; -} +.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } +.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } +.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } +.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } +.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } -html:lang(ja) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; -} +.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; } +.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } +.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } +.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; } +.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } -html:lang(ko) { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; -} +/* Linux: add `system-ui` as first font and not `Ubuntu` to allow other distribution pick their standard OS font */ +.linux { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; } +.linux:lang(zh-Hans) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } +.linux:lang(zh-Hant) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } +.linux:lang(ja) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } +.linux:lang(ko) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } body { margin: 0; @@ -197,9 +201,10 @@ select, input, textarea { } -.validation-error { +#issue-reporter .validation-error { font-size: 12px; - margin-top: 1em; + padding: 10px; + border-top: 0px !important; } @@ -252,8 +257,7 @@ a { } .section .input-group .validation-error { - margin-left: calc(15% + 5px); - padding: 10px; + margin-left: 100px; } .section .inline-form-control, .section .inline-label { @@ -264,7 +268,7 @@ a { width: 95px; } -.section .inline-form-control { +.section .inline-form-control, .section .input-group .validation-error { width: calc(100% - 100px); } @@ -290,9 +294,13 @@ a { margin-left: calc(15% + 1em); } - .section .inline-form-control { + .section .inline-form-control, .section .input-group .validation-error { width: calc(85% - 5px); } + + .section .input-group .validation-error { + margin-left: calc(15% + 4px); + } } @media (max-width: 620px) { @@ -304,7 +312,7 @@ a { margin-left: 1em; } - .section .inline-form-control { + .section .inline-form-control, .section .input-group .validation-error { width: 100%; } diff --git a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts b/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts similarity index 76% rename from src/vs/code/electron-browser/issue/test/testReporterModel.test.ts rename to src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts index b34917961c0..b09f4fb760f 100644 --- a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts +++ b/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; -import { IssueType } from 'vs/platform/issue/node/issue'; +import { IssueReporterModel } from 'vs/code/electron-sandbox/issue/issueReporterModel'; +import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; +import { IssueType } from 'vs/platform/issue/common/issue'; suite('IssueReporter', () => { @@ -81,6 +81,55 @@ OS version: undefined `); }); + test('serializes Linux environment information when data is provided', () => { + const issueReporterModel = new IssueReporterModel({ + issueType: 0, + systemInfo: { + os: 'Darwin', + cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)', + memory: '16.00GB', + vmHint: '0%', + processArgs: '', + screenReader: 'no', + remoteData: [], + gpuStatus: {}, + linuxEnv: { + desktopSession: 'ubuntu', + xdgCurrentDesktop: 'ubuntu', + xdgSessionDesktop: 'ubuntu:GNOME', + xdgSessionType: 'x11' + } + } + }); + assert.equal(issueReporterModel.serialize(), + ` +Issue Type: Bug + +undefined + +VS Code version: undefined +OS version: undefined + +
    +System Info + +|Item|Value| +|---|---| +|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)| +|GPU Status|| +|Load (avg)|undefined| +|Memory (System)|16.00GB| +|Process Argv|| +|Screen Reader|no| +|VM|0%| +|DESKTOP_SESSION|ubuntu| +|XDG_CURRENT_DESKTOP|ubuntu| +|XDG_SESSION_DESKTOP|ubuntu:GNOME| +|XDG_SESSION_TYPE|x11| +
    Extensions: none +`); + }); + test('serializes remote information when data is provided', () => { const issueReporterModel = new IssueReporterModel({ issueType: 0, @@ -169,16 +218,5 @@ Remote OS version: Linux x64 4.18.0 assert.equal(issueReporterModel.fileOnExtension(), true); }); - - [ - IssueType.SettingsSearchIssue - ].forEach(type => { - const issueReporterModel = new IssueReporterModel({ - issueType: type, - fileOnExtension: true - }); - - assert.equal(issueReporterModel.fileOnExtension(), false); - }); }); }); diff --git a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css new file mode 100644 index 00000000000..add9cdae262 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +html { + font-size: 13px; +} + +/* Font Families (with CJK support) */ + +.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } +.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } +.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } +.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } +.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } + +.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; } +.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } +.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } +.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; } +.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } + +/* Linux: add `system-ui` as first font and not `Ubuntu` to allow other distribution pick their standard OS font */ +.linux { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; } +.linux:lang(zh-Hans) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } +.linux:lang(zh-Hant) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } +.linux:lang(ja) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } +.linux:lang(ko) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } + +body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + user-select: none; + color: #cccccc; +} + +.cpu { + width: 45px; +} + +.pid { + width: 50px +} + +.memory { + width: 90px; +} + +.process-item { + line-height: 22px; +} + +table { + border-collapse: collapse; + width: 100%; + table-layout: fixed; +} + +th[scope='col'] { + vertical-align: bottom; + border-bottom: 1px solid #cccccc; + padding: .5rem; + border-top: 1px solid #cccccc; + cursor: default; +} + +td { + padding: .25rem; + vertical-align: top; + cursor: default; +} + +.centered { + text-align: center; +} + +.nameLabel{ + text-align: left; +} + +.data { + white-space: pre; + padding-left: .5rem; + font-weight: normal; + text-align: left; + height: 22px; +} + +.error { + padding-left: 20px; + white-space: nowrap; +} + +tbody > tr:hover { + background-color: #2A2D2E; +} + +.hidden { + display: none; +} + +.header { + display: flex; +} diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.html b/src/vs/code/electron-sandbox/processExplorer/processExplorer.html new file mode 100644 index 00000000000..517805abdb7 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.html @@ -0,0 +1,19 @@ + + + + + + + + +
    + + + + + + + + + + diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js new file mode 100644 index 00000000000..3b84f3acf06 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +(function () { + const bootstrapWindow = bootstrapWindowLib(); + + // Load process explorer into window + bootstrapWindow.load(['vs/code/electron-sandbox/processExplorer/processExplorerMain'], function (processExplorer, configuration) { + processExplorer.startup(configuration.windowId, configuration.data); + }, { forceEnableDeveloperKeybindings: true }); + + + //#region Globals + + /** + * @returns {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options?: object) => unknown }} + */ + function bootstrapWindowLib() { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; + } + + //#endregion +}()); diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts new file mode 100644 index 00000000000..b1883c720ec --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -0,0 +1,446 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/processExplorer'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { localize } from 'vs/nls'; +import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue'; +import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window'; +import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; +import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; +import { ProcessItem } from 'vs/base/common/processes'; +import { addDisposableListener, $ } from 'vs/base/browser/dom'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; +import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; +import { ByteSize } from 'vs/platform/files/common/files'; + +const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; +const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; + +interface FormattedProcessItem { + cpu: number; + memory: number; + pid: string; + name: string; + formattedName: string; + cmd: string; +} + +class ProcessExplorer { + private lastRequestTime: number; + + private collapsedStateCache: Map = new Map(); + + private mapPidToWindowTitle = new Map(); + + private listeners = new DisposableStore(); + + private nativeHostService: INativeHostService; + + constructor(windowId: number, private data: ProcessExplorerData) { + const mainProcessService = new MainProcessService(windowId); + this.nativeHostService = new NativeHostService(windowId, mainProcessService) as INativeHostService; + + this.applyStyles(data.styles); + + // Map window process pids to titles, annotate process names with this when rendering to distinguish between them + ipcRenderer.on('vscode:windowsInfoResponse', (event: unknown, windows: any[]) => { + this.mapPidToWindowTitle = new Map(); + windows.forEach(window => this.mapPidToWindowTitle.set(window.pid, window.title)); + }); + + ipcRenderer.on('vscode:listProcessesResponse', (event: unknown, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => { + this.updateProcessInfo(processRoots); + this.requestProcessList(0); + }); + + this.lastRequestTime = Date.now(); + ipcRenderer.send('vscode:windowsInfoRequest'); + ipcRenderer.send('vscode:listProcesses'); + } + + private getProcessList(rootProcess: ProcessItem, isLocal: boolean, totalMem: number): FormattedProcessItem[] { + const processes: FormattedProcessItem[] = []; + + if (rootProcess) { + this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem); + } + + return processes; + } + + private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number): void { + const isRoot = (indent === 0); + + let name = item.name; + if (isRoot) { + name = isLocal ? `${this.data.applicationName} main` : 'remote agent'; + } + + if (name === 'window') { + const windowTitle = this.mapPidToWindowTitle.get(item.pid); + name = windowTitle !== undefined ? `${name} (${this.mapPidToWindowTitle.get(item.pid)})` : name; + } + + // Format name with indent + const formattedName = isRoot ? name : `${' '.repeat(indent)} ${name}`; + const memory = this.data.platform === 'win32' ? item.mem : (totalMem * (item.mem / 100)); + processes.push({ + cpu: item.load, + memory: (memory / ByteSize.MB), + pid: item.pid.toFixed(0), + name, + formattedName, + cmd: item.cmd + }); + + // Recurse into children if any + if (Array.isArray(item.children)) { + item.children.forEach(child => { + if (child) { + this.getProcessItem(processes, child, indent + 1, isLocal, totalMem); + } + }); + } + } + + private isDebuggable(cmd: string): boolean { + const matches = DEBUG_FLAGS_PATTERN.exec(cmd); + return (matches && matches.length >= 2) || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0; + } + + private attachTo(item: FormattedProcessItem) { + const config: any = { + type: 'node', + request: 'attach', + name: `process ${item.pid}` + }; + + let matches = DEBUG_FLAGS_PATTERN.exec(item.cmd); + if (matches && matches.length >= 2) { + // attach via port + if (matches.length === 4 && matches[3]) { + config.port = parseInt(matches[3]); + } + config.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; + } else { + // no port -> try to attach via pid (send SIGUSR1) + config.processId = String(item.pid); + } + + // a debug-port=n or inspect-port=n overrides the port + matches = DEBUG_PORT_PATTERN.exec(item.cmd); + if (matches && matches.length === 3) { + // override port + config.port = parseInt(matches[2]); + } + + ipcRenderer.send('vscode:workbenchCommand', { id: 'debug.startFromConfig', from: 'processExplorer', args: [config] }); + } + + private getProcessIdWithHighestProperty(processList: any[], propertyName: string) { + let max = 0; + let maxProcessId; + processList.forEach(process => { + if (process[propertyName] > max) { + max = process[propertyName]; + maxProcessId = process.pid; + } + }); + + return maxProcessId; + } + + private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: CodiconLabel, sectionName: string) { + if (shouldExpand) { + body.classList.remove('hidden'); + this.collapsedStateCache.set(sectionName, false); + twistie.text = '$(chevron-down)'; + } else { + body.classList.add('hidden'); + this.collapsedStateCache.set(sectionName, true); + twistie.text = '$(chevron-right)'; + } + } + + private renderProcessFetchError(sectionName: string, errorMessage: string) { + const container = document.getElementById('process-list'); + if (!container) { + return; + } + + const body = document.createElement('tbody'); + + this.renderProcessGroupHeader(sectionName, body, container); + + const errorRow = document.createElement('tr'); + const data = document.createElement('td'); + data.textContent = errorMessage; + data.className = 'error'; + data.colSpan = 4; + errorRow.appendChild(data); + + body.appendChild(errorRow); + container.appendChild(body); + } + + private renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) { + const headerRow = document.createElement('tr'); + + const headerData = document.createElement('td'); + headerData.colSpan = 4; + headerRow.appendChild(headerData); + + const headerContainer = document.createElement('div'); + headerContainer.className = 'header'; + headerData.appendChild(headerContainer); + + const twistieContainer = document.createElement('div'); + const twistieCodicon = new CodiconLabel(twistieContainer); + this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistieCodicon, sectionName); + headerContainer.appendChild(twistieContainer); + + const headerLabel = document.createElement('span'); + headerLabel.textContent = sectionName; + headerContainer.appendChild(headerLabel); + + this.listeners.add(addDisposableListener(headerData, 'click', (e) => { + const isHidden = body.classList.contains('hidden'); + this.updateSectionCollapsedState(isHidden, body, twistieCodicon, sectionName); + })); + + container.appendChild(headerRow); + } + + private renderTableSection(sectionName: string, processList: FormattedProcessItem[], renderManySections: boolean, sectionIsLocal: boolean): void { + const container = document.getElementById('process-list'); + if (!container) { + return; + } + + const highestCPUProcess = this.getProcessIdWithHighestProperty(processList, 'cpu'); + const highestMemoryProcess = this.getProcessIdWithHighestProperty(processList, 'memory'); + + const body = document.createElement('tbody'); + + if (renderManySections) { + this.renderProcessGroupHeader(sectionName, body, container); + } + + processList.forEach(p => { + const row = document.createElement('tr'); + row.id = p.pid.toString(); + + const cpu = document.createElement('td'); + p.pid === highestCPUProcess + ? cpu.classList.add('centered', 'highest') + : cpu.classList.add('centered'); + cpu.textContent = p.cpu.toFixed(0); + + const memory = document.createElement('td'); + p.pid === highestMemoryProcess + ? memory.classList.add('centered', 'highest') + : memory.classList.add('centered'); + memory.textContent = p.memory.toFixed(0); + + const pid = document.createElement('td'); + pid.classList.add('centered'); + pid.textContent = p.pid; + + const name = document.createElement('th'); + name.scope = 'row'; + name.classList.add('data'); + name.title = p.cmd; + name.textContent = p.formattedName; + + row.append(cpu, memory, pid, name); + + this.listeners.add(addDisposableListener(row, 'contextmenu', (e) => { + this.showContextMenu(e, p, sectionIsLocal); + })); + + body.appendChild(row); + }); + + container.appendChild(body); + } + + private async updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]): Promise { + const container = document.getElementById('process-list'); + if (!container) { + return; + } + + container.innerText = ''; + this.listeners.clear(); + + const tableHead = $('thead', undefined); + const row = $('tr'); + tableHead.append(row); + + row.append($('th.cpu', { scope: 'col' }, localize('cpu', "CPU %"))); + row.append($('th.memory', { scope: 'col' }, localize('memory', "Memory (MB)"))); + row.append($('th.pid', { scope: 'col' }, localize('pid', "PID"))); + row.append($('th.nameLabel', { scope: 'col' }, localize('name', "Name"))); + + container.append(tableHead); + + const hasMultipleMachines = Object.keys(processLists).length > 1; + const { totalmem } = await this.nativeHostService.getOSStatistics(); + processLists.forEach((remote, i) => { + const isLocal = i === 0; + if (isRemoteDiagnosticError(remote.rootProcess)) { + this.renderProcessFetchError(remote.name, remote.rootProcess.errorMessage); + } else { + this.renderTableSection(remote.name, this.getProcessList(remote.rootProcess, isLocal, totalmem), hasMultipleMachines, isLocal); + } + }); + } + + private applyStyles(styles: ProcessExplorerStyles): void { + const styleTag = document.createElement('style'); + const content: string[] = []; + + if (styles.hoverBackground) { + content.push(`tbody > tr:hover, table > tr:hover { background-color: ${styles.hoverBackground}; }`); + } + + if (styles.hoverForeground) { + content.push(`tbody > tr:hover, table > tr:hover { color: ${styles.hoverForeground}; }`); + } + + if (styles.highlightForeground) { + content.push(`.highest { color: ${styles.highlightForeground}; }`); + } + + styleTag.textContent = content.join('\n'); + if (document.head) { + document.head.appendChild(styleTag); + } + if (styles.color) { + document.body.style.color = styles.color; + } + } + + private showContextMenu(e: MouseEvent, item: FormattedProcessItem, isLocal: boolean) { + e.preventDefault(); + + const items: IContextMenuItem[] = []; + const pid = Number(item.pid); + + if (isLocal) { + items.push({ + label: localize('killProcess', "Kill Process"), + click: () => { + this.nativeHostService.killProcess(pid, 'SIGTERM'); + } + }); + + items.push({ + label: localize('forceKillProcess', "Force Kill Process"), + click: () => { + this.nativeHostService.killProcess(pid, 'SIGKILL'); + } + }); + + items.push({ + type: 'separator' + }); + } + + items.push({ + label: localize('copy', "Copy"), + click: () => { + const row = document.getElementById(pid.toString()); + if (row) { + this.nativeHostService.writeClipboardText(row.innerText); + } + } + }); + + items.push({ + label: localize('copyAll', "Copy All"), + click: () => { + const processList = document.getElementById('process-list'); + if (processList) { + this.nativeHostService.writeClipboardText(processList.innerText); + } + } + }); + + if (item && isLocal && this.isDebuggable(item.cmd)) { + items.push({ + type: 'separator' + }); + + items.push({ + label: localize('debug', "Debug"), + click: () => { + this.attachTo(item); + } + }); + } + + popup(items); + } + + private requestProcessList(totalWaitTime: number): void { + setTimeout(() => { + const nextRequestTime = Date.now(); + const waited = totalWaitTime + nextRequestTime - this.lastRequestTime; + this.lastRequestTime = nextRequestTime; + + // Wait at least a second between requests. + if (waited > 1000) { + ipcRenderer.send('vscode:windowsInfoRequest'); + ipcRenderer.send('vscode:listProcesses'); + } else { + this.requestProcessList(waited); + } + }, 200); + } + + public dispose() { + this.listeners.dispose(); + } +} + + + +export function startup(windowId: number, data: ProcessExplorerData): void { + const platformClass = data.platform === 'win32' ? 'windows' : data.platform === 'linux' ? 'linux' : 'mac'; + document.body.classList.add(platformClass); // used by our fonts + applyZoom(data.zoomLevel); + + const processExplorer = new ProcessExplorer(windowId, data); + + document.onkeydown = (e: KeyboardEvent) => { + const cmdOrCtrlKey = data.platform === 'darwin' ? e.metaKey : e.ctrlKey; + + // Cmd/Ctrl + w closes issue window + if (cmdOrCtrlKey && e.keyCode === 87) { + e.stopPropagation(); + e.preventDefault(); + + processExplorer.dispose(); + ipcRenderer.send('vscode:closeProcessExplorer'); + } + + // Cmd/Ctrl + zooms in + if (cmdOrCtrlKey && e.keyCode === 187) { + zoomIn(); + } + + // Cmd/Ctrl - zooms out + if (cmdOrCtrlKey && e.keyCode === 189) { + zoomOut(); + } + }; +} diff --git a/src/vs/code/electron-browser/proxy/auth.html b/src/vs/code/electron-sandbox/proxy/auth.html similarity index 51% rename from src/vs/code/electron-browser/proxy/auth.html rename to src/vs/code/electron-sandbox/proxy/auth.html index d02876abb94..788b68fce72 100644 --- a/src/vs/code/electron-browser/proxy/auth.html +++ b/src/vs/code/electron-sandbox/proxy/auth.html @@ -4,6 +4,8 @@ + @@ -66,8 +68,8 @@

    -

    -

    +

    +

    @@ -76,43 +78,6 @@

    - + diff --git a/src/vs/code/electron-sandbox/proxy/auth.js b/src/vs/code/electron-sandbox/proxy/auth.js new file mode 100644 index 00000000000..5e0db3c2dc7 --- /dev/null +++ b/src/vs/code/electron-sandbox/proxy/auth.js @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const { ipcRenderer } = window.vscode; + +function promptForCredentials(data) { + return new Promise((c, e) => { + const $title = document.getElementById('title'); + const $username = document.getElementById('username'); + const $password = document.getElementById('password'); + const $form = document.getElementById('form'); + const $cancel = document.getElementById('cancel'); + const $message = document.getElementById('message'); + + function submit() { + c({ username: $username.value, password: $password.value }); + return false; + } + + function cancel() { + c({ username: '', password: '' }); + return false; + } + + $form.addEventListener('submit', submit); + $cancel.addEventListener('click', cancel); + + document.body.addEventListener('keydown', function (e) { + switch (e.keyCode) { + case 27: e.preventDefault(); e.stopPropagation(); return cancel(); + case 13: e.preventDefault(); e.stopPropagation(); return submit(); + } + }); + + $title.textContent = data.title; + $message.textContent = data.message; + $username.focus(); + }); +} + +ipcRenderer.on('vscode:openProxyAuthDialog', async (event, data) => { + const response = await promptForCredentials(data); + ipcRenderer.send('vscode:proxyAuthResponse', response); +}); diff --git a/src/vs/code/electron-sandbox/workbench/workbench.html b/src/vs/code/electron-sandbox/workbench/workbench.html new file mode 100644 index 00000000000..40737461d29 --- /dev/null +++ b/src/vs/code/electron-sandbox/workbench/workbench.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js new file mode 100644 index 00000000000..3966a9d0eac --- /dev/null +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +//@ts-check +'use strict'; + +(function () { + + // Add a perf entry right from the top + const perf = perfLib(); + perf.mark('renderer/started'); + + // Load environment in parallel to workbench loading to avoid waterfall + const bootstrapWindow = bootstrapWindowLib(); + const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved(); + + // Load workbench main JS, CSS and NLS all in parallel. This is an + // optimization to prevent a waterfall of loading to happen, because + // we know for a fact that workbench.desktop.sandbox.main will depend on + // the related CSS and NLS counterparts. + bootstrapWindow.load([ + 'vs/workbench/workbench.desktop.sandbox.main', + 'vs/nls!vs/workbench/workbench.desktop.main', + 'vs/css!vs/workbench/workbench.desktop.main' + ], + async function (workbench, configuration) { + + // Mark start of workbench + perf.mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); + + // Wait for process environment being fully resolved + await whenEnvResolved; + + perf.mark('main/startup'); + + // @ts-ignore + return require('vs/workbench/electron-sandbox/desktop.main').main(configuration); + }, + { + removeDeveloperKeybindingsAfterLoad: true, + canModifyDOM: function (windowConfig) { + // TODO@sandbox part-splash is non-sandboxed only + }, + beforeLoaderConfig: function (windowConfig, loaderConfig) { + loaderConfig.recordStats = true; + }, + beforeRequire: function () { + perf.mark('willLoadWorkbenchMain'); + } + } + ); + + + //region Helpers + + function perfLib() { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + } + }; + } + + /** + * @returns {{ + * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * }} + */ + function bootstrapWindowLib() { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; + } + + //#endregion + +}()); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 609395b535a..040144f4c9d 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -7,33 +7,34 @@ import * as os from 'os'; import * as fs from 'fs'; import { spawn, ChildProcess, SpawnOptions } from 'child_process'; import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { parseCLIProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; import * as paths from 'vs/base/common/path'; import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; import { findFreePort, randomPort } from 'vs/base/node/ports'; import { isWindows, isLinux } from 'vs/base/common/platform'; -import { ProfilingSession, Target } from 'v8-inspect-profiler'; +import type { ProfilingSession, Target } from 'v8-inspect-profiler'; import { isString } from 'vs/base/common/types'; import { hasStdinWithoutTty, stdinDataListener, getStdinFilePath, readFromStdin } from 'vs/platform/environment/node/stdin'; -function shouldSpawnCliProcess(argv: ParsedArgs): boolean { +function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { return !!argv['install-source'] || !!argv['list-extensions'] || !!argv['install-extension'] + || !!argv['install-builtin-extension'] || !!argv['uninstall-extension'] || !!argv['locate-extension'] || !!argv['telemetry']; } interface IMainCli { - main: (argv: ParsedArgs) => Promise; + main: (argv: NativeParsedArgs) => Promise; } export async function main(argv: string[]): Promise { - let args: ParsedArgs; + let args: NativeParsedArgs; try { args = parseCLIProcessArgv(argv); @@ -44,7 +45,7 @@ export async function main(argv: string[]): Promise { // Help if (args.help) { - const executable = `${product.applicationName}${os.platform() === 'win32' ? '.exe' : ''}`; + const executable = `${product.applicationName}${isWindows ? '.exe' : ''}`; console.log(buildHelpMessage(product.nameLong, executable, product.version, OPTIONS)); } @@ -95,9 +96,9 @@ export async function main(argv: string[]): Promise { // On Windows we use a different strategy of saving the file // by first truncating the file and then writing with r+ mode. // This helps to save hidden files on Windows - // (see https://github.com/Microsoft/vscode/issues/931) and + // (see https://github.com/microsoft/vscode/issues/931) and // prevent removing alternate data streams - // (see https://github.com/Microsoft/vscode/issues/6363) + // (see https://github.com/microsoft/vscode/issues/6363) fs.truncateSync(target, 0); writeFileSync(target, data, { flag: 'r+' }); } else { @@ -128,7 +129,7 @@ export async function main(argv: string[]): Promise { delete env['ELECTRON_RUN_AS_NODE']; - const processCallbacks: ((child: ChildProcess) => Promise)[] = []; + const processCallbacks: ((child: ChildProcess) => Promise)[] = []; const verbose = args.verbose || args.status; if (verbose) { @@ -138,7 +139,7 @@ export async function main(argv: string[]): Promise { child.stdout!.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); child.stderr!.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); - await new Promise(c => child.once('exit', () => c())); + await new Promise(resolve => child.once('exit', () => resolve())); }); } @@ -154,7 +155,7 @@ export async function main(argv: string[]): Promise { // Read from stdin: we require a single "-" argument to be passed in order to start reading from // stdin. We do this because there is no reliable way to find out if data is piped to stdin. Just - // checking for stdin being connected to a TTY is not enough (https://github.com/Microsoft/vscode/issues/40351) + // checking for stdin being connected to a TTY is not enough (https://github.com/microsoft/vscode/issues/40351) if (args._.length === 0) { if (hasReadStdinArg) { @@ -332,13 +333,13 @@ export async function main(argv: string[]): Promise { const child = spawn(process.execPath, argv.slice(2), options); if (args.wait && waitMarkerFilePath) { - return new Promise(c => { + return new Promise(resolve => { // Complete when process exits - child.once('exit', () => c(undefined)); + child.once('exit', () => resolve(undefined)); // Complete when wait marker file is deleted - whenDeleted(waitMarkerFilePath!).then(c, c); + whenDeleted(waitMarkerFilePath!).then(resolve, resolve); }).then(() => { // Make sure to delete the tmp stdin file if we have any diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 2ba0de50620..426cde72bce 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -4,17 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; +import { raceTimeout } from 'vs/base/common/async'; +import * as semver from 'vs/base/common/semver/semver'; import product from 'vs/platform/product/common/product'; import * as path from 'vs/base/common/path'; -import * as semver from 'semver-umd'; - import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IExtensionManagementService, IExtensionGalleryService, IGalleryExtension, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IExtensionManagementService, IExtensionGalleryService, IGalleryExtension, ILocalExtension, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -24,7 +25,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { getBaseLabel } from 'vs/base/common/labels'; @@ -35,7 +36,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { URI } from 'vs/base/common/uri'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; -import { IExtensionManifest, ExtensionType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, isLanguagePackExtension, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { Schemas } from 'vs/base/common/network'; @@ -49,7 +50,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); -const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-vscode.csharp'); +const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-dotnettools.csharp'); function getId(manifest: IExtensionManifest, withVersion?: boolean): string { if (withVersion) { @@ -69,25 +70,26 @@ export function getIdAndVersion(id: string): [string, string | undefined] { return [adoptToGalleryExtensionId(id), undefined]; } +type InstallExtensionInfo = { id: string, version?: string, installOptions: InstallOptions }; export class Main { constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService ) { } - async run(argv: ParsedArgs): Promise { + async run(argv: NativeParsedArgs): Promise { if (argv['install-source']) { await this.setInstallSource(argv['install-source']); } else if (argv['list-extensions']) { await this.listExtensions(!!argv['show-versions'], argv['category']); - } else if (argv['install-extension']) { - await this.installExtensions(argv['install-extension'], !!argv['force']); + } else if (argv['install-extension'] || argv['install-builtin-extension']) { + await this.installExtensions(argv['install-extension'] || [], argv['install-builtin-extension'] || [], !!argv['do-not-sync'], !!argv['force']); } else if (argv['uninstall-extension']) { - await this.uninstallExtension(argv['uninstall-extension']); + await this.uninstallExtension(argv['uninstall-extension'], !!argv['force']); } else if (argv['locate-extension']) { await this.locateExtension(argv['locate-extension']); } else if (argv['telemetry']) { @@ -101,8 +103,7 @@ export class Main { private async listExtensions(showVersions: boolean, category?: string): Promise { let extensions = await this.extensionManagementService.getInstalled(ExtensionType.User); - // TODO: we should save this array in a common place so that the command and extensionQuery can use it that way changing it is easier - const categories = ['"programming languages"', 'snippets', 'linters', 'themes', 'debuggers', 'formatters', 'keymaps', '"scm providers"', 'other', '"extension packs"', '"language packs"']; + const categories = EXTENSION_CATEGORIES.map(c => c.toLowerCase()); if (category && category !== '') { if (categories.indexOf(category.toLowerCase()) < 0) { console.log('Invalid category please enter a valid category. To list valid categories run --category without a category specified'); @@ -125,88 +126,148 @@ export class Main { extensions.forEach(e => console.log(getId(e.manifest, showVersions))); } - private async installExtensions(extensions: string[], force: boolean): Promise { + private async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise { const failed: string[] = []; const installedExtensionsManifests: IExtensionManifest[] = []; if (extensions.length) { console.log(localize('installingExtensions', "Installing extensions...")); } + const vsixs: string[] = []; + const installExtensionInfos: InstallExtensionInfo[] = []; for (const extension of extensions) { - try { - const manifest = await this.installExtension(extension, force); - if (manifest) { - installedExtensionsManifests.push(manifest); - } - } catch (err) { - console.error(err.message || err.stack || err); - failed.push(extension); + if (/\.vsix$/i.test(extension)) { + vsixs.push(extension); + } else { + const [id, version] = getIdAndVersion(extension); + installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } }); } } + for (const extension of builtinExtensionIds) { + const [id, version] = getIdAndVersion(extension); + installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } }); + } + + if (vsixs.length) { + await Promise.all(vsixs.map(async vsix => { + try { + const manifest = await this.installVSIX(vsix, force); + if (manifest) { + installedExtensionsManifests.push(manifest); + } + } catch (err) { + console.error(err.message || err.stack || err); + failed.push(vsix); + } + })); + } + + const [galleryExtensions, installed] = await Promise.all([ + this.getGalleryExtensions(installExtensionInfos), + this.extensionManagementService.getInstalled(ExtensionType.User) + ]); + + await Promise.all(installExtensionInfos.map(async extensionInfo => { + const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase()); + if (gallery) { + try { + const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force); + if (manifest) { + installedExtensionsManifests.push(manifest); + } + } catch (err) { + console.error(err.message || err.stack || err); + failed.push(extensionInfo.id); + } + } else { + console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`); + failed.push(extensionInfo.id); + } + })); + if (installedExtensionsManifests.some(manifest => isLanguagePackExtension(manifest))) { await this.updateLocalizationsCache(); } - return failed.length ? Promise.reject(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))) : Promise.resolve(); + + if (failed.length) { + throw new Error(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))); + } } - private async installExtension(extension: string, force: boolean): Promise { - if (/\.vsix$/i.test(extension)) { - extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension); - - const manifest = await getManifest(extension); - const valid = await this.validate(manifest, force); - - if (valid) { - return this.extensionManagementService.install(URI.file(extension)).then(id => { - console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed.", getBaseLabel(extension))); - return manifest; - }, error => { - if (isPromiseCanceledError(error)) { - console.log(localize('cancelVsixInstall', "Cancelled installing extension '{0}'.", getBaseLabel(extension))); - return null; - } else { - return Promise.reject(error); - } - }); + private async installVSIX(vsix: string, force: boolean): Promise { + vsix = path.isAbsolute(vsix) ? vsix : path.join(process.cwd(), vsix); + const manifest = await getManifest(vsix); + const valid = await this.validate(manifest, force); + if (valid) { + try { + await this.extensionManagementService.install(URI.file(vsix)); + console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed.", getBaseLabel(vsix))); + return manifest; + } catch (error) { + if (isPromiseCanceledError(error)) { + console.log(localize('cancelVsixInstall', "Cancelled installing extension '{0}'.", getBaseLabel(vsix))); + return null; + } else { + throw error; + } } - return null; + } + return null; + } + + private async getGalleryExtensions(extensions: InstallExtensionInfo[]): Promise> { + const extensionIds = extensions.filter(({ version }) => version === undefined).map(({ id }) => id); + const extensionsWithIdAndVersion = extensions.filter(({ version }) => version !== undefined); + + const galleryExtensions = new Map(); + await Promise.all([ + (async () => { + const result = await this.extensionGalleryService.getExtensions(extensionIds, CancellationToken.None); + result.forEach(extension => galleryExtensions.set(extension.identifier.id.toLowerCase(), extension)); + })(), + Promise.all(extensionsWithIdAndVersion.map(async ({ id, version }) => { + const extension = await this.extensionGalleryService.getCompatibleExtension({ id }, version); + if (extension) { + galleryExtensions.set(extension.identifier.id.toLowerCase(), extension); + } + })) + ]); + + return galleryExtensions; + } + + private async installFromGallery({ id, version, installOptions }: InstallExtensionInfo, galleryExtension: IGalleryExtension, installed: ILocalExtension[], force: boolean): Promise { + const manifest = await this.extensionGalleryService.getManifest(galleryExtension, CancellationToken.None); + const installedExtension = installed.find(e => areSameExtensions(e.identifier, galleryExtension.identifier)); + if (installedExtension) { + if (galleryExtension.version === installedExtension.manifest.version) { + console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id)); + return null; + } + if (!version && !force) { + console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, galleryExtension.version)); + return null; + } + console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, galleryExtension.version)); } - const [id, version] = getIdAndVersion(extension); - return this.extensionManagementService.getInstalled(ExtensionType.User) - .then(installed => this.extensionGalleryService.getCompatibleExtension({ id }, version) - .then(null, err => { - if (err.responseText) { - try { - const response = JSON.parse(err.responseText); - return Promise.reject(response.message); - } catch (e) { - // noop - } - } - return Promise.reject(err); - }) - .then(async extension => { - if (!extension) { - return Promise.reject(new Error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`)); - } - - const manifest = await this.extensionGalleryService.getManifest(extension, CancellationToken.None); - const [installedExtension] = installed.filter(e => areSameExtensions(e.identifier, { id })); - if (installedExtension) { - if (extension.version === installedExtension.manifest.version) { - console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id)); - return Promise.resolve(null); - } - if (!version && !force) { - console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, extension.version)); - return Promise.resolve(null); - } - console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, extension.version)); - } - await this.installFromGallery(id, extension); - return manifest; - })); + try { + if (installOptions.isBuiltin) { + console.log(localize('installing builtin ', "Installing builtin extension '{0}' v{1}...", id, galleryExtension.version)); + } else { + console.log(localize('installing', "Installing extension '{0}' v{1}...", id, galleryExtension.version)); + } + await this.extensionManagementService.installFromGallery(galleryExtension, installOptions); + console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, galleryExtension.version)); + return manifest; + } catch (error) { + if (isPromiseCanceledError(error)) { + console.log(localize('cancelInstall', "Cancelled installing extension '{0}'.", id)); + return null; + } else { + throw error; + } + } } private async validate(manifest: IExtensionManifest, force: boolean): Promise { @@ -216,7 +277,7 @@ export class Main { const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User); - const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, local.identifier) && semver.gt(local.manifest.version, manifest.version))[0]; + const newer = installedExtensions.find(local => areSameExtensions(extensionIdentifier, local.identifier) && semver.gt(local.manifest.version, manifest.version)); if (newer && !force) { console.log(localize('forceDowngrade', "A newer version of extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", newer.identifier.id, newer.manifest.version, manifest.version)); @@ -226,22 +287,7 @@ export class Main { return true; } - private async installFromGallery(id: string, extension: IGalleryExtension): Promise { - console.log(localize('installing', "Installing extension '{0}' v{1}...", id, extension.version)); - - try { - await this.extensionManagementService.installFromGallery(extension); - console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, extension.version)); - } catch (error) { - if (isPromiseCanceledError(error)) { - console.log(localize('cancelVsixInstall', "Cancelled installing extension '{0}'.", id)); - } else { - throw error; - } - } - } - - private async uninstallExtension(extensions: string[]): Promise { + private async uninstallExtension(extensions: string[], force: boolean): Promise { async function getExtensionId(extensionDescription: string): Promise { if (!/\.vsix$/i.test(extensionDescription)) { return extensionDescription; @@ -255,10 +301,18 @@ export class Main { const uninstalledExtensions: ILocalExtension[] = []; for (const extension of extensions) { const id = await getExtensionId(extension); - const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); - const [extensionToUninstall] = installed.filter(e => areSameExtensions(e.identifier, { id })); + const installed = await this.extensionManagementService.getInstalled(); + const extensionToUninstall = installed.find(e => areSameExtensions(e.identifier, { id })); if (!extensionToUninstall) { - return Promise.reject(new Error(`${notInstalled(id)}\n${useId}`)); + throw new Error(`${notInstalled(id)}\n${useId}`); + } + if (extensionToUninstall.type === ExtensionType.System) { + console.log(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be installed", id)); + return; + } + if (extensionToUninstall.isBuiltin && !force) { + console.log(localize('forceUninstall', "Extension '{0}' is marked as a Built-in extension by user. Please use '--force' option to uninstall it.", id)); + return; } console.log(localize('uninstalling', "Uninstalling {0}...", id)); await this.extensionManagementService.uninstall(extensionToUninstall, true); @@ -294,11 +348,11 @@ export class Main { const eventPrefix = 'monacoworkbench'; -export async function main(argv: ParsedArgs): Promise { +export async function main(argv: NativeParsedArgs): Promise { const services = new ServiceCollection(); const disposables = new DisposableStore(); - const environmentService = new EnvironmentService(argv, process.execPath); + const environmentService = new NativeEnvironmentService(argv); const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService)); process.once('exit', () => logService.dispose()); logService.info('main', argv); @@ -306,16 +360,6 @@ export async function main(argv: ParsedArgs): Promise { await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath] .map((path): undefined | Promise => path ? mkdirp(path) : undefined)); - const configurationService = new ConfigurationService(environmentService.settingsResource); - disposables.add(configurationService); - await configurationService.initialize(); - - services.set(IEnvironmentService, environmentService); - services.set(ILogService, logService); - services.set(IConfigurationService, configurationService); - services.set(IStateService, new SyncDescriptor(StateService)); - services.set(IProductService, { _serviceBrand: undefined, ...product }); - // Files const fileService = new FileService(logService); disposables.add(fileService); @@ -325,30 +369,39 @@ export async function main(argv: ParsedArgs): Promise { disposables.add(diskFileSystemProvider); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + const configurationService = new ConfigurationService(environmentService.settingsResource, fileService); + disposables.add(configurationService); + await configurationService.initialize(); + + services.set(IEnvironmentService, environmentService); + services.set(INativeEnvironmentService, environmentService); + + services.set(ILogService, logService); + services.set(IConfigurationService, configurationService); + services.set(IStateService, new SyncDescriptor(StateService)); + services.set(IProductService, { _serviceBrand: undefined, ...product }); + const instantiationService: IInstantiationService = new InstantiationService(services); return instantiationService.invokeFunction(async accessor => { - const envService = accessor.get(IEnvironmentService); const stateService = accessor.get(IStateService); - const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; const services = new ServiceCollection(); - - services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); const appenders: AppInsightsAppender[] = []; - if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) { - + if (isBuilt && !extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey) { - appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService)); + appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey)); } const config: ITelemetryServiceConfig = { appender: combinedAppender(...appenders), + sendErrorTelemetry: false, commonProperties: resolveCommonProperties(product.commit, product.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath), piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] }; @@ -364,8 +417,10 @@ export async function main(argv: ParsedArgs): Promise { try { await main.run(argv); + // Flush the remaining data in AI adapter. - await combinedAppender(...appenders).flush(); + // If it does not complete in 1 second, exit the process. + await raceTimeout(combinedAppender(...appenders).flush(), 1000); } finally { disposables.dispose(); } diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts deleted file mode 100644 index f605a4526f3..00000000000 --- a/src/vs/code/node/paths.ts +++ /dev/null @@ -1,137 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'vs/base/common/path'; -import * as arrays from 'vs/base/common/arrays'; -import * as strings from 'vs/base/common/strings'; -import * as extpath from 'vs/base/common/extpath'; -import * as platform from 'vs/base/common/platform'; -import * as types from 'vs/base/common/types'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; - -export function validatePaths(args: ParsedArgs): ParsedArgs { - - // Track URLs if they're going to be used - if (args['open-url']) { - args._urls = args._; - args._ = []; - } - - if (!args['remote']) { - // Normalize paths and watch out for goto line mode - const paths = doValidatePaths(args._, args.goto); - args._ = paths; - } - - return args; -} - -function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { - const cwd = process.env['VSCODE_CWD'] || process.cwd(); - const result = args.map(arg => { - let pathCandidate = String(arg); - - let parsedPath: IPathWithLineAndColumn | undefined = undefined; - if (gotoLineMode) { - parsedPath = parseLineAndColumnAware(pathCandidate); - pathCandidate = parsedPath.path; - } - - if (pathCandidate) { - pathCandidate = preparePath(cwd, pathCandidate); - } - - const sanitizedFilePath = extpath.sanitizeFilePath(pathCandidate, cwd); - - const basename = path.basename(sanitizedFilePath); - if (basename /* can be empty if code is opened on root */ && !extpath.isValidBasename(basename)) { - return null; // do not allow invalid file names - } - - if (gotoLineMode && parsedPath) { - parsedPath.path = sanitizedFilePath; - - return toPath(parsedPath); - } - - return sanitizedFilePath; - }); - - const caseInsensitive = platform.isWindows || platform.isMacintosh; - const distinct = arrays.distinct(result, e => e && caseInsensitive ? e.toLowerCase() : (e || '')); - - return arrays.coalesce(distinct); -} - -function preparePath(cwd: string, p: string): string { - - // Trim trailing quotes - if (platform.isWindows) { - p = strings.rtrim(p, '"'); // https://github.com/Microsoft/vscode/issues/1498 - } - - // Trim whitespaces - p = strings.trim(strings.trim(p, ' '), '\t'); - - if (platform.isWindows) { - - // Resolve the path against cwd if it is relative - p = path.resolve(cwd, p); - - // Trim trailing '.' chars on Windows to prevent invalid file names - p = strings.rtrim(p, '.'); - } - - return p; -} - -export interface IPathWithLineAndColumn { - path: string; - line?: number; - column?: number; -} - -export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn { - const segments = rawPath.split(':'); // C:\file.txt:: - - let path: string | null = null; - let line: number | null = null; - let column: number | null = null; - - segments.forEach(segment => { - const segmentAsNumber = Number(segment); - if (!types.isNumber(segmentAsNumber)) { - path = !!path ? [path, segment].join(':') : segment; // a colon can well be part of a path (e.g. C:\...) - } else if (line === null) { - line = segmentAsNumber; - } else if (column === null) { - column = segmentAsNumber; - } - }); - - if (!path) { - throw new Error('Format for `--goto` should be: `FILE:LINE(:COLUMN)`'); - } - - return { - path: path, - line: line !== null ? line : undefined, - column: column !== null ? column : line !== null ? 1 : undefined // if we have a line, make sure column is also set - }; -} - -function toPath(p: IPathWithLineAndColumn): string { - const segments = [p.path]; - - if (types.isNumber(p.line)) { - segments.push(String(p.line)); - } - - if (types.isNumber(p.column)) { - segments.push(String(p.column)); - } - - return segments.join(':'); -} diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index b431b6d0514..619886d0fd3 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as cp from 'child_process'; -import { assign } from 'vs/base/common/objects'; +import { spawn } from 'child_process'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; function getUnixShellEnvironment(logService: ILogService): Promise { const promise = new Promise((resolve, reject) => { @@ -21,16 +20,17 @@ function getUnixShellEnvironment(logService: ILogService): Promise ({})); } - -let _shellEnv: Promise; +let shellEnvPromise: Promise | undefined = undefined; /** * We need to get the environment from a user's shell. * This should only be done when Code itself is not launched * from within a shell. */ -export function getShellEnvironment(logService: ILogService, environmentService: IEnvironmentService): Promise { - if (_shellEnv === undefined) { +export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise { + if (!shellEnvPromise) { if (environmentService.args['disable-user-env-probe']) { logService.trace('getShellEnvironment: disable-user-env-probe set, skipping'); - _shellEnv = Promise.resolve({}); + shellEnvPromise = Promise.resolve({}); } else if (isWindows) { logService.trace('getShellEnvironment: running on Windows, skipping'); - _shellEnv = Promise.resolve({}); + shellEnvPromise = Promise.resolve({}); } else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') { logService.trace('getShellEnvironment: running on CLI, skipping'); - _shellEnv = Promise.resolve({}); + shellEnvPromise = Promise.resolve({}); } else { logService.trace('getShellEnvironment: running on Unix'); - _shellEnv = getUnixShellEnvironment(logService); + shellEnvPromise = getUnixShellEnvironment(logService); } } - return _shellEnv; + return shellEnvPromise; } diff --git a/src/vs/code/test/electron-main/nativeHelpers.test.ts b/src/vs/code/test/electron-main/nativeHelpers.test.ts index 199fa7acaf7..1ce46448038 100644 --- a/src/vs/code/test/electron-main/nativeHelpers.test.ts +++ b/src/vs/code/test/electron-main/nativeHelpers.test.ts @@ -28,9 +28,8 @@ suite('Windows Native Helpers', () => { }); test('vscode-windows-ca-certs', async () => { - const windowsCerts = await new Promise((resolve, reject) => { - require(['vscode-windows-ca-certs'], resolve, reject); - }); + // @ts-ignore Windows only + const windowsCerts = await import('vscode-windows-ca-certs'); assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.'); }); diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 69c6240891d..5ad45ba6a0d 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -7,7 +7,7 @@ *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/Microsoft/vscode-loader/ + * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- @@ -53,7 +53,7 @@ var CSSBuildLoaderPlugin; BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { this._pendingLoads++; var head = document.head || document.getElementsByTagName('head')[0]; - var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script'); + var other = head.getElementsByTagName('link') || head.getElementsByTagName('script'); if (other.length > 0) { head.insertBefore(linkNode, other[other.length - 1]); } @@ -319,7 +319,7 @@ var CSSBuildLoaderPlugin; global.cssInlinedResources = global.cssInlinedResources || []; var normalizedFSPath = fsPath.replace(/\\/g, '/'); if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { - console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); + // console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); } global.cssInlinedResources.push(normalizedFSPath); var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; diff --git a/src/vs/css.js b/src/vs/css.js index 4a2d5a4d299..8ec30aa7342 100644 --- a/src/vs/css.js +++ b/src/vs/css.js @@ -7,7 +7,7 @@ *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/Microsoft/vscode-loader/ + * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------------- @@ -51,7 +51,7 @@ var CSSLoaderPlugin; BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { this._pendingLoads++; var head = document.head || document.getElementsByTagName('head')[0]; - var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script'); + var other = head.getElementsByTagName('link') || head.getElementsByTagName('script'); if (other.length > 0) { head.insertBefore(linkNode, other[other.length - 1]); } @@ -97,7 +97,14 @@ var CSSLoaderPlugin; function CSSPlugin() { this._cssLoader = new BrowserCSSLoader(); } - CSSPlugin.prototype.load = function (name, req, load) { + CSSPlugin.prototype.load = function (name, req, load, config) { + config = config || {}; + var cssConfig = config['vs/css'] || {}; + if (cssConfig.disabled) { + // the plugin is asked to not create any style sheets + load({}); + return; + } var cssUrl = req.toUrl(name + '.css'); this._cssLoader.load(name, cssUrl, function (contents) { load({}); diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 479a35c7bea..97fe48dd732 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -124,12 +124,12 @@ class DomCharWidthReader { private static _render(testElement: HTMLElement, request: CharWidthRequest): void { if (request.chr === ' ') { - let htmlString = ' '; + let htmlString = '\u00a0'; // Repeat character 256 (2^8) times for (let i = 0; i < 8; i++) { htmlString += htmlString; } - testElement.innerHTML = htmlString; + testElement.innerText = htmlString; } else { let testString = request.chr; // Repeat character 256 (2^8) times diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 142b4b3eda1..ee76fd4b903 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -11,10 +11,11 @@ import * as platform from 'vs/base/common/platform'; import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { EditorOption, IEditorConstructionOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; +import { EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { IDimension } from 'vs/editor/common/editorCommon'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; class CSSBasedConfigurationCache { @@ -88,6 +89,7 @@ export interface ISerializedFontInfo { readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; middotWidth: number; + wsmiddotWidth: number; readonly maxDigitWidth: number; } @@ -161,6 +163,7 @@ class CSSBasedConfiguration extends Disposable { // compatibility with older versions of VS Code which did not store this... savedFontInfo.fontFeatureSettings = savedFontInfo.fontFeatureSettings || EditorFontLigatures.OFF; savedFontInfo.middotWidth = savedFontInfo.middotWidth || savedFontInfo.spaceWidth; + savedFontInfo.wsmiddotWidth = savedFontInfo.wsmiddotWidth || savedFontInfo.spaceWidth; const fontInfo = new FontInfo(savedFontInfo, false); this._writeToCache(fontInfo, fontInfo); } @@ -186,6 +189,7 @@ class CSSBasedConfiguration extends Disposable { canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow, spaceWidth: Math.max(readConfig.spaceWidth, 5), middotWidth: Math.max(readConfig.middotWidth, 5), + wsmiddotWidth: Math.max(readConfig.wsmiddotWidth, 5), maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5), }, false); } @@ -226,9 +230,12 @@ class CSSBasedConfiguration extends Disposable { const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace); const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null); - // middle dot character + // U+00B7 - MIDDLE DOT const middot = this.createRequest('·', CharWidthRequestType.Regular, all, monospace); + // U+2E31 - WORD SEPARATOR MIDDLE DOT + const wsmiddotWidth = this.createRequest(String.fromCharCode(0x2E31), CharWidthRequestType.Regular, all, null); + // monospace test: some characters this.createRequest('|', CharWidthRequestType.Regular, all, monospace); this.createRequest('/', CharWidthRequestType.Regular, all, monospace); @@ -294,6 +301,7 @@ class CSSBasedConfiguration extends Disposable { canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow, spaceWidth: space.width, middotWidth: middot.width, + wsmiddotWidth: wsmiddotWidth.width, maxDigitWidth: maxDigitWidth }, canTrustBrowserZoomLevel); } @@ -338,7 +346,7 @@ export class Configuration extends CommonEditorConfiguration { } this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions())); - this._register(this.accessibilityService.onDidChangeAccessibilitySupport(() => this._recomputeOptions())); + this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions())); this._recomputeOptions(); } @@ -379,7 +387,11 @@ export class Configuration extends CommonEditorConfiguration { emptySelectionClipboard: browser.isWebKit || browser.isFirefox, pixelRatio: browser.getPixelRatio(), zoomLevel: browser.getZoomLevel(), - accessibilitySupport: this.accessibilityService.getAccessibilitySupport() + accessibilitySupport: ( + this.accessibilityService.isScreenReaderOptimized() + ? AccessibilitySupport.Enabled + : this.accessibilityService.getAccessibilitySupport() + ) }; } diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index 16eea46d1f3..06b42c6f9c6 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -6,21 +6,49 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IDimension } from 'vs/editor/common/editorCommon'; +interface ResizeObserver { + observe(target: Element): void; + unobserve(target: Element): void; + disconnect(): void; +} + +interface ResizeObserverSize { + inlineSize: number; + blockSize: number; +} + +interface ResizeObserverEntry { + readonly target: Element; + readonly contentRect: DOMRectReadOnly; + readonly borderBoxSize: ResizeObserverSize; + readonly contentBoxSize: ResizeObserverSize; +} + +type ResizeObserverCallback = (entries: ReadonlyArray, observer: ResizeObserver) => void; + +declare const ResizeObserver: { + prototype: ResizeObserver; + new(callback: ResizeObserverCallback): ResizeObserver; +}; + + export class ElementSizeObserver extends Disposable { private readonly referenceDomElement: HTMLElement | null; - private measureReferenceDomElementToken: any; private readonly changeCallback: () => void; private width: number; private height: number; + private resizeObserver: ResizeObserver | null; + private measureReferenceDomElementToken: number; constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) { super(); this.referenceDomElement = referenceDomElement; this.changeCallback = changeCallback; - this.measureReferenceDomElementToken = -1; this.width = -1; this.height = -1; + this.resizeObserver = null; + this.measureReferenceDomElementToken = -1; this.measureReferenceDomElement(false, dimension); } @@ -38,12 +66,30 @@ export class ElementSizeObserver extends Disposable { } public startObserving(): void { - if (this.measureReferenceDomElementToken === -1) { - this.measureReferenceDomElementToken = setInterval(() => this.measureReferenceDomElement(true), 100); + if (typeof ResizeObserver !== 'undefined') { + if (!this.resizeObserver && this.referenceDomElement) { + this.resizeObserver = new ResizeObserver((entries) => { + if (entries && entries[0] && entries[0].contentRect) { + this.observe({ width: entries[0].contentRect.width, height: entries[0].contentRect.height }); + } else { + this.observe(); + } + }); + this.resizeObserver.observe(this.referenceDomElement); + } + } else { + if (this.measureReferenceDomElementToken === -1) { + // setInterval type defaults to NodeJS.Timeout instead of number, so specify it as a number + this.measureReferenceDomElementToken = setInterval(() => this.observe(), 100); + } } } public stopObserving(): void { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } if (this.measureReferenceDomElementToken !== -1) { clearInterval(this.measureReferenceDomElementToken); this.measureReferenceDomElementToken = -1; diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index cb20bc72ea6..fd78371ffbc 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { isFirefox } from 'vs/base/browser/browser'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as types from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Command, EditorCommand, ICommandOptions, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { Command, EditorCommand, ICommandOptions, registerEditorCommand, MultiCommand, UndoCommand, RedoCommand, SelectAllCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/controller/cursorColumnSelection'; -import { CursorContext, CursorState, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon'; +import { CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { CursorMove as CursorMove_, CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; @@ -20,25 +21,26 @@ import { Range } from 'vs/editor/common/core/range'; import { Handler, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { MenuId } from 'vs/platform/actions/common/actions'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; const CORE_WEIGHT = KeybindingWeight.EditorCore; export abstract class CoreEditorCommand extends EditorCommand { public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void { - const cursors = editor._getCursors(); - if (!cursors) { + const viewModel = editor._getViewModel(); + if (!viewModel) { // the editor has no view => has no cursors return; } - this.runCoreEditorCommand(cursors, args || {}); + this.runCoreEditorCommand(viewModel, args || {}); } - public abstract runCoreEditorCommand(cursors: ICursors, args: any): void; + public abstract runCoreEditorCommand(viewModel: IViewModel, args: any): void; } export namespace EditorScroll_ { @@ -215,7 +217,7 @@ export namespace RevealLine_ { const reveaLineArg: RawArguments = arg; - if (!types.isNumber(reveaLineArg.lineNumber)) { + if (!types.isNumber(reveaLineArg.lineNumber) && !types.isString(reveaLineArg.lineNumber)) { return false; } @@ -244,7 +246,7 @@ export namespace RevealLine_ { 'required': ['lineNumber'], 'properties': { 'lineNumber': { - 'type': 'number', + 'type': ['number', 'string'], }, 'at': { 'type': 'string', @@ -260,7 +262,7 @@ export namespace RevealLine_ { * Arguments for reveal line command */ export interface RawArguments { - lineNumber?: number; + lineNumber?: number | string; at?: string; } @@ -274,6 +276,54 @@ export namespace RevealLine_ { }; } +abstract class EditorOrNativeTextInputCommand { + + constructor(target: MultiCommand) { + // 1. handle case when focus is in editor. + target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { + // Only if editor text focus (i.e. not if editor has widget focus). + const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (focusedEditor && focusedEditor.hasTextFocus()) { + return this._runEditorCommand(accessor, focusedEditor, args); + } + return false; + }); + + // 2. handle case when focus is in some other `input` / `textarea`. + target.addImplementation(1000, (accessor: ServicesAccessor, args: any) => { + // Only if focused on an element that allows for entering text + const activeElement = document.activeElement; + if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { + this.runDOMCommand(); + return true; + } + return false; + }); + + // 3. (default) handle case when focus is somewhere else. + target.addImplementation(0, (accessor: ServicesAccessor, args: any) => { + // Redirecting to active editor + const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor(); + if (activeEditor) { + activeEditor.focus(); + return this._runEditorCommand(accessor, activeEditor, args); + } + return false; + }); + } + + public _runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): boolean | Promise { + const result = this.runEditorCommand(accessor, editor, args); + if (result) { + return result; + } + return true; + } + + public abstract runDOMCommand(): void; + public abstract runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void | Promise; +} + export namespace CoreNavigationCommands { class BaseMoveToCommand extends CoreEditorCommand { @@ -285,16 +335,16 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ - CursorMoveCommands.moveTo(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition) + CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition) ] ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } } @@ -311,21 +361,25 @@ export namespace CoreNavigationCommands { })); abstract class ColumnSelectCommand extends CoreEditorCommand { - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - const result = this._getColumnSelectResult(cursors.context, cursors.getPrimaryCursor(), cursors.getColumnSelectData(), args); - cursors.setStates(args.source, CursorChangeReason.Explicit, result.viewStates.map((viewState) => CursorState.fromViewState(viewState))); - cursors.setColumnSelectData({ + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + const result = this._getColumnSelectResult(viewModel, viewModel.getPrimaryCursorState(), viewModel.getCursorColumnSelectData(), args); + viewModel.setCursorStates(args.source, CursorChangeReason.Explicit, result.viewStates.map((viewState) => CursorState.fromViewState(viewState))); + viewModel.setCursorColumnSelectData({ isReal: true, fromViewLineNumber: result.fromLineNumber, fromViewVisualColumn: result.fromVisualColumn, toViewLineNumber: result.toLineNumber, toViewVisualColumn: result.toVisualColumn }); - cursors.reveal(args.source, true, (result.reversed ? RevealTarget.TopMost : RevealTarget.BottomMost), ScrollType.Smooth); + if (result.reversed) { + viewModel.revealTopMostCursor(args.source); + } else { + viewModel.revealBottomMostCursor(args.source); + } } - protected abstract _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult; + protected abstract _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult; } @@ -337,15 +391,15 @@ export namespace CoreNavigationCommands { }); } - protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { // validate `args` - const validatedPosition = context.model.validatePosition(args.position); - const validatedViewPosition = context.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition); + const validatedPosition = viewModel.model.validatePosition(args.position); + const validatedViewPosition = viewModel.coordinatesConverter.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition); let fromViewLineNumber = args.doColumnSelect ? prevColumnSelectData.fromViewLineNumber : validatedViewPosition.lineNumber; let fromViewVisualColumn = args.doColumnSelect ? prevColumnSelectData.fromViewVisualColumn : args.mouseColumn - 1; - return ColumnSelection.columnSelect(context.config, context.viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1); + return ColumnSelection.columnSelect(viewModel.cursorConfig, viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1); } }); @@ -363,8 +417,8 @@ export namespace CoreNavigationCommands { }); } - protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { - return ColumnSelection.columnSelectLeft(context.config, context.viewModel, prevColumnSelectData); + protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectLeft(viewModel.cursorConfig, viewModel, prevColumnSelectData); } }); @@ -382,8 +436,8 @@ export namespace CoreNavigationCommands { }); } - protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { - return ColumnSelection.columnSelectRight(context.config, context.viewModel, prevColumnSelectData); + protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectRight(viewModel.cursorConfig, viewModel, prevColumnSelectData); } }); @@ -396,8 +450,8 @@ export namespace CoreNavigationCommands { this._isPaged = opts.isPaged; } - protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { - return ColumnSelection.columnSelectUp(context.config, context.viewModel, prevColumnSelectData, this._isPaged); + protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectUp(viewModel.cursorConfig, viewModel, prevColumnSelectData, this._isPaged); } } @@ -434,8 +488,8 @@ export namespace CoreNavigationCommands { this._isPaged = opts.isPaged; } - protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { - return ColumnSelection.columnSelectDown(context.config, context.viewModel, prevColumnSelectData, this._isPaged); + protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult { + return ColumnSelection.columnSelectDown(viewModel.cursorConfig, viewModel, prevColumnSelectData, this._isPaged); } } @@ -472,23 +526,49 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { const parsed = CursorMove_.parse(args); if (!parsed) { // illegal arguments return; } - this._runCursorMove(cursors, args.source, parsed); + this._runCursorMove(viewModel, args.source, parsed); } - _runCursorMove(cursors: ICursors, source: string, args: CursorMove_.ParsedArguments): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + private _runCursorMove(viewModel: IViewModel, source: string | null | undefined, args: CursorMove_.ParsedArguments): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( source, CursorChangeReason.Explicit, - CursorMoveCommands.move(cursors.context, cursors.getAll(), args) + CursorMoveImpl._move(viewModel, viewModel.getCursorStates(), args) ); - cursors.reveal(source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(source, true); + } + + private static _move(viewModel: IViewModel, cursors: CursorState[], args: CursorMove_.ParsedArguments): PartialCursorState[] | null { + const inSelectionMode = args.select; + const value = args.value; + + switch (args.direction) { + case CursorMove_.Direction.Left: + case CursorMove_.Direction.Right: + case CursorMove_.Direction.Up: + case CursorMove_.Direction.Down: + case CursorMove_.Direction.WrappedLineStart: + case CursorMove_.Direction.WrappedLineFirstNonWhitespaceCharacter: + case CursorMove_.Direction.WrappedLineColumnCenter: + case CursorMove_.Direction.WrappedLineEnd: + case CursorMove_.Direction.WrappedLineLastNonWhitespaceCharacter: + return CursorMoveCommands.simpleMove(viewModel, cursors, args.direction, inSelectionMode, value, args.unit); + + case CursorMove_.Direction.ViewPortTop: + case CursorMove_.Direction.ViewPortBottom: + case CursorMove_.Direction.ViewPortCenter: + case CursorMove_.Direction.ViewPortIfOutside: + return CursorMoveCommands.viewportMove(viewModel, cursors, args.direction, inSelectionMode, value); + default: + return null; + } } } @@ -500,14 +580,14 @@ export namespace CoreNavigationCommands { class CursorMoveBasedCommand extends CoreEditorCommand { - private readonly _staticArgs: CursorMove_.ParsedArguments; + private readonly _staticArgs: CursorMove_.SimpleMoveArguments; - constructor(opts: ICommandOptions & { args: CursorMove_.ParsedArguments }) { + constructor(opts: ICommandOptions & { args: CursorMove_.SimpleMoveArguments }) { super(opts); this._staticArgs = opts.args; } - public runCoreEditorCommand(cursors: ICursors, dynamicArgs: any): void { + public runCoreEditorCommand(viewModel: IViewModel, dynamicArgs: any): void { let args = this._staticArgs; if (this._staticArgs.value === Constants.PAGE_SIZE_MARKER) { // -1 is a marker for page size @@ -515,10 +595,17 @@ export namespace CoreNavigationCommands { direction: this._staticArgs.direction, unit: this._staticArgs.unit, select: this._staticArgs.select, - value: cursors.context.config.pageSize + value: viewModel.cursorConfig.pageSize }; } - CursorMove._runCursorMove(cursors, dynamicArgs.source, args); + + viewModel.model.pushStackElement(); + viewModel.setCursorStates( + dynamicArgs.source, + CursorChangeReason.Explicit, + CursorMoveCommands.simpleMove(viewModel, viewModel.getCursorStates(), args.direction, args.select, args.value, args.unit) + ); + viewModel.revealPrimaryCursor(dynamicArgs.source, true); } } @@ -732,17 +819,15 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - const context = cursors.context; - + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { let newState: PartialCursorState; if (args.wholeLine) { - newState = CursorMoveCommands.line(context, cursors.getPrimaryCursor(), false, args.position, args.viewPosition); + newState = CursorMoveCommands.line(viewModel, viewModel.getPrimaryCursorState(), false, args.position, args.viewPosition); } else { - newState = CursorMoveCommands.moveTo(context, cursors.getPrimaryCursor(), false, args.position, args.viewPosition); + newState = CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), false, args.position, args.viewPosition); } - const states: PartialCursorState[] = cursors.getAll(); + const states: PartialCursorState[] = viewModel.getCursorStates(); // Check if we should remove a cursor (sort of like a toggle) if (states.length > 1) { @@ -763,8 +848,8 @@ export namespace CoreNavigationCommands { // => Remove the cursor states.splice(i, 1); - cursors.context.model.pushStackElement(); - cursors.setStates( + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, states @@ -776,8 +861,8 @@ export namespace CoreNavigationCommands { // => Add the new cursor states.push(newState); - cursors.context.model.pushStackElement(); - cursors.setStates( + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, states @@ -793,17 +878,15 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - const context = cursors.context; + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex(); - const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); - - const states = cursors.getAll(); + const states = viewModel.getCursorStates(); const newStates: PartialCursorState[] = states.slice(0); - newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(context, states[lastAddedCursorIndex], true, args.position, args.viewPosition); + newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(viewModel, states[lastAddedCursorIndex], true, args.position, args.viewPosition); - cursors.context.model.pushStackElement(); - cursors.setStates( + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, newStates @@ -820,14 +903,14 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.moveToBeginningOfLine(cursors.context, cursors.getAll(), this._inSelectionMode) + CursorMoveCommands.moveToBeginningOfLine(viewModel, viewModel.getCursorStates(), this._inSelectionMode) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } } @@ -855,40 +938,59 @@ export namespace CoreNavigationCommands { } })); - export const CursorLineStart: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { - constructor() { - super({ - id: 'cursorLineStart', - precondition: undefined, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: EditorContextKeys.textInputFocus, - primary: 0, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_A } - } - }); + class LineStartCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - this._exec(cursors.context, cursors.getAll()) + this._exec(viewModel.getCursorStates()) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } - private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { + private _exec(cursors: CursorState[]): PartialCursorState[] { const result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const lineNumber = cursor.modelState.position.lineNumber; - result[i] = CursorState.fromModelState(cursor.modelState.move(false, lineNumber, 1, 0)); + result[i] = CursorState.fromModelState(cursor.modelState.move(this._inSelectionMode, lineNumber, 1, 0)); } return result; } - }); + } + + export const CursorLineStart: CoreEditorCommand = registerEditorCommand(new LineStartCommand({ + inSelectionMode: false, + id: 'cursorLineStart', + precondition: undefined, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textInputFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_A } + } + })); + + export const CursorLineStartSelect: CoreEditorCommand = registerEditorCommand(new LineStartCommand({ + inSelectionMode: true, + id: 'cursorLineStartSelect', + precondition: undefined, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textInputFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_A } + } + })); class EndCommand extends CoreEditorCommand { @@ -899,14 +1001,14 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.moveToEndOfLine(cursors.context, cursors.getAll(), this._inSelectionMode) + CursorMoveCommands.moveToEndOfLine(viewModel, viewModel.getCursorStates(), this._inSelectionMode, args.sticky || false) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } } @@ -915,10 +1017,27 @@ export namespace CoreNavigationCommands { id: 'cursorEnd', precondition: undefined, kbOpts: { + args: { sticky: false }, weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, primary: KeyCode.End, mac: { primary: KeyCode.End, secondary: [KeyMod.CtrlCmd | KeyCode.RightArrow] } + }, + description: { + description: `Go to End`, + args: [{ + name: 'args', + schema: { + type: 'object', + properties: { + 'sticky': { + description: nls.localize('stickydesc', "Stick to the end even when going to longer lines"), + type: 'boolean', + default: false + } + } + } + }] } })); @@ -927,48 +1046,84 @@ export namespace CoreNavigationCommands { id: 'cursorEndSelect', precondition: undefined, kbOpts: { + args: { sticky: false }, weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Shift | KeyCode.End, mac: { primary: KeyMod.Shift | KeyCode.End, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow] } + }, + description: { + description: `Select to End`, + args: [{ + name: 'args', + schema: { + type: 'object', + properties: { + 'sticky': { + description: nls.localize('stickydesc', "Stick to the end even when going to longer lines"), + type: 'boolean', + default: false + } + } + } + }] } })); - export const CursorLineEnd: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { - constructor() { - super({ - id: 'cursorLineEnd', - precondition: undefined, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: EditorContextKeys.textInputFocus, - primary: 0, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_E } - } - }); + class LineEndCommand extends CoreEditorCommand { + + private readonly _inSelectionMode: boolean; + + constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + super(opts); + this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - this._exec(cursors.context, cursors.getAll()) + this._exec(viewModel, viewModel.getCursorStates()) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } - private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { + private _exec(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] { const result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const lineNumber = cursor.modelState.position.lineNumber; - const maxColumn = context.model.getLineMaxColumn(lineNumber); - result[i] = CursorState.fromModelState(cursor.modelState.move(false, lineNumber, maxColumn, 0)); + const maxColumn = viewModel.model.getLineMaxColumn(lineNumber); + result[i] = CursorState.fromModelState(cursor.modelState.move(this._inSelectionMode, lineNumber, maxColumn, 0)); } return result; } - }); + } + + export const CursorLineEnd: CoreEditorCommand = registerEditorCommand(new LineEndCommand({ + inSelectionMode: false, + id: 'cursorLineEnd', + precondition: undefined, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textInputFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_E } + } + })); + + export const CursorLineEndSelect: CoreEditorCommand = registerEditorCommand(new LineEndCommand({ + inSelectionMode: true, + id: 'cursorLineEndSelect', + precondition: undefined, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textInputFocus, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_E } + } + })); class TopCommand extends CoreEditorCommand { @@ -979,14 +1134,14 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.moveToBeginningOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode) + CursorMoveCommands.moveToBeginningOfBuffer(viewModel, viewModel.getCursorStates(), this._inSelectionMode) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } } @@ -1023,14 +1178,14 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.moveToEndOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode) + CursorMoveCommands.moveToEndOfBuffer(viewModel, viewModel.getCursorStates(), this._inSelectionMode) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } } @@ -1067,39 +1222,40 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { const parsed = EditorScroll_.parse(args); if (!parsed) { // illegal arguments return; } - this._runEditorScroll(cursors, args.source, parsed); + this._runEditorScroll(viewModel, args.source, parsed); } - _runEditorScroll(cursors: ICursors, source: string, args: EditorScroll_.ParsedArguments): void { + _runEditorScroll(viewModel: IViewModel, source: string | null | undefined, args: EditorScroll_.ParsedArguments): void { - const desiredScrollTop = this._computeDesiredScrollTop(cursors.context, args); + const desiredScrollTop = this._computeDesiredScrollTop(viewModel, args); if (args.revealCursor) { // must ensure cursor is in new visible range - const desiredVisibleViewRange = cursors.context.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop); - cursors.setStates( + const desiredVisibleViewRange = viewModel.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop); + viewModel.setCursorStates( source, CursorChangeReason.Explicit, [ - CursorMoveCommands.findPositionInViewportIfOutside(cursors.context, cursors.getPrimaryCursor(), desiredVisibleViewRange, args.select) + CursorMoveCommands.findPositionInViewportIfOutside(viewModel, viewModel.getPrimaryCursorState(), desiredVisibleViewRange, args.select) ] ); } - cursors.scrollTo(desiredScrollTop); + viewModel.setScrollTop(desiredScrollTop, ScrollType.Smooth); } - private _computeDesiredScrollTop(context: CursorContext, args: EditorScroll_.ParsedArguments): number { + private _computeDesiredScrollTop(viewModel: IViewModel, args: EditorScroll_.ParsedArguments): number { if (args.unit === EditorScroll_.Unit.Line) { // scrolling by model lines - const visibleModelRange = context.getCompletelyVisibleModelRange(); + const visibleViewRange = viewModel.getCompletelyVisibleViewRange(); + const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange); let desiredTopModelLineNumber: number; if (args.direction === EditorScroll_.Direction.Up) { @@ -1107,23 +1263,23 @@ export namespace CoreNavigationCommands { desiredTopModelLineNumber = Math.max(1, visibleModelRange.startLineNumber - args.value); } else { // must go x model lines down - desiredTopModelLineNumber = Math.min(context.model.getLineCount(), visibleModelRange.startLineNumber + args.value); + desiredTopModelLineNumber = Math.min(viewModel.model.getLineCount(), visibleModelRange.startLineNumber + args.value); } - const desiredTopViewPosition = context.convertModelPositionToViewPosition(new Position(desiredTopModelLineNumber, 1)); - return context.getVerticalOffsetForViewLine(desiredTopViewPosition.lineNumber); + const viewPosition = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(desiredTopModelLineNumber, 1)); + return viewModel.getVerticalOffsetForLineNumber(viewPosition.lineNumber); } let noOfLines: number; if (args.unit === EditorScroll_.Unit.Page) { - noOfLines = context.config.pageSize * args.value; + noOfLines = viewModel.cursorConfig.pageSize * args.value; } else if (args.unit === EditorScroll_.Unit.HalfPage) { - noOfLines = Math.round(context.config.pageSize / 2) * args.value; + noOfLines = Math.round(viewModel.cursorConfig.pageSize / 2) * args.value; } else { noOfLines = args.value; } const deltaLines = (args.direction === EditorScroll_.Direction.Up ? -1 : 1) * noOfLines; - return context.getCurrentScrollTop() + deltaLines * context.config.lineHeight; + return viewModel.getScrollTop() + deltaLines * viewModel.cursorConfig.lineHeight; } } @@ -1143,8 +1299,8 @@ export namespace CoreNavigationCommands { }); } - runCoreEditorCommand(cursors: ICursors, args: any): void { - EditorScroll._runEditorScroll(cursors, args.source, { + runCoreEditorCommand(viewModel: IViewModel, args: any): void { + EditorScroll._runEditorScroll(viewModel, args.source, { direction: EditorScroll_.Direction.Up, unit: EditorScroll_.Unit.WrappedLine, value: 1, @@ -1169,8 +1325,8 @@ export namespace CoreNavigationCommands { }); } - runCoreEditorCommand(cursors: ICursors, args: any): void { - EditorScroll._runEditorScroll(cursors, args.source, { + runCoreEditorCommand(viewModel: IViewModel, args: any): void { + EditorScroll._runEditorScroll(viewModel, args.source, { direction: EditorScroll_.Direction.Up, unit: EditorScroll_.Unit.Page, value: 1, @@ -1194,8 +1350,8 @@ export namespace CoreNavigationCommands { }); } - runCoreEditorCommand(cursors: ICursors, args: any): void { - EditorScroll._runEditorScroll(cursors, args.source, { + runCoreEditorCommand(viewModel: IViewModel, args: any): void { + EditorScroll._runEditorScroll(viewModel, args.source, { direction: EditorScroll_.Direction.Down, unit: EditorScroll_.Unit.WrappedLine, value: 1, @@ -1220,8 +1376,8 @@ export namespace CoreNavigationCommands { }); } - runCoreEditorCommand(cursors: ICursors, args: any): void { - EditorScroll._runEditorScroll(cursors, args.source, { + runCoreEditorCommand(viewModel: IViewModel, args: any): void { + EditorScroll._runEditorScroll(viewModel, args.source, { direction: EditorScroll_.Direction.Down, unit: EditorScroll_.Unit.Page, value: 1, @@ -1240,16 +1396,16 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ - CursorMoveCommands.word(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position) + CursorMoveCommands.word(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position) ] ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } } @@ -1273,18 +1429,16 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - const context = cursors.context; + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex(); - const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); - - const states = cursors.getAll(); + const states = viewModel.getCursorStates(); const newStates: PartialCursorState[] = states.slice(0); const lastAddedState = states[lastAddedCursorIndex]; - newStates[lastAddedCursorIndex] = CursorMoveCommands.word(context, lastAddedState, lastAddedState.modelState.hasSelection(), args.position); + newStates[lastAddedCursorIndex] = CursorMoveCommands.word(viewModel, lastAddedState, lastAddedState.modelState.hasSelection(), args.position); - context.model.pushStackElement(); - cursors.setStates( + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, newStates @@ -1300,16 +1454,16 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ - CursorMoveCommands.line(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition) + CursorMoveCommands.line(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition) ] ); - cursors.reveal(args.source, false, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, false); } } @@ -1333,15 +1487,15 @@ export namespace CoreNavigationCommands { this._inSelectionMode = opts.inSelectionMode; } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex(); - const states = cursors.getAll(); + const states = viewModel.getCursorStates(); const newStates: PartialCursorState[] = states.slice(0); - newStates[lastAddedCursorIndex] = CursorMoveCommands.line(cursors.context, states[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition); + newStates[lastAddedCursorIndex] = CursorMoveCommands.line(viewModel, states[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition); - cursors.context.model.pushStackElement(); - cursors.setStates( + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, newStates @@ -1374,14 +1528,14 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.expandLineSelection(cursors.context, cursors.getAll()) + CursorMoveCommands.expandLineSelection(viewModel, viewModel.getCursorStates()) ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } }); @@ -1400,16 +1554,16 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ - CursorMoveCommands.cancelSelection(cursors.context, cursors.getPrimaryCursor()) + CursorMoveCommands.cancelSelection(viewModel, viewModel.getPrimaryCursorState()) ] ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } }); @@ -1427,16 +1581,16 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ - cursors.getPrimaryCursor() + viewModel.getPrimaryCursorState() ] ); - cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth); + viewModel.revealPrimaryCursor(args.source, true); } }); @@ -1449,20 +1603,21 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { const revealLineArg = args; - let lineNumber = (revealLineArg.lineNumber || 0) + 1; + const lineNumberArg = revealLineArg.lineNumber || 0; + let lineNumber = typeof lineNumberArg === 'number' ? (lineNumberArg + 1) : (parseInt(lineNumberArg) + 1); if (lineNumber < 1) { lineNumber = 1; } - const lineCount = cursors.context.model.getLineCount(); + const lineCount = viewModel.model.getLineCount(); if (lineNumber > lineCount) { lineNumber = lineCount; } const range = new Range( lineNumber, 1, - lineNumber, cursors.context.model.getLineMaxColumn(lineNumber) + lineNumber, viewModel.model.getLineMaxColumn(lineNumber) ); let revealAt = VerticalRevealType.Simple; @@ -1482,31 +1637,43 @@ export namespace CoreNavigationCommands { } } - const viewRange = cursors.context.convertModelRangeToViewRange(range); + const viewRange = viewModel.coordinatesConverter.convertModelRangeToViewRange(range); - cursors.revealRange(args.source, false, viewRange, revealAt, ScrollType.Smooth); + viewModel.revealRange(args.source, false, viewRange, revealAt, ScrollType.Smooth); } }); - export const SelectAll: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + export const SelectAll = new class extends EditorOrNativeTextInputCommand { constructor() { - super({ - id: 'selectAll', - precondition: undefined - }); + super(SelectAllCommand); } + public runDOMCommand(): void { + if (isFirefox) { + (document.activeElement).focus(); + (document.activeElement).select(); + } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( - args.source, + document.execCommand('selectAll'); + } + public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { + const viewModel = editor._getViewModel(); + if (!viewModel) { + // the editor has no view => has no cursors + return; + } + this.runCoreEditorCommand(viewModel, args); + } + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( + 'keyboard', CursorChangeReason.Explicit, [ - CursorMoveCommands.selectAll(cursors.context, cursors.getPrimaryCursor()) + CursorMoveCommands.selectAll(viewModel, viewModel.getPrimaryCursorState()) ] ); } - }); + }(); export const SetSelection: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { constructor() { @@ -1516,9 +1683,9 @@ export namespace CoreNavigationCommands { }); } - public runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( + public runCoreEditorCommand(viewModel: IViewModel, args: any): void { + viewModel.model.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ @@ -1529,19 +1696,44 @@ export namespace CoreNavigationCommands { }); } +const columnSelectionCondition = ContextKeyExpr.and( + EditorContextKeys.textInputFocus, + EditorContextKeys.columnSelection +); +function registerColumnSelection(id: string, keybinding: number): void { + KeybindingsRegistry.registerKeybindingRule({ + id: id, + primary: keybinding, + when: columnSelectionCondition, + weight: CORE_WEIGHT + 1 + }); +} + +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectLeft.id, KeyMod.Shift | KeyCode.LeftArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectRight.id, KeyMod.Shift | KeyCode.RightArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectUp.id, KeyMod.Shift | KeyCode.UpArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageUp.id, KeyMod.Shift | KeyCode.PageUp); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectDown.id, KeyMod.Shift | KeyCode.DownArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageDown.id, KeyMod.Shift | KeyCode.PageDown); + +function registerCommand(command: T): T { + command.register(); + return command; +} + export namespace CoreEditingCommands { export abstract class CoreEditingCommand extends EditorCommand { public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { - const cursors = editor._getCursors(); - if (!cursors) { + const viewModel = editor._getViewModel(); + if (!viewModel) { // the editor has no view => has no cursors return; } - this.runCoreEditingCommand(editor, cursors, args || {}); + this.runCoreEditingCommand(editor, viewModel, args || {}); } - public abstract runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void; + public abstract runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void; } export const LineBreakInsert: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand { @@ -1558,9 +1750,9 @@ export namespace CoreEditingCommands { }); } - public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void { + public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void { editor.pushUndoStop(); - editor.executeCommands(this.id, TypeOperations.lineBreakInsert(cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection))); + editor.executeCommands(this.id, TypeOperations.lineBreakInsert(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection))); } }); @@ -1580,9 +1772,9 @@ export namespace CoreEditingCommands { }); } - public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void { + public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void { editor.pushUndoStop(); - editor.executeCommands(this.id, TypeOperations.outdent(cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection))); + editor.executeCommands(this.id, TypeOperations.outdent(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection))); editor.pushUndoStop(); } }); @@ -1603,9 +1795,9 @@ export namespace CoreEditingCommands { }); } - public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void { + public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void { editor.pushUndoStop(); - editor.executeCommands(this.id, TypeOperations.tab(cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection))); + editor.executeCommands(this.id, TypeOperations.tab(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection))); editor.pushUndoStop(); } }); @@ -1614,7 +1806,7 @@ export namespace CoreEditingCommands { constructor() { super({ id: 'deleteLeft', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1625,13 +1817,13 @@ export namespace CoreEditingCommands { }); } - public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void { - const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(cursors.getPrevEditOperationType(), cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection)); + public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void { + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)); if (shouldPushStackElementBefore) { editor.pushUndoStop(); } editor.executeCommands(this.id, commands); - cursors.setPrevEditOperationType(EditOperationType.DeletingLeft); + viewModel.setPrevEditOperationType(EditOperationType.DeletingLeft); } }); @@ -1639,7 +1831,7 @@ export namespace CoreEditingCommands { constructor() { super({ id: 'deleteRight', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1649,72 +1841,45 @@ export namespace CoreEditingCommands { }); } - public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void { - const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(cursors.getPrevEditOperationType(), cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection)); + public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void { + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)); if (shouldPushStackElementBefore) { editor.pushUndoStop(); } editor.executeCommands(this.id, commands); - cursors.setPrevEditOperationType(EditOperationType.DeletingRight); + viewModel.setPrevEditOperationType(EditOperationType.DeletingRight); } }); -} - -function registerCommand(command: Command) { - command.register(); -} - -/** - * A command that will: - * 1. invoke a command on the focused editor. - * 2. otherwise, invoke a browser built-in command on the `activeElement`. - * 3. otherwise, invoke a command on the workbench active editor. - */ -class EditorOrNativeTextInputCommand extends Command { - - private readonly _editorHandler: string | EditorCommand; - private readonly _inputHandler: string; - - constructor(opts: ICommandOptions & { editorHandler: string | EditorCommand; inputHandler: string; }) { - super(opts); - this._editorHandler = opts.editorHandler; - this._inputHandler = opts.inputHandler; - } - - public runCommand(accessor: ServicesAccessor, args: any): void { - - const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); - // Only if editor text focus (i.e. not if editor has widget focus). - if (focusedEditor && focusedEditor.hasTextFocus()) { - return this._runEditorHandler(accessor, focusedEditor, args); + export const Undo = new class extends EditorOrNativeTextInputCommand { + constructor() { + super(UndoCommand); } - - // Ignore this action when user is focused on an element that allows for entering text - const activeElement = document.activeElement; - if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { - document.execCommand(this._inputHandler); - return; + public runDOMCommand(): void { + document.execCommand('undo'); } - - // Redirecting to active editor - const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor(); - if (activeEditor) { - activeEditor.focus(); - return this._runEditorHandler(accessor, activeEditor, args); + public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void | Promise { + if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) { + return; + } + return editor.getModel().undo(); } - } + }(); - private _runEditorHandler(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { - const HANDLER = this._editorHandler; - if (typeof HANDLER === 'string') { - editor.trigger('keyboard', HANDLER, args); - } else { - args = args || {}; - args.source = 'keyboard'; - HANDLER.runEditorCommand(accessor, editor, args); + export const Redo = new class extends EditorOrNativeTextInputCommand { + constructor() { + super(RedoCommand); } - } + public runDOMCommand(): void { + document.execCommand('redo'); + } + public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void | Promise { + if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) { + return; + } + return editor.getModel().redo(); + } + }(); } /** @@ -1743,64 +1908,6 @@ class EditorHandlerCommand extends Command { } } -registerCommand(new EditorOrNativeTextInputCommand({ - editorHandler: CoreNavigationCommands.SelectAll, - inputHandler: 'selectAll', - id: 'editor.action.selectAll', - precondition: EditorContextKeys.textInputFocus, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: null, - primary: KeyMod.CtrlCmd | KeyCode.KEY_A - }, - menuOpts: { - menuId: MenuId.MenubarSelectionMenu, - group: '1_basic', - title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), - order: 1 - } -})); - -registerCommand(new EditorOrNativeTextInputCommand({ - editorHandler: Handler.Undo, - inputHandler: 'undo', - id: Handler.Undo, - precondition: EditorContextKeys.writable, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '1_do', - title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), - order: 1 - } -})); -registerCommand(new EditorHandlerCommand('default:' + Handler.Undo, Handler.Undo)); - -registerCommand(new EditorOrNativeTextInputCommand({ - editorHandler: Handler.Redo, - inputHandler: 'redo', - id: Handler.Redo, - precondition: EditorContextKeys.writable, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z } - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '1_do', - title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), - order: 2 - } -})); -registerCommand(new EditorHandlerCommand('default:' + Handler.Redo, Handler.Redo)); - function registerOverwritableCommand(handlerId: string, description?: ICommandHandlerDescription): void { registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId)); registerCommand(new EditorHandlerCommand(handlerId, handlerId, description)); diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 0e5f6b0d454..1e7b0929da4 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async'; +import { TimeoutTimer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; @@ -69,7 +68,6 @@ export class MouseHandler extends ViewEventHandler { protected viewController: ViewController; protected viewHelper: IPointerHandlerHelper; protected mouseTargetFactory: MouseTargetFactory; - private readonly _asyncFocus: RunOnceScheduler; protected readonly _mouseDownOperation: MouseDownOperation; private lastMouseLeaveTime: number; @@ -89,8 +87,6 @@ export class MouseHandler extends ViewEventHandler { (e) => this._getMouseColumn(e) )); - this._asyncFocus = this._register(new RunOnceScheduler(() => this.viewHelper.focusTextArea(), 0)); - this.lastMouseLeaveTime = -1; const mouseEvents = new EditorMouseEventFactory(this.viewHelper.viewDomNode); @@ -122,7 +118,7 @@ export class MouseHandler extends ViewEventHandler { e.stopPropagation(); } }; - this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, { capture: true, passive: false })); + this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { capture: true, passive: false })); this._context.addEventHandler(this); } @@ -137,9 +133,7 @@ export class MouseHandler extends ViewEventHandler { this._mouseDownOperation.onCursorStateChanged(e); return false; } - private _isFocused = false; public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { - this._isFocused = e.isFocused; return false; } public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { @@ -223,15 +217,8 @@ export class MouseHandler extends ViewEventHandler { } const focus = () => { - // In IE11, if the focus is in the browser's address bar and - // then you click in the editor, calling preventDefault() - // will not move focus properly (focus remains the address bar) - if (browser.isIE && !this._isFocused) { - this._asyncFocus.schedule(); - } else { - e.preventDefault(); - this.viewHelper.focusTextArea(); - } + e.preventDefault(); + this.viewHelper.focusTextArea(); }; if (shouldHandle && (targetIsContent || (targetIsLineNumbers && selectOnLineNumbers))) { @@ -352,6 +339,7 @@ class MouseDownOperation extends Disposable { if (!options.get(EditorOption.readOnly) && options.get(EditorOption.dragAndDrop) + && !options.get(EditorOption.columnSelection) && !this._mouseState.altKey // we don't support multiple mouse && e.detail < 2 // only single click on a selection can work && !this._isActive // the mouse is not down yet diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index f83ff9bfd94..f2be04e3a39 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -420,7 +420,7 @@ class HitTestRequest extends BareHitTestRequest { let mouseColumn = this.mouseColumn; if (position && position.column < this._ctx.model.getLineMaxColumn(position.lineNumber)) { // Most likely, the line contains foreign decorations... - mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getOptions().tabSize) + 1; + mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1; } return new MouseTarget(this.target, type, mouseColumn, position, range, detail); } @@ -666,7 +666,7 @@ export class MouseTargetFactory { if (domHitTestExecuted) { // Check if we are hitting a view-line (can happen in the case of inline decorations on empty lines) - // See https://github.com/Microsoft/vscode/issues/46942 + // See https://github.com/microsoft/vscode/issues/46942 if (ElementPath.isStrictChildOfViewLines(request.targetPath)) { const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); if (ctx.model.getLineLength(lineNumber) === 0) { @@ -753,7 +753,7 @@ export class MouseTargetFactory { if (request.mouseContentHorizontalOffset > lineWidth) { if (browser.isEdge && pos.column === 1) { - // See https://github.com/Microsoft/vscode/issues/10875 + // See https://github.com/microsoft/vscode/issues/10875 const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber)), undefined, detail); } @@ -925,6 +925,23 @@ export class MouseTargetFactory { } } + // For inline decorations, Gecko returns the `` of the line and the offset is the `` with the inline decoration + if (hitResult.offsetNode.nodeType === hitResult.offsetNode.ELEMENT_NODE) { + const parent1 = hitResult.offsetNode.parentNode; // expected to be the view line div + const parent1ClassName = parent1 && parent1.nodeType === parent1.ELEMENT_NODE ? (parent1).className : null; + + if (parent1ClassName === ViewLine.CLASS_NAME) { + const tokenSpan = hitResult.offsetNode.childNodes[Math.min(hitResult.offset, hitResult.offsetNode.childNodes.length - 1)]; + if (tokenSpan) { + const p = ctx.getPositionFromDOMInfo(tokenSpan, 0); + return { + position: p, + hitTarget: null + }; + } + } + } + return { position: null, hitTarget: hitResult.offsetNode diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 46894f1be15..b86fd84fd67 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -31,84 +31,6 @@ function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent | null, curr return r; } -/** - * Basically IE10 and IE11 - */ -class MsPointerHandler extends MouseHandler implements IDisposable { - - private _lastPointerType: string; - private _installGestureHandlerTimeout: number; - - constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { - super(context, viewController, viewHelper); - - this.viewHelper.linesContentDomNode.style.msTouchAction = 'none'; - this.viewHelper.linesContentDomNode.style.msContentZooming = 'none'; - - // TODO@Alex -> this expects that the view is added in 100 ms, might not be the case - // This handler should be added when the dom node is in the dom tree - this._installGestureHandlerTimeout = window.setTimeout(() => { - this._installGestureHandlerTimeout = -1; - if ((window).MSGesture) { - const touchGesture = new MSGesture(); - const penGesture = new MSGesture(); - touchGesture.target = this.viewHelper.linesContentDomNode; - penGesture.target = this.viewHelper.linesContentDomNode; - this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => { - // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions - const pointerType = e.pointerType; - if (pointerType === ((e).MSPOINTER_TYPE_MOUSE || 'mouse')) { - this._lastPointerType = 'mouse'; - return; - } else if (pointerType === ((e).MSPOINTER_TYPE_TOUCH || 'touch')) { - this._lastPointerType = 'touch'; - touchGesture.addPointer(e.pointerId); - } else { - this._lastPointerType = 'pen'; - penGesture.addPointer(e.pointerId); - } - }); - this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); - this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true)); - } - }, 100); - this._lastPointerType = 'mouse'; - } - - public _onMouseDown(e: EditorMouseEvent): void { - if (this._lastPointerType === 'mouse') { - super._onMouseDown(e); - } - } - - private _onCaptureGestureTap(rawEvent: MSGestureEvent): void { - const e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); - const t = this._createMouseTarget(e, false); - if (t.position) { - this.viewController.moveTo(t.position); - } - // IE does not want to focus when coming in from the browser's address bar - if ((e.browserEvent).fromElement) { - e.preventDefault(); - this.viewHelper.focusTextArea(); - } else { - // TODO@Alex -> cancel this is focus is lost - setTimeout(() => { - this.viewHelper.focusTextArea(); - }); - } - } - - private _onGestureChange(e: IThrottledGestureEvent): void { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); - } - - public dispose(): void { - window.clearTimeout(this._installGestureHandlerTimeout); - super.dispose(); - } -} - /** * Basically Edge but should be modified to handle any pointerEnabled, even without support of MSGesture */ @@ -128,7 +50,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { this._installGestureHandlerTimeout = -1; // TODO@Alex: replace the usage of MSGesture here with something that works across all browsers - if ((window).MSGesture) { + if (window.MSGesture) { const touchGesture = new MSGesture(); const penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; @@ -178,7 +100,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { } private _onGestureChange(e: IThrottledGestureEvent): void { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + this._context.model.deltaScrollNow(-e.translationX, -e.translationY); } public dispose(): void { @@ -255,12 +177,12 @@ export class PointerEventHandler extends MouseHandler { private onChange(e: GestureEvent): void { if (this._lastPointerType === 'touch') { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + this._context.model.deltaScrollNow(-e.translationX, -e.translationY); } } public _onMouseDown(e: EditorMouseEvent): void { - if (e.target && this.viewHelper.linesContentDomNode.contains(e.target) && this._lastPointerType === 'touch') { + if ((e.browserEvent as any).pointerType === 'touch') { return; } @@ -293,7 +215,7 @@ class TouchHandler extends MouseHandler { } private onChange(e: GestureEvent): void { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + this._context.model.deltaScrollNow(-e.translationX, -e.translationY); } } @@ -302,13 +224,11 @@ export class PointerHandler extends Disposable { constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { super(); - if (window.navigator.msPointerEnabled) { - this.handler = this._register(new MsPointerHandler(context, viewController, viewHelper)); - } else if ((platform.isIOS && BrowserFeatures.pointerEvents)) { + if ((platform.isIOS && BrowserFeatures.pointerEvents)) { this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper)); - } else if ((window).TouchEvent) { + } else if (window.TouchEvent) { this.handler = this._register(new TouchHandler(context, viewController, viewHelper)); - } else if (window.navigator.pointerEnabled || (window).PointerEvent) { + } else if (window.navigator.pointerEnabled || window.PointerEvent) { this.handler = this._register(new StandardPointerHandler(context, viewController, viewHelper)); } else { this.handler = this._register(new MouseHandler(context, viewController, viewHelper)); diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index ea4e4f8930b..07eadee8415 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -30,6 +30,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; export interface ITextAreaHandlerHelper { visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalPosition | null; @@ -53,7 +54,7 @@ class VisibleTextAreaData { } } -const canUseZeroSizeTextarea = (browser.isEdgeOrIE || browser.isFirefox); +const canUseZeroSizeTextarea = (browser.isEdge || browser.isFirefox); export class TextAreaHandler extends ViewPart { @@ -117,14 +118,16 @@ export class TextAreaHandler extends ViewPart { // Text Area (The focus will always be in the textarea when the cursor is blinking) this.textArea = createFastDomNode(document.createElement('textarea')); PartFingerprints.write(this.textArea, PartFingerprint.TextArea); - this.textArea.setClassName('inputarea'); + this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); this.textArea.setAttribute('wrap', 'off'); this.textArea.setAttribute('autocorrect', 'off'); this.textArea.setAttribute('autocapitalize', 'off'); this.textArea.setAttribute('autocomplete', 'off'); this.textArea.setAttribute('spellcheck', 'false'); this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); + this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex))); this.textArea.setAttribute('role', 'textbox'); + this.textArea.setAttribute('aria-roledescription', nls.localize('editor', "editor")); this.textArea.setAttribute('aria-multiline', 'true'); this.textArea.setAttribute('aria-haspopup', 'false'); this.textArea.setAttribute('aria-autocomplete', 'both'); @@ -176,14 +179,7 @@ export class TextAreaHandler extends ViewPart { mode }; }, - getScreenReaderContent: (currentState: TextAreaState): TextAreaState => { - - if (browser.isIPad) { - // Do not place anything in the textarea for the iPad - return TextAreaState.EMPTY; - } - if (this._accessibilitySupport === AccessibilitySupport.Disabled) { // We know for a fact that a screen reader is not attached // On OSX, we write the character before the cursor to allow for "long-press" composition @@ -233,36 +229,36 @@ export class TextAreaHandler extends ViewPart { multicursorText = (typeof e.metadata.multicursorText !== 'undefined' ? e.metadata.multicursorText : null); mode = e.metadata.mode; } - this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText, mode); + this._viewController.paste(e.text, pasteOnNewLine, multicursorText, mode); })); this._register(this._textAreaInput.onCut(() => { - this._viewController.cut('keyboard'); + this._viewController.cut(); })); this._register(this._textAreaInput.onType((e: ITypeData) => { if (e.replaceCharCnt) { - this._viewController.replacePreviousChar('keyboard', e.text, e.replaceCharCnt); + this._viewController.replacePreviousChar(e.text, e.replaceCharCnt); } else { - this._viewController.type('keyboard', e.text); + this._viewController.type(e.text); } })); this._register(this._textAreaInput.onSelectionChangeRequest((modelSelection: Selection) => { - this._viewController.setSelection('keyboard', modelSelection); + this._viewController.setSelection(modelSelection); })); - this._register(this._textAreaInput.onCompositionStart(() => { + this._register(this._textAreaInput.onCompositionStart((e) => { const lineNumber = this._selections[0].startLineNumber; - const column = this._selections[0].startColumn; + const column = this._selections[0].startColumn - (e.moveOneCharacterLeft ? 1 : 0); - this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( + this._context.model.revealRange( 'keyboard', + true, new Range(lineNumber, column, lineNumber, column), viewEvents.VerticalRevealType.Simple, - true, ScrollType.Immediate - )); + ); // Find range pixel position const visibleRange = this._viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column); @@ -277,13 +273,13 @@ export class TextAreaHandler extends ViewPart { } // Show the textarea - this.textArea.setClassName('inputarea ime-input'); + this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME} ime-input`); - this._viewController.compositionStart('keyboard'); + this._viewController.compositionStart(); })); this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => { - if (browser.isEdgeOrIE) { + if (browser.isEdge) { // Due to isEdgeOrIE (where the textarea was not cleared initially) // we cannot assume the text consists only of the composited text this._visibleTextArea = this._visibleTextArea!.setWidth(0); @@ -299,16 +295,16 @@ export class TextAreaHandler extends ViewPart { this._visibleTextArea = null; this._render(); - this.textArea.setClassName('inputarea'); - this._viewController.compositionEnd('keyboard'); + this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); + this._viewController.compositionEnd(); })); this._register(this._textAreaInput.onFocus(() => { - this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(true)); + this._context.model.setHasFocus(true); })); this._register(this._textAreaInput.onBlur(() => { - this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(false)); + this._context.model.setHasFocus(false); })); } @@ -348,7 +344,7 @@ export class TextAreaHandler extends ViewPart { private _getAriaLabel(options: IComputedEditorOptions): string { const accessibilitySupport = options.get(EditorOption.accessibilitySupport); if (accessibilitySupport === AccessibilitySupport.Disabled) { - return nls.localize('accessibilityOffAriaLabel', "The editor is not accessible at this time. Press Alt+F1 for options."); + return nls.localize('accessibilityOffAriaLabel', "The editor is not accessible at this time. Press {0} for options.", platform.isLinux ? 'Shift+Alt+F1' : 'Alt+F1'); } return options.get(EditorOption.ariaLabel); } @@ -357,8 +353,9 @@ export class TextAreaHandler extends ViewPart { this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); const accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); if (this._accessibilitySupport === AccessibilitySupport.Enabled && accessibilityPageSize === EditorOptions.accessibilityPageSize.defaultValue) { - // If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 1000 for a better experience - this._accessibilityPageSize = 1000; + // If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 100 for a better experience + // If we put more than 100 lines the nvda can not handle this https://github.com/microsoft/vscode/issues/89717 + this._accessibilityPageSize = 100; } else { this._accessibilityPageSize = accessibilityPageSize; } @@ -379,6 +376,7 @@ export class TextAreaHandler extends ViewPart { this._emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard); this._copyWithSyntaxHighlighting = options.get(EditorOption.copyWithSyntaxHighlighting); this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); + this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex))); if (platform.isWeb && e.hasChanged(EditorOption.readOnly)) { if (options.get(EditorOption.readOnly)) { @@ -455,6 +453,9 @@ export class TextAreaHandler extends ViewPart { this.textArea.setAttribute('aria-autocomplete', 'both'); this.textArea.removeAttribute('aria-activedescendant'); } + if (options.role) { + this.textArea.setAttribute('role', options.role); + } } // --- end view API diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 8e80e794f8b..e2d42baf4c3 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -57,19 +57,6 @@ export interface ITextAreaInputHost { deduceModelPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position; } -const enum TextAreaInputEventType { - none, - compositionstart, - compositionupdate, - compositionend, - input, - cut, - copy, - paste, - focus, - blur -} - interface CompositionEvent extends UIEvent { readonly data: string; readonly locale: string; @@ -85,7 +72,7 @@ interface InMemoryClipboardMetadata { * Every time we read from the cipboard, if the text matches our last written text, * we can fetch the previous metadata. */ -class InMemoryClipboardMetadataManager { +export class InMemoryClipboardMetadataManager { public static readonly INSTANCE = new InMemoryClipboardMetadataManager(); private _lastState: InMemoryClipboardMetadata | null; @@ -108,6 +95,10 @@ class InMemoryClipboardMetadataManager { } } +export interface ICompositionStartEvent { + moveOneCharacterLeft: boolean; +} + /** * Writes screen reader content to the textarea and is able to analyze its input events to generate: * - onCut @@ -139,8 +130,8 @@ export class TextAreaInput extends Disposable { private _onType = this._register(new Emitter()); public readonly onType: Event = this._onType.event; - private _onCompositionStart = this._register(new Emitter()); - public readonly onCompositionStart: Event = this._onCompositionStart.event; + private _onCompositionStart = this._register(new Emitter()); + public readonly onCompositionStart: Event = this._onCompositionStart.event; private _onCompositionUpdate = this._register(new Emitter()); public readonly onCompositionUpdate: Event = this._onCompositionUpdate.event; @@ -155,7 +146,6 @@ export class TextAreaInput extends Disposable { private readonly _host: ITextAreaInputHost; private readonly _textArea: TextAreaWrapper; - private _lastTextAreaEvent: TextAreaInputEventType; private readonly _asyncTriggerCut: RunOnceScheduler; private _textAreaState: TextAreaState; @@ -169,7 +159,6 @@ export class TextAreaInput extends Disposable { super(); this._host = host; this._textArea = this._register(new TextAreaWrapper(textArea)); - this._lastTextAreaEvent = TextAreaInputEventType.none; this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0)); this._textAreaState = TextAreaState.EMPTY; @@ -180,9 +169,11 @@ export class TextAreaInput extends Disposable { this._isDoingComposition = false; this._nextCommand = ReadFromTextArea.Type; + let lastKeyDown: IKeyboardEvent | null = null; + this._register(dom.addStandardDisposableListener(textArea.domNode, 'keydown', (e: IKeyboardEvent) => { - if (this._isDoingComposition && - (e.keyCode === KeyCode.KEY_IN_COMPOSITION || e.keyCode === KeyCode.Backspace)) { + if (e.keyCode === KeyCode.KEY_IN_COMPOSITION + || (this._isDoingComposition && e.keyCode === KeyCode.Backspace)) { // Stop propagation for keyDown events if the IME is processing key input e.stopPropagation(); } @@ -192,6 +183,8 @@ export class TextAreaInput extends Disposable { // See https://msdn.microsoft.com/en-us/library/ie/ms536939(v=vs.85).aspx e.preventDefault(); } + + lastKeyDown = e; this._onKeyDown.fire(e); })); @@ -200,28 +193,49 @@ export class TextAreaInput extends Disposable { })); this._register(dom.addDisposableListener(textArea.domNode, 'compositionstart', (e: CompositionEvent) => { - this._lastTextAreaEvent = TextAreaInputEventType.compositionstart; - if (this._isDoingComposition) { return; } this._isDoingComposition = true; - // In IE we cannot set .value when handling 'compositionstart' because the entire composition will get canceled. - if (!browser.isEdgeOrIE) { + let moveOneCharacterLeft = false; + if ( + platform.isMacintosh + && lastKeyDown + && lastKeyDown.equals(KeyCode.KEY_IN_COMPOSITION) + && this._textAreaState.selectionStart === this._textAreaState.selectionEnd + && this._textAreaState.selectionStart > 0 + && this._textAreaState.value.substr(this._textAreaState.selectionStart - 1, 1) === e.data + ) { + // Handling long press case on macOS + arrow key => pretend the character was selected + if (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft') { + moveOneCharacterLeft = true; + } + } + + if (moveOneCharacterLeft) { + this._textAreaState = new TextAreaState( + this._textAreaState.value, + this._textAreaState.selectionStart - 1, + this._textAreaState.selectionEnd, + this._textAreaState.selectionStartPosition ? new Position(this._textAreaState.selectionStartPosition.lineNumber, this._textAreaState.selectionStartPosition.column - 1) : null, + this._textAreaState.selectionEndPosition + ); + } else if (!browser.isEdge) { + // In IE we cannot set .value when handling 'compositionstart' because the entire composition will get canceled. this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY); } - this._onCompositionStart.fire(); + this._onCompositionStart.fire({ moveOneCharacterLeft }); })); /** * Deduce the typed input from a text area's value and the last observed state. */ - const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): [TextAreaState, ITypeData] => { + const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean): [TextAreaState, ITypeData] => { const oldState = this._textAreaState; const newState = TextAreaState.readFromTextArea(this._textArea); - return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput, couldBeTypingAtOffset0)]; + return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)]; }; /** @@ -238,19 +252,11 @@ export class TextAreaInput extends Disposable { }; const compositionDataInValid = (locale: string): boolean => { - // https://github.com/Microsoft/monaco-editor/issues/339 + // https://github.com/microsoft/monaco-editor/issues/339 // Multi-part Japanese compositions reset cursor in Edge/IE, Chinese and Korean IME don't have this issue. // The reason that we can't use this path for all CJK IME is IE and Edge behave differently when handling Korean IME, // which breaks this path of code. - if (browser.isEdgeOrIE && locale === 'ja') { - return true; - } - - // https://github.com/Microsoft/monaco-editor/issues/545 - // On IE11, we can't trust composition data when typing Chinese as IE11 doesn't emit correct - // events when users type numbers in IME. - // Chinese: zh-Hans-CN, zh-Hans-SG, zh-Hant-TW, zh-Hant-HK - if (browser.isIE && locale.indexOf('zh-Han') === 0) { + if (browser.isEdge && locale === 'ja') { return true; } @@ -258,43 +264,40 @@ export class TextAreaInput extends Disposable { }; this._register(dom.addDisposableListener(textArea.domNode, 'compositionupdate', (e: CompositionEvent) => { - this._lastTextAreaEvent = TextAreaInputEventType.compositionupdate; - if (compositionDataInValid(e.locale)) { - const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false); + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false); this._textAreaState = newState; this._onType.fire(typeInput); this._onCompositionUpdate.fire(e); return; } - const [newState, typeInput] = deduceComposition(e.data); + const [newState, typeInput] = deduceComposition(e.data || ''); this._textAreaState = newState; this._onType.fire(typeInput); this._onCompositionUpdate.fire(e); })); this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => { - this._lastTextAreaEvent = TextAreaInputEventType.compositionend; // https://github.com/microsoft/monaco-editor/issues/1663 // On iOS 13.2, Chinese system IME randomly trigger an additional compositionend event with empty data if (!this._isDoingComposition) { return; } if (compositionDataInValid(e.locale)) { - // https://github.com/Microsoft/monaco-editor/issues/339 - const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false); + // https://github.com/microsoft/monaco-editor/issues/339 + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false); this._textAreaState = newState; this._onType.fire(typeInput); } else { - const [newState, typeInput] = deduceComposition(e.data); + const [newState, typeInput] = deduceComposition(e.data || ''); this._textAreaState = newState; this._onType.fire(typeInput); } // Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome (the textarea is not updated correctly when composition ends) // we cannot assume the text at the end consists only of the composited text - if (browser.isEdgeOrIE || browser.isChrome) { + if (browser.isEdge || browser.isChrome) { this._textAreaState = TextAreaState.readFromTextArea(this._textArea); } @@ -307,10 +310,6 @@ export class TextAreaInput extends Disposable { })); this._register(dom.addDisposableListener(textArea.domNode, 'input', () => { - // We want to find out if this is the first `input` after a `focus`. - const previousEventWasFocus = (this._lastTextAreaEvent === TextAreaInputEventType.focus); - this._lastTextAreaEvent = TextAreaInputEventType.input; - // Pretend here we touched the text area, as the `input` event will most likely // result in a `selectionchange` event which we want to ignore this._textArea.setIgnoreSelectionChangeTime('received input event'); @@ -319,7 +318,7 @@ export class TextAreaInput extends Disposable { return; } - const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh, /*couldBeTypingAtOffset0*/previousEventWasFocus && platform.isMacintosh); + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh); if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) { // Ignore invalid input but keep it around for next time return; @@ -331,7 +330,7 @@ export class TextAreaInput extends Disposable { this._onType.fire(typeInput); } } else { - if (typeInput.text !== '') { + if (typeInput.text !== '' || typeInput.replaceCharCnt !== 0) { this._firePaste(typeInput.text, null); } this._nextCommand = ReadFromTextArea.Type; @@ -341,8 +340,6 @@ export class TextAreaInput extends Disposable { // --- Clipboard operations this._register(dom.addDisposableListener(textArea.domNode, 'cut', (e: ClipboardEvent) => { - this._lastTextAreaEvent = TextAreaInputEventType.cut; - // Pretend here we touched the text area, as the `cut` event will most likely // result in a `selectionchange` event which we want to ignore this._textArea.setIgnoreSelectionChangeTime('received cut event'); @@ -352,14 +349,10 @@ export class TextAreaInput extends Disposable { })); this._register(dom.addDisposableListener(textArea.domNode, 'copy', (e: ClipboardEvent) => { - this._lastTextAreaEvent = TextAreaInputEventType.copy; - this._ensureClipboardGetsEditorSelection(e); })); this._register(dom.addDisposableListener(textArea.domNode, 'paste', (e: ClipboardEvent) => { - this._lastTextAreaEvent = TextAreaInputEventType.paste; - // Pretend here we touched the text area, as the `paste` event will most likely // result in a `selectionchange` event which we want to ignore this._textArea.setIgnoreSelectionChangeTime('received paste event'); @@ -379,17 +372,15 @@ export class TextAreaInput extends Disposable { })); this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => { - this._lastTextAreaEvent = TextAreaInputEventType.focus; this._setHasFocus(true); })); this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => { - this._lastTextAreaEvent = TextAreaInputEventType.blur; this._setHasFocus(false); })); } private _installSelectionChangeListener(): IDisposable { - // See https://github.com/Microsoft/vscode/issues/27216 + // See https://github.com/microsoft/vscode/issues/27216 and https://github.com/microsoft/vscode/issues/98256 // When using a Braille display, it is possible for users to reposition the // system caret. This is reflected in Chrome as a `selectionchange` event. // @@ -415,8 +406,8 @@ export class TextAreaInput extends Disposable { if (this._isDoingComposition) { return; } - if (!browser.isChrome || !platform.isWindows) { - // Support only for Chrome on Windows until testing happens on other browsers + OS configurations + if (!browser.isChrome) { + // Support only for Chrome until testing happens on other browsers return; } @@ -483,6 +474,9 @@ export class TextAreaInput extends Disposable { // Setting this._hasFocus and writing the screen reader content // will result in a focus() and setSelectionRange() in the textarea this._setHasFocus(true); + + // If the editor is off DOM, focus cannot be really set, so let's double check that we have managed to set the focus + this.refreshFocusState(); } public isFocused(): boolean { @@ -490,8 +484,11 @@ export class TextAreaInput extends Disposable { } public refreshFocusState(): void { - if (document.body.contains(this.textArea.domNode) && document.activeElement === this.textArea.domNode) { - this._setHasFocus(true); + const shadowRoot = dom.getShadowRoot(this.textArea.domNode); + if (shadowRoot) { + this._setHasFocus(shadowRoot.activeElement === this.textArea.domNode); + } else if (dom.isInDOM(this.textArea.domNode)) { + this._setHasFocus(document.activeElement === this.textArea.domNode); } else { this._setHasFocus(false); } @@ -619,7 +616,8 @@ class ClipboardEventUtils { if ((window).clipboardData) { e.preventDefault(); - return (window).clipboardData.getData('Text'); + const text: string = (window).clipboardData.getData('Text'); + return [text, null]; } throw new Error('ClipboardEventUtils.getTextData: Cannot use text data!'); @@ -696,13 +694,21 @@ class TextAreaWrapper extends Disposable implements ITextAreaWrapper { public setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void { const textArea = this._actual.domNode; - const currentIsFocused = (document.activeElement === textArea); + let activeElement: Element | null = null; + const shadowRoot = dom.getShadowRoot(textArea); + if (shadowRoot) { + activeElement = shadowRoot.activeElement; + } else { + activeElement = document.activeElement; + } + + const currentIsFocused = (activeElement === textArea); const currentSelectionStart = textArea.selectionStart; const currentSelectionEnd = textArea.selectionEnd; if (currentIsFocused && currentSelectionStart === selectionStart && currentSelectionEnd === selectionEnd) { // No change - // Firefox iframe bug https://github.com/Microsoft/monaco-editor/issues/643#issuecomment-367871377 + // Firefox iframe bug https://github.com/microsoft/monaco-editor/issues/643#issuecomment-367871377 if (browser.isFirefox && window.parent !== window) { textArea.focus(); } diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts index cca8755dc48..65c815e38de 100644 --- a/src/vs/editor/browser/controller/textAreaState.ts +++ b/src/vs/editor/browser/controller/textAreaState.ts @@ -96,7 +96,7 @@ export class TextAreaState { return new TextAreaState(text, 0, text.length, null, null); } - public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): ITypeData { + public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean): ITypeData { if (!previousState) { // This is the EMPTY state return { @@ -116,18 +116,6 @@ export class TextAreaState { let currentSelectionStart = currentState.selectionStart; let currentSelectionEnd = currentState.selectionEnd; - if (couldBeTypingAtOffset0 && previousValue.length > 0 && previousSelectionStart === previousSelectionEnd && currentSelectionStart === currentSelectionEnd) { - // See https://github.com/Microsoft/vscode/issues/42251 - // where typing always happens at offset 0 in the textarea - // when using a custom title area in OSX and moving the window - if (!strings.startsWith(currentValue, previousValue) && strings.endsWith(currentValue, previousValue)) { - // Looks like something was typed at offset 0 - // ==> pretend we placed the cursor at offset 0 to begin with... - previousSelectionStart = 0; - previousSelectionEnd = 0; - } - } - // Strip the previous suffix from the value (without interfering with the current selection) const previousSuffix = previousValue.substring(previousSelectionEnd); const currentSuffix = currentValue.substring(currentSelectionEnd); @@ -157,13 +145,13 @@ export class TextAreaState { if (currentSelectionStart === currentValue.length) { // emoji potentially inserted "somewhere" after the previous selection => it should appear at the end of `currentValue` - if (strings.startsWith(currentValue, previousValue)) { + if (currentValue.startsWith(previousValue)) { // only if all of the old text is accounted for potentialEmojiInput = currentValue.substring(previousValue.length); } } else { // emoji potentially inserted "somewhere" before the previous selection => it should appear at the start of `currentValue` - if (strings.endsWith(currentValue, previousValue)) { + if (currentValue.endsWith(previousValue)) { // only if all of the old text is accounted for potentialEmojiInput = currentValue.substring(0, currentValue.length - previousValue.length); } diff --git a/src/vs/editor/browser/core/editorState.ts b/src/vs/editor/browser/core/editorState.ts index 9d469299bbb..5d4c1ffb0e5 100644 --- a/src/vs/editor/browser/core/editorState.ts +++ b/src/vs/editor/browser/core/editorState.ts @@ -6,7 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; @@ -87,19 +87,28 @@ export class EditorState { /** * A cancellation token source that cancels when the editor changes as expressed * by the provided flags + * @param range If provided, changes in position and selection within this range will not trigger cancellation */ export class EditorStateCancellationTokenSource extends EditorKeybindingCancellationTokenSource implements IDisposable { private readonly _listener = new DisposableStore(); - constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, parent?: CancellationToken) { + constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, range?: IRange, parent?: CancellationToken) { super(editor, parent); if (flags & CodeEditorStateFlag.Position) { - this._listener.add(editor.onDidChangeCursorPosition(_ => this.cancel())); + this._listener.add(editor.onDidChangeCursorPosition(e => { + if (!range || !Range.containsPosition(range, e.position)) { + this.cancel(); + } + })); } if (flags & CodeEditorStateFlag.Selection) { - this._listener.add(editor.onDidChangeCursorSelection(_ => this.cancel())); + this._listener.add(editor.onDidChangeCursorSelection(e => { + if (!range || !Range.containsRange(range, e.selection)) { + this.cancel(); + } + })); } if (flags & CodeEditorStateFlag.Scroll) { this._listener.add(editor.onDidScrollChange(_ => this.cancel())); @@ -147,12 +156,13 @@ export class StableEditorScrollState { visiblePositionScrollDelta = editor.getScrollTop() - visiblePositionScrollTop; } } - return new StableEditorScrollState(visiblePosition, visiblePositionScrollDelta); + return new StableEditorScrollState(visiblePosition, visiblePositionScrollDelta, editor.getPosition()); } constructor( private readonly _visiblePosition: Position | null, - private readonly _visiblePositionScrollDelta: number + private readonly _visiblePositionScrollDelta: number, + private readonly _cursorPosition: Position | null ) { } @@ -162,4 +172,15 @@ export class StableEditorScrollState { editor.setScrollTop(visiblePositionScrollTop + this._visiblePositionScrollDelta); } } + + public restoreRelativeVerticalPositionOfCursor(editor: ICodeEditor): void { + const currentCursorPosition = editor.getPosition(); + + if (!this._cursorPosition || !currentCursorPosition) { + return; + } + + const offset = editor.getTopForLineNumber(currentCursorPosition.lineNumber) - editor.getTopForLineNumber(this._cursorPosition.lineNumber); + editor.setScrollTop(editor.getScrollTop() + offset); + } } diff --git a/src/vs/editor/browser/core/keybindingCancellation.ts b/src/vs/editor/browser/core/keybindingCancellation.ts index df5795ffe28..1f260f9b6cd 100644 --- a/src/vs/editor/browser/core/keybindingCancellation.ts +++ b/src/vs/editor/browser/core/keybindingCancellation.ts @@ -17,7 +17,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; const IEditorCancellationTokens = createDecorator('IEditorCancelService'); interface IEditorCancellationTokens { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; add(editor: ICodeEditor, cts: CancellationTokenSource): () => void; cancel(editor: ICodeEditor): void; } @@ -26,7 +26,7 @@ const ctxCancellableOperation = new RawContextKey('cancellableOperation', false) registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _tokens = new WeakMap, tokens: LinkedList }>(); diff --git a/src/vs/editor/browser/core/markdownRenderer.ts b/src/vs/editor/browser/core/markdownRenderer.ts new file mode 100644 index 00000000000..375518d2ce0 --- /dev/null +++ b/src/vs/editor/browser/core/markdownRenderer.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { renderMarkdown, MarkdownRenderOptions, MarkedOptions } from 'vs/base/browser/markdownRenderer'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Emitter } from 'vs/base/common/event'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/modes'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { URI } from 'vs/base/common/uri'; + +export interface IMarkdownRenderResult extends IDisposable { + element: HTMLElement; +} + +export interface IMarkdownRendererOptions { + editor?: ICodeEditor; + baseUrl?: URI; + codeBlockFontFamily?: string; +} + +/** + * Markdown renderer that can render codeblocks with the editor mechanics. This + * renderer should always be preferred. + */ +export class MarkdownRenderer { + + private static _ttpTokenizer = window.trustedTypes?.createPolicy('tokenizeToString', { + createHTML(value: string, tokenizer: ITokenizationSupport | undefined) { + return tokenizeToString(value, tokenizer); + } + }); + + private readonly _onDidRenderCodeBlock = new Emitter(); + readonly onDidRenderCodeBlock = this._onDidRenderCodeBlock.event; + + constructor( + private readonly _options: IMarkdownRendererOptions, + @IModeService private readonly _modeService: IModeService, + @IOpenerService private readonly _openerService: IOpenerService, + ) { } + + dispose(): void { + this._onDidRenderCodeBlock.dispose(); + } + + render(markdown: IMarkdownString | undefined, options?: MarkdownRenderOptions, markedOptions?: MarkedOptions): IMarkdownRenderResult { + const disposeables = new DisposableStore(); + + let element: HTMLElement; + if (!markdown) { + element = document.createElement('span'); + } else { + element = renderMarkdown(markdown, { ...this._getRenderOptions(disposeables), ...options }, markedOptions); + } + + return { + element, + dispose: () => disposeables.dispose() + }; + } + + protected _getRenderOptions(disposeables: DisposableStore): MarkdownRenderOptions { + return { + baseUrl: this._options.baseUrl, + codeBlockRenderer: async (languageAlias, value) => { + // In markdown, + // it is possible that we stumble upon language aliases (e.g.js instead of javascript) + // it is possible no alias is given in which case we fall back to the current editor lang + let modeId: string | undefined | null; + if (languageAlias) { + modeId = this._modeService.getModeIdForLanguageName(languageAlias); + } else if (this._options.editor) { + modeId = this._options.editor.getModel()?.getLanguageIdentifier().language; + } + if (!modeId) { + modeId = 'plaintext'; + } + this._modeService.triggerMode(modeId); + const tokenization = await TokenizationRegistry.getPromise(modeId) ?? undefined; + + const element = document.createElement('span'); + + element.innerHTML = MarkdownRenderer._ttpTokenizer + ? MarkdownRenderer._ttpTokenizer.createHTML(value, tokenization) as unknown as string + : tokenizeToString(value, tokenization); + + // use "good" font + let fontFamily = this._options.codeBlockFontFamily; + if (this._options.editor) { + fontFamily = this._options.editor.getOption(EditorOption.fontInfo).fontFamily; + } + if (fontFamily) { + element.style.fontFamily = fontFamily; + } + + return element; + }, + codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), + actionHandler: { + callback: (content) => this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError), + disposeables + } + }; + } +} diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 85ee85e34f7..815fdf6b51c 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -7,18 +7,18 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { OverviewRulerPosition, ConfigurationChangedEvent, EditorLayoutInfo, IComputedEditorOptions, EditorOption, FindComputedEditorOptionValueById, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { ICursors } from 'vs/editor/common/controller/cursorCommon'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer, IWordAtPosition } from 'vs/editor/common/model'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; /** * A view zone is a full horizontal rectangle that 'pushes' text down. @@ -156,6 +156,18 @@ export interface IContentWidget { * If null is returned, the content widget will be placed off screen. */ getPosition(): IContentWidgetPosition | null; + /** + * Optional function that is invoked before rendering + * the content widget. If a dimension is returned the editor will + * attempt to use it. + */ + beforeRender?(): editorCommon.IDimension | null; + /** + * Optional function that is invoked after rendering the content + * widget. Is being invoked with the selected position preference + * or `null` if not rendered. + */ + afterRender?(position: ContentWidgetPositionPreference | null): void; } /** @@ -333,6 +345,27 @@ export interface IOverviewRuler { */ export interface IEditorAriaOptions { activeDescendant: string | undefined; + role?: string; +} + +export interface IEditorConstructionOptions extends IEditorOptions { + /** + * The initial editor dimension (to avoid measuring the container). + */ + dimension?: editorCommon.IDimension; + /** + * Place overflow widgets inside an external DOM node. + * Defaults to an internal DOM node. + */ + overflowWidgetsDomNode?: HTMLElement; +} + +export interface IDiffEditorConstructionOptions extends IDiffEditorOptions { + /** + * Place overflow widgets inside an external DOM node. + * Defaults to an internal DOM node. + */ + overflowWidgetsDomNode?: HTMLElement; } /** @@ -432,7 +465,6 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * An event emitted when editing failed because the editor is read-only. * @event - * @internal */ onDidAttemptReadOnlyEdit(listener: () => void): IDisposable; /** @@ -567,6 +599,16 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getRawOptions(): IEditorOptions; + /** + * @internal + */ + getOverflowWidgetsDomNode(): HTMLElement | undefined; + + /** + * @internal + */ + getConfiguredWordAtPosition(position: Position): IWordAtPosition | null; + /** * Get value of the current model attached to this editor. * @see `ITextModel.getValue` @@ -610,15 +652,15 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * Change the scrollLeft of the editor's viewport. */ - setScrollLeft(newScrollLeft: number): void; + setScrollLeft(newScrollLeft: number, scrollType?: editorCommon.ScrollType): void; /** * Change the scrollTop of the editor's viewport. */ - setScrollTop(newScrollTop: number): void; + setScrollTop(newScrollTop: number, scrollType?: editorCommon.ScrollType): void; /** * Change the scroll position of the editor's viewport. */ - setScrollPosition(position: editorCommon.INewScrollPosition): void; + setScrollPosition(position: editorCommon.INewScrollPosition, scrollType?: editorCommon.ScrollType): void; /** * Get an action that is a contribution to this editor. @@ -633,7 +675,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * @param source The source of the call. * @param command The command to execute */ - executeCommand(source: string, command: editorCommon.ICommand): void; + executeCommand(source: string | null | undefined, command: editorCommon.ICommand): void; /** * Push an "undo stop" in the undo-redo stack. @@ -647,19 +689,19 @@ export interface ICodeEditor extends editorCommon.IEditor { * @param edits The edits to execute. * @param endCursorState Cursor state after the edits were applied. */ - executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean; + executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean; /** * Execute multiple (concomitant) commands on the editor. * @param source The source of the call. * @param command The commands to execute */ - executeCommands(source: string, commands: (editorCommon.ICommand | null)[]): void; + executeCommands(source: string | null | undefined, commands: (editorCommon.ICommand | null)[]): void; /** * @internal */ - _getCursors(): ICursors | null; + _getViewModel(): IViewModel | null; /** * Get all the decorations on a line (filtering out decorations from other editors). @@ -698,6 +740,11 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getVisibleRanges(): Range[]; + /** + * @internal + */ + getVisibleRangesPlusViewportAboveBelow(): Range[]; + /** * Get the view zones. * @internal @@ -847,7 +894,7 @@ export interface IActiveCodeEditor extends ICodeEditor { /** * @internal */ - _getCursors(): ICursors; + _getViewModel(): IViewModel; /** * Get all the decorations on a line (filtering out decorations from other editors). @@ -1008,6 +1055,16 @@ export function isDiffEditor(thing: any): thing is IDiffEditor { } } +/** + *@internal + */ +export function isCompositeEditor(thing: any): thing is editorCommon.ICompositeCodeEditor { + return thing + && typeof thing === 'object' + && typeof (thing).onDidChangeActiveEditor === 'function'; + +} + /** *@internal */ @@ -1022,3 +1079,14 @@ export function getCodeEditor(thing: any): ICodeEditor | null { return null; } + +/** + *@internal + */ +export function getIEditor(thing: any): editorCommon.IEditor | null { + if (isCodeEditor(thing) || isDiffEditor(thing)) { + return thing; + } + + return null; +} diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index f6d836aac53..aaf9feaf3ed 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; @@ -13,14 +14,18 @@ import { IEditorContribution, IDiffEditorContribution } from 'vs/editor/common/e import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, Action2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IKeybindings, KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { withNullAsUndefined, assertType } from 'vs/base/common/types'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; + export type ServicesAccessor = InstantiationServicesAccessor; export type IEditorContributionCtor = IConstructorSignature1; @@ -39,26 +44,31 @@ export interface IDiffEditorContributionDescription { //#region Command export interface ICommandKeybindingsOptions extends IKeybindings { - kbExpr?: ContextKeyExpr | null; + kbExpr?: ContextKeyExpression | null; weight: number; + /** + * the default keybinding arguments + */ + args?: any; } export interface ICommandMenuOptions { menuId: MenuId; group: string; order: number; - when?: ContextKeyExpr; + when?: ContextKeyExpression; title: string; + icon?: ThemeIcon } export interface ICommandOptions { id: string; - precondition: ContextKeyExpr | undefined; + precondition: ContextKeyExpression | undefined; kbOpts?: ICommandKeybindingsOptions; description?: ICommandHandlerDescription; menuOpts?: ICommandMenuOptions | ICommandMenuOptions[]; } export abstract class Command { public readonly id: string; - public readonly precondition: ContextKeyExpr | undefined; + public readonly precondition: ContextKeyExpression | undefined; private readonly _kbOpts: ICommandKeybindingsOptions | undefined; private readonly _menuOpts: ICommandMenuOptions | ICommandMenuOptions[] | undefined; private readonly _description: ICommandHandlerDescription | undefined; @@ -93,6 +103,7 @@ export abstract class Command { id: this.id, handler: (accessor, args) => this.runCommand(accessor, args), weight: this._kbOpts.weight, + args: this._kbOpts.args, when: kbWhen, primary: this._kbOpts.primary, secondary: this._kbOpts.secondary, @@ -118,7 +129,8 @@ export abstract class Command { command: { id: this.id, title: item.title, - // precondition: this.precondition + icon: item.icon, + precondition: this.precondition }, when: item.when, order: item.order @@ -130,6 +142,70 @@ export abstract class Command { //#endregion Command +//#region MultiplexingCommand + +/** + * Potential override for a command. + * + * @return `true` if the command was successfully run. This stops other overrides from being executed. + */ +export type CommandImplementation = (accessor: ServicesAccessor, args: unknown) => boolean | Promise; + +export class MultiCommand extends Command { + + private readonly _implementations: [number, CommandImplementation][] = []; + + /** + * A higher priority gets to be looked at first + */ + public addImplementation(priority: number, implementation: CommandImplementation): IDisposable { + this._implementations.push([priority, implementation]); + this._implementations.sort((a, b) => b[0] - a[0]); + return { + dispose: () => { + for (let i = 0; i < this._implementations.length; i++) { + if (this._implementations[i][1] === implementation) { + this._implementations.splice(i, 1); + return; + } + } + } + }; + } + + public runCommand(accessor: ServicesAccessor, args: any): void | Promise { + for (const impl of this._implementations) { + const result = impl[1](accessor, args); + if (result) { + if (typeof result === 'boolean') { + return; + } + return result; + } + } + } +} + +//#endregion + +/** + * A command that delegates to another command's implementation. + * + * This lets different commands be registered but share the same implementation + */ +export class ProxyCommand extends Command { + constructor( + private readonly command: Command, + opts: ICommandOptions + ) { + super(opts); + } + + public runCommand(accessor: ServicesAccessor, args: any): void | Promise { + return this.command.runCommand(accessor, args); + } +} + //#region EditorCommand export interface IContributionCommandOptions extends ICommandOptions { @@ -193,7 +269,7 @@ export abstract class EditorCommand extends Command { export interface IEditorActionContextMenuOptions { group: string; order: number; - when?: ContextKeyExpr; + when?: ContextKeyExpression; menuId?: MenuId; } export interface IActionOptions extends ICommandOptions { @@ -265,8 +341,72 @@ export abstract class EditorAction extends EditorCommand { public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise; } +export abstract class MultiEditorAction extends EditorAction { + private readonly _implementations: [number, CommandImplementation][] = []; + + constructor(opts: IActionOptions) { + super(opts); + } + + public addImplementation(priority: number, implementation: CommandImplementation): IDisposable { + this._implementations.push([priority, implementation]); + this._implementations.sort((a, b) => b[0] - a[0]); + return { + dispose: () => { + for (let i = 0; i < this._implementations.length; i++) { + if (this._implementations[i][1] === implementation) { + this._implementations.splice(i, 1); + return; + } + } + } + }; + } + + public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise { + this.reportTelemetry(accessor, editor); + + for (const impl of this._implementations) { + if (impl[1](accessor, args)) { + return; + } + } + + return this.run(accessor, editor, args || {}); + } + + public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise; + +} + //#endregion EditorAction +//#region EditorAction2 + +export abstract class EditorAction2 extends Action2 { + + run(accessor: ServicesAccessor, ...args: any[]) { + // Find the editor with text focus or active + const codeEditorService = accessor.get(ICodeEditorService); + const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor(); + if (!editor) { + // well, at least we tried... + return; + } + // precondition does hold + return editor.invokeWithinContext((editorAccessor) => { + const kbService = editorAccessor.get(IContextKeyService); + if (kbService.contextMatchesRules(withNullAsUndefined(this.desc.precondition))) { + return this.runEditorCommand(editorAccessor, editor!, args); + } + }); + } + + abstract runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): any; +} + +//#endregion + // --- Registration of commands and actions export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: Args) => any) { @@ -321,7 +461,7 @@ export function registerModelAndPositionCommand(id: string, handler: (model: ITe const model = accessor.get(IModelService).getModel(resource); if (model) { const editorPosition = Position.lift(position); - return handler(model, editorPosition, args.slice(2)); + return handler(model, editorPosition, ...args.slice(2)); } return accessor.get(ITextModelService).createModelReference(resource).then(reference => { @@ -347,7 +487,7 @@ export function registerModelCommand(id: string, handler: (model: ITextModel, .. const model = accessor.get(IModelService).getModel(resource); if (model) { - return handler(model, args.slice(1)); + return handler(model, ...args.slice(1)); } return accessor.get(ITextModelService).createModelReference(resource).then(reference => { @@ -370,8 +510,15 @@ export function registerEditorCommand(editorCommand: T) return editorCommand; } -export function registerEditorAction(ctor: { new(): EditorAction; }): void { - EditorContributionRegistry.INSTANCE.registerEditorAction(new ctor()); +export function registerEditorAction(ctor: { new(): T; }): T { + const action = new ctor(); + EditorContributionRegistry.INSTANCE.registerEditorAction(action); + return action; +} + +export function registerMultiEditorAction(action: T): T { + EditorContributionRegistry.INSTANCE.registerEditorAction(action); + return action; } export function registerInstantiatedEditorAction(editorAction: EditorAction): void { @@ -466,3 +613,75 @@ class EditorContributionRegistry { } Registry.add(Extensions.EditorCommonContributions, EditorContributionRegistry.INSTANCE); + +function registerCommand(command: T): T { + command.register(); + return command; +} + +export const UndoCommand = registerCommand(new MultiCommand({ + id: 'undo', + precondition: undefined, + kbOpts: { + weight: KeybindingWeight.EditorCore, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Z + }, + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '1_do', + title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), + order: 1 + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('undo', "Undo"), + order: 1 + }] +})); + +registerCommand(new ProxyCommand(UndoCommand, { id: 'default:undo', precondition: undefined })); + +export const RedoCommand = registerCommand(new MultiCommand({ + id: 'redo', + precondition: undefined, + kbOpts: { + weight: KeybindingWeight.EditorCore, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z } + }, + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '1_do', + title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), + order: 2 + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('redo', "Redo"), + order: 1 + }] +})); + +registerCommand(new ProxyCommand(RedoCommand, { id: 'default:redo', precondition: undefined })); + +export const SelectAllCommand = registerCommand(new MultiCommand({ + id: 'editor.action.selectAll', + precondition: undefined, + kbOpts: { + weight: KeybindingWeight.EditorCore, + kbExpr: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_A + }, + menuOpts: [{ + menuId: MenuId.MenubarSelectionMenu, + group: '1_basic', + title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), + order: 1 + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('selectAll', "Select All"), + order: 1 + }] +})); diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index bb96485db3e..f48a9cff839 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -9,11 +9,12 @@ import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { URI } from 'vs/base/common/uri'; export abstract class AbstractCodeEditorService extends Disposable implements ICodeEditorService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onCodeEditorAdd: Emitter = this._register(new Emitter()); public readonly onCodeEditorAdd: Event = this._onCodeEditorAdd.event; @@ -94,6 +95,29 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions; private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; + private readonly _modelProperties = new Map>(); + + public setModelProperty(resource: URI, key: string, value: any): void { + const key1 = resource.toString(); + let dest: Map; + if (this._modelProperties.has(key1)) { + dest = this._modelProperties.get(key1)!; + } else { + dest = new Map(); + this._modelProperties.set(key1, dest); + } + + dest.set(key, value); + } + + public getModelProperty(resource: URI, key: string): any { + const key1 = resource.toString(); + if (this._modelProperties.has(key1)) { + const innerMap = this._modelProperties.get(key1)!; + return innerMap.get(key); + } + return undefined; + } public setTransientModelProperty(model: ITextModel, key: string, value: any): void { const uri = model.uri.toString(); @@ -135,7 +159,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC } abstract getActiveCodeEditor(): ICodeEditor | null; - abstract openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; + abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; } export class ModelTransientSettingWatcher { diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index d7d1ad70417..7a1f3346e46 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -4,31 +4,84 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { WorkspaceEdit } from 'vs/editor/common/modes'; +import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, WorkspaceFileEdit, WorkspaceFileEditOptions, WorkspaceTextEdit } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { isObject } from 'vs/base/common/types'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); +function isWorkspaceFileEdit(thing: any): thing is WorkspaceFileEdit { + return isObject(thing) && (Boolean((thing).newUri) || Boolean((thing).oldUri)); +} + +function isWorkspaceTextEdit(thing: any): thing is WorkspaceTextEdit { + return isObject(thing) && URI.isUri((thing).resource) && isObject((thing).edit); +} + +export class ResourceEdit { + + protected constructor(readonly metadata?: WorkspaceEditMetadata) { } + + static convert(edit: WorkspaceEdit): ResourceEdit[] { + + + return edit.edits.map(edit => { + if (isWorkspaceTextEdit(edit)) { + return new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata); + } + if (isWorkspaceFileEdit(edit)) { + return new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata); + } + throw new Error('Unsupported edit'); + }); + } +} + +export class ResourceTextEdit extends ResourceEdit { + constructor( + readonly resource: URI, + readonly textEdit: TextEdit, + readonly versionId?: number, + readonly metadata?: WorkspaceEditMetadata + ) { + super(metadata); + } +} + +export class ResourceFileEdit extends ResourceEdit { + constructor( + readonly oldResource: URI | undefined, + readonly newResource: URI | undefined, + readonly options?: WorkspaceFileEditOptions, + readonly metadata?: WorkspaceEditMetadata + ) { + super(metadata); + } +} + export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; showPreview?: boolean; label?: string; + quotableLabel?: string; } export interface IBulkEditResult { ariaSummary: string; } -export type IBulkEditPreviewHandler = (edit: WorkspaceEdit, options?: IBulkEditOptions) => Promise; +export type IBulkEditPreviewHandler = (edits: ResourceEdit[], options?: IBulkEditOptions) => Promise; export interface IBulkEditService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; + + hasPreviewHandler(): boolean; setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable; - apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise; + apply(edit: ResourceEdit[], options?: IBulkEditOptions): Promise; } - diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 145cb9d2641..0174886d201 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -7,13 +7,14 @@ import { Event } from 'vs/base/common/event'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; export const ICodeEditorService = createDecorator('codeEditorService'); export interface ICodeEditorService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onCodeEditorAdd: Event; readonly onCodeEditorRemove: Event; @@ -41,10 +42,13 @@ export interface ICodeEditorService { removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; + setModelProperty(resource: URI, key: string, value: any): void; + getModelProperty(resource: URI, key: string): any; + setTransientModelProperty(model: ITextModel, key: string, value: any): void; getTransientModelProperty(model: ITextModel, key: string): any; getTransientModelProperties(model: ITextModel): [string, any][] | undefined; getActiveCodeEditor(): ICodeEditor | null; - openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; + openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; } diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 944a6d0f20f..464c1a33d41 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -11,20 +11,20 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { AbstractCodeEditorService } from 'vs/editor/browser/services/abstractCodeEditorService'; import { IContentDecorationRenderOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions, isThemeColor } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { IColorTheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService'; -class RefCountedStyleSheet { +export class RefCountedStyleSheet { private readonly _parent: CodeEditorServiceImpl; private readonly _editorId: string; - public readonly styleSheet: HTMLStyleElement; + private readonly _styleSheet: HTMLStyleElement; private _refCount: number; constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) { this._parent = parent; this._editorId = editorId; - this.styleSheet = styleSheet; + this._styleSheet = styleSheet; this._refCount = 0; } @@ -35,17 +35,26 @@ class RefCountedStyleSheet { public unref(): void { this._refCount--; if (this._refCount === 0) { - this.styleSheet.parentNode?.removeChild(this.styleSheet); + this._styleSheet.parentNode?.removeChild(this._styleSheet); this._parent._removeEditorStyleSheets(this._editorId); } } + + public insertRule(rule: string, index?: number): void { + const sheet = this._styleSheet.sheet; + sheet.insertRule(rule, index); + } + + public removeRulesContainingSelector(ruleName: string): void { + dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet); + } } -class GlobalStyleSheet { - public readonly styleSheet: HTMLStyleElement; +export class GlobalStyleSheet { + private readonly _styleSheet: HTMLStyleElement; constructor(styleSheet: HTMLStyleElement) { - this.styleSheet = styleSheet; + this._styleSheet = styleSheet; } public ref(): void { @@ -53,6 +62,15 @@ class GlobalStyleSheet { public unref(): void { } + + public insertRule(rule: string, index?: number): void { + const sheet = this._styleSheet.sheet; + sheet.insertRule(rule, index); + } + + public removeRulesContainingSelector(ruleName: string): void { + dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet); + } } export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { @@ -62,9 +80,9 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { private readonly _editorStyleSheets = new Map(); private readonly _themeService: IThemeService; - constructor(@IThemeService themeService: IThemeService, styleSheet: HTMLStyleElement | null = null) { + constructor(@IThemeService themeService: IThemeService, styleSheet: GlobalStyleSheet | null = null) { super(); - this._globalStyleSheet = styleSheet ? new GlobalStyleSheet(styleSheet) : null; + this._globalStyleSheet = styleSheet ? styleSheet : null; this._themeService = themeService; } @@ -100,7 +118,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { if (!provider) { const styleSheet = this._getOrCreateStyleSheet(editor); const providerArgs: ProviderArguments = { - styleSheet: styleSheet.styleSheet, + styleSheet: styleSheet, key: key, parentTypeKey: parentTypeKey, options: options || Object.create(null) @@ -136,7 +154,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { } abstract getActiveCodeEditor(): ICodeEditor | null; - abstract openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; + abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; } interface IModelDecorationOptionsProvider extends IDisposable { @@ -188,7 +206,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide } interface ProviderArguments { - styleSheet: HTMLStyleElement; + styleSheet: GlobalStyleSheet | RefCountedStyleSheet; key: string; parentTypeKey?: string; options: IDecorationRenderOptions; @@ -320,7 +338,7 @@ const _CSS_MAP: { [prop: string]: string; } = { class DecorationCSSRules { - private _theme: ITheme; + private _theme: IColorTheme; private readonly _className: string; private readonly _unThemedSelector: string; private _hasContent: boolean; @@ -330,8 +348,8 @@ class DecorationCSSRules { private readonly _providerArgs: ProviderArguments; private _usesThemeColors: boolean; - public constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) { - this._theme = themeService.getTheme(); + constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) { + this._theme = themeService.getColorTheme(); this._ruleType = ruleType; this._providerArgs = providerArgs; this._usesThemeColors = false; @@ -349,8 +367,8 @@ class DecorationCSSRules { this._buildCSS(); if (this._usesThemeColors) { - this._themeListener = themeService.onThemeChange(theme => { - this._theme = themeService.getTheme(); + this._themeListener = themeService.onDidColorThemeChange(theme => { + this._theme = themeService.getColorTheme(); this._removeCSS(); this._buildCSS(); }); @@ -414,7 +432,7 @@ class DecorationCSSRules { default: throw new Error('Unknown rule type: ' + this._ruleType); } - const sheet = this._providerArgs.styleSheet.sheet; + const sheet = this._providerArgs.styleSheet; let hasContent = false; if (unthemedCSS.length > 0) { @@ -433,7 +451,7 @@ class DecorationCSSRules { } private _removeCSS(): void { - dom.removeCSSRulesContainingSelector(this._unThemedSelector, this._providerArgs.styleSheet); + this._providerArgs.styleSheet.removeRulesContainingSelector(this._unThemedSelector); } /** diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index a33a4ec776f..cb61a91ef72 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -11,7 +11,7 @@ import { Schemas } from 'vs/base/common/network'; import { normalizePath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener'; import { EditorOpenContext } from 'vs/platform/editor/common/editor'; @@ -28,9 +28,6 @@ class CommandOpener implements IOpener { if (typeof target === 'string') { target = URI.parse(target); } - if (!CommandsRegistry.getCommand(target.path)) { - throw new Error(`command '${target.path}' NOT known`); - } // execute as command let args: any = []; try { @@ -73,7 +70,7 @@ class EditorOpener implements IOpener { } if (target.scheme === Schemas.file) { - target = normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + target = normalizePath(target); // workaround for non-normalized paths (https://github.com/microsoft/vscode/issues/12954) } await this._editorService.openCodeEditor( @@ -88,7 +85,7 @@ class EditorOpener implements IOpener { export class OpenerService implements IOpenerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); @@ -103,7 +100,15 @@ export class OpenerService implements IOpenerService { // Default external opener is going through window.open() this._externalOpener = { openExternal: href => { - dom.windowOpenNoOpener(href); + // ensure to open HTTP/HTTPS links into new windows + // to not trigger a navigation. Any other link is + // safe to be set as HREF to prevent a blank window + // from opening. + if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) { + dom.windowOpenNoOpener(href); + } else { + window.location.href = href; + } return Promise.resolve(true); } }; @@ -175,7 +180,7 @@ export class OpenerService implements IOpenerService { private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise { - //todo@joh IExternalUriResolver should support `uri: URI | string` + //todo@jrieken IExternalUriResolver should support `uri: URI | string` const uri = typeof resource === 'string' ? URI.parse(resource) : resource; const { resolved } = await this.resolveExternalUri(uri, options); diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index ab567c36c9f..45c568d0715 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -111,6 +111,7 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe containerDomNode.style.position = 'absolute'; containerDomNode.style.top = '10000'; + containerDomNode.style.wordWrap = 'break-word'; document.body.appendChild(containerDomNode); let range = document.createRange(); @@ -148,6 +149,10 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe return result; } +const enum Constants { + SPAN_MODULO_LIMIT = 16384 +} + function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] { sb.appendASCIIString('
    MinimapCharRenderer; - /** * container dom node left position (in CSS px) */ @@ -107,40 +91,67 @@ class MinimapOptions { */ public readonly canvasOuterHeight: number; - constructor(configuration: IConfiguration) { + public readonly isSampling: boolean; + public readonly editorHeight: number; + public readonly fontScale: number; + public readonly minimapLineHeight: number; + public readonly minimapCharWidth: number; + + public readonly charRenderer: () => MinimapCharRenderer; + public readonly backgroundColor: RGBA8; + + constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker) { const options = configuration.options; const pixelRatio = options.get(EditorOption.pixelRatio); const layoutInfo = options.get(EditorOption.layoutInfo); + const minimapLayout = layoutInfo.minimap; const fontInfo = options.get(EditorOption.fontInfo); - - this.renderMinimap = layoutInfo.renderMinimap | 0; - this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); const minimapOpts = options.get(EditorOption.minimap); + + this.renderMinimap = minimapLayout.renderMinimap; + this.size = minimapOpts.size; + this.minimapHeightIsEditorHeight = minimapLayout.minimapHeightIsEditorHeight; + this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); this.showSlider = minimapOpts.showSlider; - this.fontScale = Math.round(minimapOpts.scale * pixelRatio); - this.charRenderer = once(() => MinimapCharRendererFactory.create(this.fontScale, fontInfo.fontFamily)); this.pixelRatio = pixelRatio; this.typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; this.lineHeight = options.get(EditorOption.lineHeight); - this.minimapLeft = layoutInfo.minimapLeft; - this.minimapWidth = layoutInfo.minimapWidth; + this.minimapLeft = minimapLayout.minimapLeft; + this.minimapWidth = minimapLayout.minimapWidth; this.minimapHeight = layoutInfo.height; - this.canvasInnerWidth = Math.floor(pixelRatio * this.minimapWidth); - this.canvasInnerHeight = Math.floor(pixelRatio * this.minimapHeight); + this.canvasInnerWidth = minimapLayout.minimapCanvasInnerWidth; + this.canvasInnerHeight = minimapLayout.minimapCanvasInnerHeight; + this.canvasOuterWidth = minimapLayout.minimapCanvasOuterWidth; + this.canvasOuterHeight = minimapLayout.minimapCanvasOuterHeight; - this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio; - this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio; + this.isSampling = minimapLayout.minimapIsSampling; + this.editorHeight = layoutInfo.height; + this.fontScale = minimapLayout.minimapScale; + this.minimapLineHeight = minimapLayout.minimapLineHeight; + this.minimapCharWidth = Constants.BASE_CHAR_WIDTH * this.fontScale; + + this.charRenderer = once(() => MinimapCharRendererFactory.create(this.fontScale, fontInfo.fontFamily)); + this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker); + } + + private static _getMinimapBackground(theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 { + const themeColor = theme.getColor(minimapBackground); + if (themeColor) { + return new RGBA8(themeColor.rgba.r, themeColor.rgba.g, themeColor.rgba.b, themeColor.rgba.a); + } + return tokensColorTracker.getColor(ColorId.DefaultBackground); } public equals(other: MinimapOptions): boolean { return (this.renderMinimap === other.renderMinimap + && this.size === other.size + && this.minimapHeightIsEditorHeight === other.minimapHeightIsEditorHeight && this.scrollBeyondLastLine === other.scrollBeyondLastLine && this.showSlider === other.showSlider && this.pixelRatio === other.pixelRatio && this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth && this.lineHeight === other.lineHeight - && this.fontScale === other.fontScale && this.minimapLeft === other.minimapLeft && this.minimapWidth === other.minimapWidth && this.minimapHeight === other.minimapHeight @@ -148,6 +159,12 @@ class MinimapOptions { && this.canvasInnerHeight === other.canvasInnerHeight && this.canvasOuterWidth === other.canvasOuterWidth && this.canvasOuterHeight === other.canvasOuterHeight + && this.isSampling === other.isSampling + && this.editorHeight === other.editorHeight + && this.fontScale === other.fontScale + && this.minimapLineHeight === other.minimapLineHeight + && this.minimapCharWidth === other.minimapCharWidth + && this.backgroundColor && this.backgroundColor.equals(other.backgroundColor) ); } } @@ -164,6 +181,7 @@ class MinimapLayout { */ public readonly scrollHeight: number; + public readonly sliderNeeded: boolean; private readonly _computedSliderRatio: number; /** @@ -187,6 +205,7 @@ class MinimapLayout { constructor( scrollTop: number, scrollHeight: number, + sliderNeeded: boolean, computedSliderRatio: number, sliderTop: number, sliderHeight: number, @@ -195,6 +214,7 @@ class MinimapLayout { ) { this.scrollTop = scrollTop; this.scrollHeight = scrollHeight; + this.sliderNeeded = sliderNeeded; this._computedSliderRatio = computedSliderRatio; this.sliderTop = sliderTop; this.sliderHeight = sliderHeight; @@ -221,15 +241,32 @@ class MinimapLayout { viewportHeight: number, viewportContainsWhitespaceGaps: boolean, lineCount: number, + realLineCount: number, scrollTop: number, scrollHeight: number, previousLayout: MinimapLayout | null ): MinimapLayout { const pixelRatio = options.pixelRatio; - const minimapLineHeight = getMinimapLineHeight(options.renderMinimap, options.fontScale); + const minimapLineHeight = options.minimapLineHeight; const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight); const lineHeight = options.lineHeight; + if (options.minimapHeightIsEditorHeight) { + const logicalScrollHeight = ( + realLineCount * options.lineHeight + + (options.scrollBeyondLastLine ? viewportHeight - options.lineHeight : 0) + ); + const sliderHeight = Math.max(1, Math.floor(viewportHeight * viewportHeight / logicalScrollHeight)); + const maxMinimapSliderTop = Math.max(0, options.minimapHeight - sliderHeight); + // The slider can move from 0 to `maxMinimapSliderTop` + // in the same way `scrollTop` can move from 0 to `scrollHeight` - `viewportHeight`. + const computedSliderRatio = (maxMinimapSliderTop) / (scrollHeight - viewportHeight); + const sliderTop = (scrollTop * computedSliderRatio); + const sliderNeeded = (maxMinimapSliderTop > 0); + const maxLinesFitting = Math.floor(options.canvasInnerHeight / options.minimapLineHeight); + return new MinimapLayout(scrollTop, scrollHeight, sliderNeeded, computedSliderRatio, sliderTop, sliderHeight, 1, Math.min(lineCount, maxLinesFitting)); + } + // The visible line count in a viewport can change due to a number of reasons: // a) with the same viewport width, different scroll positions can result in partial lines being visible: // e.g. for a line height of 20, and a viewport height of 600 @@ -270,14 +307,14 @@ class MinimapLayout { let extraLinesAtTheBottom = 0; if (options.scrollBeyondLastLine) { const expectedViewportLineCount = viewportHeight / lineHeight; - extraLinesAtTheBottom = expectedViewportLineCount; + extraLinesAtTheBottom = expectedViewportLineCount - 1; } if (minimapLinesFitting >= lineCount + extraLinesAtTheBottom) { // All lines fit in the minimap const startLineNumber = 1; const endLineNumber = lineCount; - - return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + const sliderNeeded = (maxMinimapSliderTop > 0); + return new MinimapLayout(scrollTop, scrollHeight, sliderNeeded, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); } else { let startLineNumber = Math.max(1, Math.floor(viewportStartLineNumber - sliderTop * pixelRatio / minimapLineHeight)); @@ -296,7 +333,7 @@ class MinimapLayout { const endLineNumber = Math.min(lineCount, startLineNumber + minimapLinesFitting - 1); - return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + return new MinimapLayout(scrollTop, scrollHeight, true, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); } } } @@ -378,17 +415,17 @@ class RenderData { }; } - public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { - return this._renderedLines.onLinesChanged(e.fromLineNumber, e.toLineNumber); + public onLinesChanged(changeFromLineNumber: number, changeToLineNumber: number): boolean { + return this._renderedLines.onLinesChanged(changeFromLineNumber, changeToLineNumber); } - public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): void { - this._renderedLines.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): void { + this._renderedLines.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); } - public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void { - this._renderedLines.onLinesInserted(e.fromLineNumber, e.toLineNumber); + public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): void { + this._renderedLines.onLinesInserted(insertFromLineNumber, insertToLineNumber); } - public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { - return this._renderedLines.onTokensChanged(e.ranges); + public onTokensChanged(ranges: { fromLineNumber: number; toLineNumber: number; }[]): boolean { + return this._renderedLines.onTokensChanged(ranges); } } @@ -445,7 +482,557 @@ class MinimapBuffers { } } -export class Minimap extends ViewPart { +export interface IMinimapModel { + readonly tokensColorTracker: MinimapTokensColorTracker; + readonly options: MinimapOptions; + + getLineCount(): number; + getRealLineCount(): number; + getLineContent(lineNumber: number): string; + getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; + getSelections(): Selection[]; + getMinimapDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[]; + getOptions(): TextModelResolvedOptions; + revealLineNumber(lineNumber: number): void; + setScrollTop(scrollTop: number): void; +} + +interface IMinimapRenderingContext { + readonly viewportContainsWhitespaceGaps: boolean; + + readonly scrollWidth: number; + readonly scrollHeight: number; + + readonly viewportStartLineNumber: number; + readonly viewportEndLineNumber: number; + + readonly scrollTop: number; + readonly scrollLeft: number; + + readonly viewportWidth: number; + readonly viewportHeight: number; +} + +interface SamplingStateLinesDeletedEvent { + type: 'deleted'; + _oldIndex: number; + deleteFromLineNumber: number; + deleteToLineNumber: number; +} + +interface SamplingStateLinesInsertedEvent { + type: 'inserted'; + _i: number; + insertFromLineNumber: number; + insertToLineNumber: number; +} + +interface SamplingStateFlushEvent { + type: 'flush'; +} + +type SamplingStateEvent = SamplingStateLinesInsertedEvent | SamplingStateLinesDeletedEvent | SamplingStateFlushEvent; + +class MinimapSamplingState { + + public static compute(options: MinimapOptions, viewLineCount: number, oldSamplingState: MinimapSamplingState | null): [MinimapSamplingState | null, SamplingStateEvent[]] { + if (options.renderMinimap === RenderMinimap.None || !options.isSampling) { + return [null, []]; + } + + // ratio is intentionally not part of the layout to avoid the layout changing all the time + // so we need to recompute it again... + const pixelRatio = options.pixelRatio; + const lineHeight = options.lineHeight; + const scrollBeyondLastLine = options.scrollBeyondLastLine; + const { minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({ + viewLineCount: viewLineCount, + scrollBeyondLastLine: scrollBeyondLastLine, + height: options.editorHeight, + lineHeight: lineHeight, + pixelRatio: pixelRatio + }); + const ratio = viewLineCount / minimapLineCount; + const halfRatio = ratio / 2; + + if (!oldSamplingState || oldSamplingState.minimapLines.length === 0) { + let result: number[] = []; + result[0] = 1; + if (minimapLineCount > 1) { + for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) { + result[i] = Math.round(i * ratio + halfRatio); + } + result[minimapLineCount - 1] = viewLineCount; + } + return [new MinimapSamplingState(ratio, result), []]; + } + + const oldMinimapLines = oldSamplingState.minimapLines; + const oldLength = oldMinimapLines.length; + let result: number[] = []; + let oldIndex = 0; + let oldDeltaLineCount = 0; + let minViewLineNumber = 1; + const MAX_EVENT_COUNT = 10; // generate at most 10 events, if there are more than 10 changes, just flush all previous data + let events: SamplingStateEvent[] = []; + let lastEvent: SamplingStateEvent | null = null; + for (let i = 0; i < minimapLineCount; i++) { + const fromViewLineNumber = Math.max(minViewLineNumber, Math.round(i * ratio)); + const toViewLineNumber = Math.max(fromViewLineNumber, Math.round((i + 1) * ratio)); + + while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromViewLineNumber) { + if (events.length < MAX_EVENT_COUNT) { + const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount; + if (lastEvent && lastEvent.type === 'deleted' && lastEvent._oldIndex === oldIndex - 1) { + lastEvent.deleteToLineNumber++; + } else { + lastEvent = { type: 'deleted', _oldIndex: oldIndex, deleteFromLineNumber: oldMinimapLineNumber, deleteToLineNumber: oldMinimapLineNumber }; + events.push(lastEvent); + } + oldDeltaLineCount--; + } + oldIndex++; + } + + let selectedViewLineNumber: number; + if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toViewLineNumber) { + // reuse the old sampled line + selectedViewLineNumber = oldMinimapLines[oldIndex]; + oldIndex++; + } else { + if (i === 0) { + selectedViewLineNumber = 1; + } else if (i + 1 === minimapLineCount) { + selectedViewLineNumber = viewLineCount; + } else { + selectedViewLineNumber = Math.round(i * ratio + halfRatio); + } + if (events.length < MAX_EVENT_COUNT) { + const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount; + if (lastEvent && lastEvent.type === 'inserted' && lastEvent._i === i - 1) { + lastEvent.insertToLineNumber++; + } else { + lastEvent = { type: 'inserted', _i: i, insertFromLineNumber: oldMinimapLineNumber, insertToLineNumber: oldMinimapLineNumber }; + events.push(lastEvent); + } + oldDeltaLineCount++; + } + } + + result[i] = selectedViewLineNumber; + minViewLineNumber = selectedViewLineNumber; + } + + if (events.length < MAX_EVENT_COUNT) { + while (oldIndex < oldLength) { + const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount; + if (lastEvent && lastEvent.type === 'deleted' && lastEvent._oldIndex === oldIndex - 1) { + lastEvent.deleteToLineNumber++; + } else { + lastEvent = { type: 'deleted', _oldIndex: oldIndex, deleteFromLineNumber: oldMinimapLineNumber, deleteToLineNumber: oldMinimapLineNumber }; + events.push(lastEvent); + } + oldDeltaLineCount--; + oldIndex++; + } + } else { + // too many events, just give up + events = [{ type: 'flush' }]; + } + + return [new MinimapSamplingState(ratio, result), events]; + } + + constructor( + public readonly samplingRatio: number, + public readonly minimapLines: number[] + ) { + } + + public modelLineToMinimapLine(lineNumber: number): number { + return Math.min(this.minimapLines.length, Math.max(1, Math.round(lineNumber / this.samplingRatio))); + } + + /** + * Will return null if the model line ranges are not intersecting with a sampled model line. + */ + public modelLineRangeToMinimapLineRange(fromLineNumber: number, toLineNumber: number): [number, number] | null { + let fromLineIndex = this.modelLineToMinimapLine(fromLineNumber) - 1; + while (fromLineIndex > 0 && this.minimapLines[fromLineIndex - 1] >= fromLineNumber) { + fromLineIndex--; + } + let toLineIndex = this.modelLineToMinimapLine(toLineNumber) - 1; + while (toLineIndex + 1 < this.minimapLines.length && this.minimapLines[toLineIndex + 1] <= toLineNumber) { + toLineIndex++; + } + if (fromLineIndex === toLineIndex) { + const sampledLineNumber = this.minimapLines[fromLineIndex]; + if (sampledLineNumber < fromLineNumber || sampledLineNumber > toLineNumber) { + // This line is not part of the sampled lines ==> nothing to do + return null; + } + } + return [fromLineIndex + 1, toLineIndex + 1]; + } + + /** + * Will always return a range, even if it is not intersecting with a sampled model line. + */ + public decorationLineRangeToMinimapLineRange(startLineNumber: number, endLineNumber: number): [number, number] { + let minimapLineStart = this.modelLineToMinimapLine(startLineNumber); + let minimapLineEnd = this.modelLineToMinimapLine(endLineNumber); + if (startLineNumber !== endLineNumber && minimapLineEnd === minimapLineStart) { + if (minimapLineEnd === this.minimapLines.length) { + if (minimapLineStart > 1) { + minimapLineStart--; + } + } else { + minimapLineEnd++; + } + } + return [minimapLineStart, minimapLineEnd]; + } + + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): [number, number] { + // have the mapping be sticky + const deletedLineCount = e.toLineNumber - e.fromLineNumber + 1; + let changeStartIndex = this.minimapLines.length; + let changeEndIndex = 0; + for (let i = this.minimapLines.length - 1; i >= 0; i--) { + if (this.minimapLines[i] < e.fromLineNumber) { + break; + } + if (this.minimapLines[i] <= e.toLineNumber) { + // this line got deleted => move to previous available + this.minimapLines[i] = Math.max(1, e.fromLineNumber - 1); + changeStartIndex = Math.min(changeStartIndex, i); + changeEndIndex = Math.max(changeEndIndex, i); + } else { + this.minimapLines[i] -= deletedLineCount; + } + } + return [changeStartIndex, changeEndIndex]; + } + + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void { + // have the mapping be sticky + const insertedLineCount = e.toLineNumber - e.fromLineNumber + 1; + for (let i = this.minimapLines.length - 1; i >= 0; i--) { + if (this.minimapLines[i] < e.fromLineNumber) { + break; + } + this.minimapLines[i] += insertedLineCount; + } + } +} + +export class Minimap extends ViewPart implements IMinimapModel { + + public readonly tokensColorTracker: MinimapTokensColorTracker; + + private _selections: Selection[]; + private _minimapSelections: Selection[] | null; + + public options: MinimapOptions; + + private _samplingState: MinimapSamplingState | null; + private _shouldCheckSampling: boolean; + + private _actual: InnerMinimap; + + constructor(context: ViewContext) { + super(context); + + this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); + + this._selections = []; + this._minimapSelections = null; + + this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker); + const [samplingState,] = MinimapSamplingState.compute(this.options, this._context.model.getLineCount(), null); + this._samplingState = samplingState; + this._shouldCheckSampling = false; + + this._actual = new InnerMinimap(context.theme, this); + } + + public dispose(): void { + this._actual.dispose(); + super.dispose(); + } + + public getDomNode(): FastDomNode { + return this._actual.getDomNode(); + } + + private _onOptionsMaybeChanged(): boolean { + const opts = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker); + if (this.options.equals(opts)) { + return false; + } + this.options = opts; + this._recreateLineSampling(); + this._actual.onDidChangeOptions(); + return true; + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return this._onOptionsMaybeChanged(); + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._selections = e.selections; + this._minimapSelections = null; + return this._actual.onSelectionChanged(); + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + if (e.affectsMinimap) { + return this._actual.onDecorationsChanged(); + } + return false; + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + if (this._samplingState) { + this._shouldCheckSampling = true; + } + return this._actual.onFlushed(); + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + if (this._samplingState) { + const minimapLineRange = this._samplingState.modelLineRangeToMinimapLineRange(e.fromLineNumber, e.toLineNumber); + if (minimapLineRange) { + return this._actual.onLinesChanged(minimapLineRange[0], minimapLineRange[1]); + } else { + return false; + } + } else { + return this._actual.onLinesChanged(e.fromLineNumber, e.toLineNumber); + } + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + if (this._samplingState) { + const [changeStartIndex, changeEndIndex] = this._samplingState.onLinesDeleted(e); + if (changeStartIndex <= changeEndIndex) { + this._actual.onLinesChanged(changeStartIndex + 1, changeEndIndex + 1); + } + this._shouldCheckSampling = true; + return true; + } else { + return this._actual.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + } + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + if (this._samplingState) { + this._samplingState.onLinesInserted(e); + this._shouldCheckSampling = true; + return true; + } else { + return this._actual.onLinesInserted(e.fromLineNumber, e.toLineNumber); + } + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return this._actual.onScrollChanged(); + } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + this._context.model.invalidateMinimapColorCache(); + this._actual.onThemeChanged(); + this._onOptionsMaybeChanged(); + return true; + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + if (this._samplingState) { + let ranges: { fromLineNumber: number; toLineNumber: number; }[] = []; + for (const range of e.ranges) { + const minimapLineRange = this._samplingState.modelLineRangeToMinimapLineRange(range.fromLineNumber, range.toLineNumber); + if (minimapLineRange) { + ranges.push({ fromLineNumber: minimapLineRange[0], toLineNumber: minimapLineRange[1] }); + } + } + if (ranges.length) { + return this._actual.onTokensChanged(ranges); + } else { + return false; + } + } else { + return this._actual.onTokensChanged(e.ranges); + } + } + public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { + return this._actual.onTokensColorsChanged(); + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return this._actual.onZonesChanged(); + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + if (this._shouldCheckSampling) { + this._shouldCheckSampling = false; + this._recreateLineSampling(); + } + } + + public render(ctx: RestrictedRenderingContext): void { + let viewportStartLineNumber = ctx.visibleRange.startLineNumber; + let viewportEndLineNumber = ctx.visibleRange.endLineNumber; + + if (this._samplingState) { + viewportStartLineNumber = this._samplingState.modelLineToMinimapLine(viewportStartLineNumber); + viewportEndLineNumber = this._samplingState.modelLineToMinimapLine(viewportEndLineNumber); + } + + const minimapCtx: IMinimapRenderingContext = { + viewportContainsWhitespaceGaps: (ctx.viewportData.whitespaceViewportData.length > 0), + + scrollWidth: ctx.scrollWidth, + scrollHeight: ctx.scrollHeight, + + viewportStartLineNumber: viewportStartLineNumber, + viewportEndLineNumber: viewportEndLineNumber, + + scrollTop: ctx.scrollTop, + scrollLeft: ctx.scrollLeft, + + viewportWidth: ctx.viewportWidth, + viewportHeight: ctx.viewportHeight, + }; + this._actual.render(minimapCtx); + } + + //#region IMinimapModel + + private _recreateLineSampling(): void { + this._minimapSelections = null; + + const wasSampling = Boolean(this._samplingState); + const [samplingState, events] = MinimapSamplingState.compute(this.options, this._context.model.getLineCount(), this._samplingState); + this._samplingState = samplingState; + + if (wasSampling && this._samplingState) { + // was sampling, is sampling + for (const event of events) { + switch (event.type) { + case 'deleted': + this._actual.onLinesDeleted(event.deleteFromLineNumber, event.deleteToLineNumber); + break; + case 'inserted': + this._actual.onLinesInserted(event.insertFromLineNumber, event.insertToLineNumber); + break; + case 'flush': + this._actual.onFlushed(); + break; + } + } + } + } + + public getLineCount(): number { + if (this._samplingState) { + return this._samplingState.minimapLines.length; + } + return this._context.model.getLineCount(); + } + + public getRealLineCount(): number { + return this._context.model.getLineCount(); + } + + public getLineContent(lineNumber: number): string { + if (this._samplingState) { + return this._context.model.getLineContent(this._samplingState.minimapLines[lineNumber - 1]); + } + return this._context.model.getLineContent(lineNumber); + } + + public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[] { + if (this._samplingState) { + let result: (ViewLineData | null)[] = []; + for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { + if (needed[lineIndex]) { + result[lineIndex] = this._context.model.getViewLineData(this._samplingState.minimapLines[startLineNumber + lineIndex - 1]); + } else { + result[lineIndex] = null; + } + } + return result; + } + return this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed).data; + } + + public getSelections(): Selection[] { + if (this._minimapSelections === null) { + if (this._samplingState) { + this._minimapSelections = []; + for (const selection of this._selections) { + const [minimapLineStart, minimapLineEnd] = this._samplingState.decorationLineRangeToMinimapLineRange(selection.startLineNumber, selection.endLineNumber); + this._minimapSelections.push(new Selection(minimapLineStart, selection.startColumn, minimapLineEnd, selection.endColumn)); + } + } else { + this._minimapSelections = this._selections; + } + } + return this._minimapSelections; + } + + public getMinimapDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[] { + let visibleRange: Range; + if (this._samplingState) { + const modelStartLineNumber = this._samplingState.minimapLines[startLineNumber - 1]; + const modelEndLineNumber = this._samplingState.minimapLines[endLineNumber - 1]; + visibleRange = new Range(modelStartLineNumber, 1, modelEndLineNumber, this._context.model.getLineMaxColumn(modelEndLineNumber)); + } else { + visibleRange = new Range(startLineNumber, 1, endLineNumber, this._context.model.getLineMaxColumn(endLineNumber)); + } + const decorations = this._context.model.getDecorationsInViewport(visibleRange); + + if (this._samplingState) { + let result: ViewModelDecoration[] = []; + for (const decoration of decorations) { + if (!decoration.options.minimap) { + continue; + } + const range = decoration.range; + const minimapStartLineNumber = this._samplingState.modelLineToMinimapLine(range.startLineNumber); + const minimapEndLineNumber = this._samplingState.modelLineToMinimapLine(range.endLineNumber); + result.push(new ViewModelDecoration(new Range(minimapStartLineNumber, range.startColumn, minimapEndLineNumber, range.endColumn), decoration.options)); + } + return result; + } + return decorations; + } + + public getOptions(): TextModelResolvedOptions { + return this._context.model.getTextModelOptions(); + } + + public revealLineNumber(lineNumber: number): void { + if (this._samplingState) { + lineNumber = this._samplingState.minimapLines[lineNumber - 1]; + } + this._context.model.revealRange( + 'mouse', + false, + new Range(lineNumber, 1, lineNumber, 1), + viewEvents.VerticalRevealType.Center, + ScrollType.Smooth + ); + } + + public setScrollTop(scrollTop: number): void { + this._context.model.setScrollPosition({ + scrollTop: scrollTop + }, ScrollType.Immediate); + } + + //#endregion +} + +class InnerMinimap extends Disposable { + + private readonly _theme: EditorTheme; + private readonly _model: IMinimapModel; private readonly _domNode: FastDomNode; private readonly _shadow: FastDomNode; @@ -453,7 +1040,6 @@ export class Minimap extends ViewPart { private readonly _decorationsCanvas: FastDomNode; private readonly _slider: FastDomNode; private readonly _sliderHorizontal: FastDomNode; - private readonly _tokensColorTracker: MinimapTokensColorTracker; private readonly _mouseDownListener: IDisposable; private readonly _sliderMouseMoveMonitor: GlobalMouseMoveMonitor; private readonly _sliderMouseDownListener: IDisposable; @@ -462,21 +1048,24 @@ export class Minimap extends ViewPart { private readonly _sliderTouchMoveListener: IDisposable; private readonly _sliderTouchEndListener: IDisposable; - private _options: MinimapOptions; private _lastRenderData: RenderData | null; - private _selections: Selection[] = []; private _selectionColor: Color | undefined; private _renderDecorations: boolean = false; private _gestureInProgress: boolean = false; private _buffers: MinimapBuffers | null; - constructor(context: ViewContext) { - super(context); + constructor( + theme: EditorTheme, + model: IMinimapModel + ) { + super(); + + this._theme = theme; + this._model = model; - this._options = new MinimapOptions(this._context.configuration); this._lastRenderData = null; this._buffers = null; - this._selectionColor = this._context.theme.getColor(minimapSelection); + this._selectionColor = this._theme.getColor(minimapSelection); this._domNode = createFastDomNode(document.createElement('div')); PartFingerprints.write(this._domNode, PartFingerprint.Minimap); @@ -512,34 +1101,35 @@ export class Minimap extends ViewPart { this._sliderHorizontal.setClassName('minimap-slider-horizontal'); this._slider.appendChild(this._sliderHorizontal); - this._tokensColorTracker = MinimapTokensColorTracker.getInstance(); - this._applyLayout(); this._mouseDownListener = dom.addStandardDisposableListener(this._domNode.domNode, 'mousedown', (e) => { e.preventDefault(); - const renderMinimap = this._options.renderMinimap; + const renderMinimap = this._model.options.renderMinimap; if (renderMinimap === RenderMinimap.None) { return; } if (!this._lastRenderData) { return; } - const minimapLineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); - const internalOffsetY = this._options.pixelRatio * e.browserEvent.offsetY; + if (this._model.options.size !== 'proportional') { + if (e.leftButton && this._lastRenderData) { + // pretend the click occured in the center of the slider + const position = dom.getDomNodePagePosition(this._slider.domNode); + const initialPosY = position.top + position.height / 2; + this._startSliderDragging(e.buttons, e.posx, initialPosY, e.posy, this._lastRenderData.renderedLayout); + } + return; + } + const minimapLineHeight = this._model.options.minimapLineHeight; + const internalOffsetY = (this._model.options.canvasInnerHeight / this._model.options.canvasOuterHeight) * e.browserEvent.offsetY; const lineIndex = Math.floor(internalOffsetY / minimapLineHeight); let lineNumber = lineIndex + this._lastRenderData.renderedLayout.startLineNumber; - lineNumber = Math.min(lineNumber, this._context.model.getLineCount()); + lineNumber = Math.min(lineNumber, this._model.getLineCount()); - this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( - 'mouse', - new Range(lineNumber, 1, lineNumber, 1), - viewEvents.VerticalRevealType.Center, - false, - ScrollType.Smooth - )); + this._model.revealLineNumber(lineNumber); }); this._sliderMouseMoveMonitor = new GlobalMouseMoveMonitor(); @@ -548,36 +1138,7 @@ export class Minimap extends ViewPart { e.preventDefault(); e.stopPropagation(); if (e.leftButton && this._lastRenderData) { - - const initialMousePosition = e.posy; - const initialMouseOrthogonalPosition = e.posx; - const initialSliderState = this._lastRenderData.renderedLayout; - this._slider.toggleClassName('active', true); - - this._sliderMouseMoveMonitor.startMonitoring( - e.target, - e.buttons, - standardMouseMoveMerger, - (mouseMoveData: IStandardMouseMoveEventData) => { - const mouseOrthogonalDelta = Math.abs(mouseMoveData.posx - initialMouseOrthogonalPosition); - - if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { - // The mouse has wondered away from the scrollbar => reset dragging - this._context.viewLayout.setScrollPositionNow({ - scrollTop: initialSliderState.scrollTop - }); - return; - } - - const mouseDelta = mouseMoveData.posy - initialMousePosition; - this._context.viewLayout.setScrollPositionNow({ - scrollTop: initialSliderState.getDesiredScrollTopFromDelta(mouseDelta) - }); - }, - () => { - this._slider.toggleClassName('active', false); - } - ); + this._startSliderDragging(e.buttons, e.posx, e.posy, e.posy, this._lastRenderData.renderedLayout); } }); @@ -590,15 +1151,15 @@ export class Minimap extends ViewPart { this._gestureInProgress = true; this.scrollDueToTouchEvent(e); } - }); + }, { passive: false }); - this._sliderTouchMoveListener = dom.addStandardDisposableListener(this._domNode.domNode, EventType.Change, (e: GestureEvent) => { + this._sliderTouchMoveListener = dom.addDisposableListener(this._domNode.domNode, EventType.Change, (e: GestureEvent) => { e.preventDefault(); e.stopPropagation(); if (this._lastRenderData && this._gestureInProgress) { this.scrollDueToTouchEvent(e); } - }); + }, { passive: false }); this._sliderTouchEndListener = dom.addStandardDisposableListener(this._domNode.domNode, EventType.End, (e: GestureEvent) => { e.preventDefault(); @@ -608,12 +1169,41 @@ export class Minimap extends ViewPart { }); } + private _startSliderDragging(initialButtons: number, initialPosX: number, initialPosY: number, posy: number, initialSliderState: MinimapLayout): void { + this._slider.toggleClassName('active', true); + + const handleMouseMove = (posy: number, posx: number) => { + const mouseOrthogonalDelta = Math.abs(posx - initialPosX); + + if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { + // The mouse has wondered away from the scrollbar => reset dragging + this._model.setScrollTop(initialSliderState.scrollTop); + return; + } + + const mouseDelta = posy - initialPosY; + this._model.setScrollTop(initialSliderState.getDesiredScrollTopFromDelta(mouseDelta)); + }; + + if (posy !== initialPosY) { + handleMouseMove(posy, initialPosX); + } + + this._sliderMouseMoveMonitor.startMonitoring( + this._slider.domNode, + initialButtons, + standardMouseMoveMerger, + (mouseMoveData: IStandardMouseMoveEventData) => handleMouseMove(mouseMoveData.posy, mouseMoveData.posx), + () => { + this._slider.toggleClassName('active', false); + } + ); + } + private scrollDueToTouchEvent(touch: GestureEvent) { const startY = this._domNode.domNode.getBoundingClientRect().top; const scrollTop = this._lastRenderData!.renderedLayout.getDesiredScrollTopFromTouchLocation(touch.pageY - startY); - this._context.viewLayout.setScrollPositionNow({ - scrollTop: scrollTop - }); + this._model.setScrollTop(scrollTop); } public dispose(): void { @@ -628,7 +1218,7 @@ export class Minimap extends ViewPart { } private _getMinimapDomNodeClassName(): string { - if (this._options.showSlider === 'always') { + if (this._model.options.showSlider === 'always') { return 'minimap slider-always'; } return 'minimap slider-mouseover'; @@ -639,123 +1229,105 @@ export class Minimap extends ViewPart { } private _applyLayout(): void { - this._domNode.setLeft(this._options.minimapLeft); - this._domNode.setWidth(this._options.minimapWidth); - this._domNode.setHeight(this._options.minimapHeight); - this._shadow.setHeight(this._options.minimapHeight); + this._domNode.setLeft(this._model.options.minimapLeft); + this._domNode.setWidth(this._model.options.minimapWidth); + this._domNode.setHeight(this._model.options.minimapHeight); + this._shadow.setHeight(this._model.options.minimapHeight); - this._canvas.setWidth(this._options.canvasOuterWidth); - this._canvas.setHeight(this._options.canvasOuterHeight); - this._canvas.domNode.width = this._options.canvasInnerWidth; - this._canvas.domNode.height = this._options.canvasInnerHeight; + this._canvas.setWidth(this._model.options.canvasOuterWidth); + this._canvas.setHeight(this._model.options.canvasOuterHeight); + this._canvas.domNode.width = this._model.options.canvasInnerWidth; + this._canvas.domNode.height = this._model.options.canvasInnerHeight; - this._decorationsCanvas.setWidth(this._options.canvasOuterWidth); - this._decorationsCanvas.setHeight(this._options.canvasOuterHeight); - this._decorationsCanvas.domNode.width = this._options.canvasInnerWidth; - this._decorationsCanvas.domNode.height = this._options.canvasInnerHeight; + this._decorationsCanvas.setWidth(this._model.options.canvasOuterWidth); + this._decorationsCanvas.setHeight(this._model.options.canvasOuterHeight); + this._decorationsCanvas.domNode.width = this._model.options.canvasInnerWidth; + this._decorationsCanvas.domNode.height = this._model.options.canvasInnerHeight; - this._slider.setWidth(this._options.minimapWidth); + this._slider.setWidth(this._model.options.minimapWidth); } private _getBuffer(): ImageData | null { if (!this._buffers) { - if (this._options.canvasInnerWidth > 0 && this._options.canvasInnerHeight > 0) { + if (this._model.options.canvasInnerWidth > 0 && this._model.options.canvasInnerHeight > 0) { this._buffers = new MinimapBuffers( this._canvas.domNode.getContext('2d')!, - this._options.canvasInnerWidth, - this._options.canvasInnerHeight, - this._tokensColorTracker.getColor(ColorId.DefaultBackground) + this._model.options.canvasInnerWidth, + this._model.options.canvasInnerHeight, + this._model.options.backgroundColor ); } } return this._buffers ? this._buffers.getBuffer() : null; } - private _onOptionsMaybeChanged(): boolean { - const opts = new MinimapOptions(this._context.configuration); - if (this._options.equals(opts)) { - return false; - } - this._options = opts; + // ---- begin view event handlers + + public onDidChangeOptions(): void { this._lastRenderData = null; this._buffers = null; this._applyLayout(); this._domNode.setClassName(this._getMinimapDomNodeClassName()); - return true; } - - // ---- begin view event handlers - - public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - return this._onOptionsMaybeChanged(); - } - public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - this._selections = e.selections; + public onSelectionChanged(): boolean { this._renderDecorations = true; return true; } - public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + public onDecorationsChanged(): boolean { + this._renderDecorations = true; + return true; + } + public onFlushed(): boolean { this._lastRenderData = null; return true; } - public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + public onLinesChanged(changeFromLineNumber: number, changeToLineNumber: number): boolean { if (this._lastRenderData) { - return this._lastRenderData.onLinesChanged(e); + return this._lastRenderData.onLinesChanged(changeFromLineNumber, changeToLineNumber); } return false; } - public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): boolean { if (this._lastRenderData) { - this._lastRenderData.onLinesDeleted(e); + this._lastRenderData.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); } return true; } - public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): boolean { if (this._lastRenderData) { - this._lastRenderData.onLinesInserted(e); + this._lastRenderData.onLinesInserted(insertFromLineNumber, insertToLineNumber); } return true; } - public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + public onScrollChanged(): boolean { this._renderDecorations = true; return true; } - public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + public onThemeChanged(): boolean { + this._selectionColor = this._theme.getColor(minimapSelection); + this._renderDecorations = true; + return true; + } + public onTokensChanged(ranges: { fromLineNumber: number; toLineNumber: number; }[]): boolean { if (this._lastRenderData) { - return this._lastRenderData.onTokensChanged(e); + return this._lastRenderData.onTokensChanged(ranges); } return false; } - public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { + public onTokensColorsChanged(): boolean { this._lastRenderData = null; this._buffers = null; return true; } - public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + public onZonesChanged(): boolean { this._lastRenderData = null; return true; } - public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - this._renderDecorations = true; - return true; - } - - public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { - this._context.model.invalidateMinimapColorCache(); - this._selectionColor = this._context.theme.getColor(minimapSelection); - this._renderDecorations = true; - return true; - } - // --- end event handlers - public prepareRender(ctx: RenderingContext): void { - // Nothing to read - } - - public render(renderingCtx: RestrictedRenderingContext): void { - const renderMinimap = this._options.renderMinimap; + public render(renderingCtx: IMinimapRenderingContext): void { + const renderMinimap = this._model.options.renderMinimap; if (renderMinimap === RenderMinimap.None) { this._shadow.setClassName('minimap-shadow-hidden'); this._sliderHorizontal.setWidth(0); @@ -769,24 +1341,26 @@ export class Minimap extends ViewPart { } const layout = MinimapLayout.create( - this._options, - renderingCtx.visibleRange.startLineNumber, - renderingCtx.visibleRange.endLineNumber, + this._model.options, + renderingCtx.viewportStartLineNumber, + renderingCtx.viewportEndLineNumber, renderingCtx.viewportHeight, - (renderingCtx.viewportData.whitespaceViewportData.length > 0), - this._context.model.getLineCount(), + renderingCtx.viewportContainsWhitespaceGaps, + this._model.getLineCount(), + this._model.getRealLineCount(), renderingCtx.scrollTop, renderingCtx.scrollHeight, this._lastRenderData ? this._lastRenderData.renderedLayout : null ); + this._slider.setDisplay(layout.sliderNeeded ? 'block' : 'none'); this._slider.setTop(layout.sliderTop); this._slider.setHeight(layout.sliderHeight); // Compute horizontal slider coordinates - const scrollLeftChars = renderingCtx.scrollLeft / this._options.typicalHalfwidthCharacterWidth; - const horizontalSliderLeft = Math.min(this._options.minimapWidth, Math.round(scrollLeftChars * getMinimapCharWidth(this._options.renderMinimap, this._options.fontScale) / this._options.pixelRatio)); + const scrollLeftChars = renderingCtx.scrollLeft / this._model.options.typicalHalfwidthCharacterWidth; + const horizontalSliderLeft = Math.min(this._model.options.minimapWidth, Math.round(scrollLeftChars * this._model.options.minimapCharWidth / this._model.options.pixelRatio)); this._sliderHorizontal.setLeft(horizontalSliderLeft); - this._sliderHorizontal.setWidth(this._options.minimapWidth - horizontalSliderLeft); + this._sliderHorizontal.setWidth(this._model.options.minimapWidth - horizontalSliderLeft); this._sliderHorizontal.setTop(0); this._sliderHorizontal.setHeight(layout.sliderHeight); @@ -797,19 +1371,20 @@ export class Minimap extends ViewPart { private renderDecorations(layout: MinimapLayout) { if (this._renderDecorations) { this._renderDecorations = false; - const decorations = this._context.model.getDecorationsInViewport(new Range(layout.startLineNumber, 1, layout.endLineNumber, this._context.model.getLineMaxColumn(layout.endLineNumber))); + const selections = this._model.getSelections(); + const decorations = this._model.getMinimapDecorationsInViewport(layout.startLineNumber, layout.endLineNumber); - const { renderMinimap, canvasInnerWidth, canvasInnerHeight } = this._options; - const lineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); - const characterWidth = getMinimapCharWidth(renderMinimap, this._options.fontScale); - const tabSize = this._context.model.getOptions().tabSize; + const { canvasInnerWidth, canvasInnerHeight } = this._model.options; + const lineHeight = this._model.options.minimapLineHeight; + const characterWidth = this._model.options.minimapCharWidth; + const tabSize = this._model.getOptions().tabSize; const canvasContext = this._decorationsCanvas.domNode.getContext('2d')!; canvasContext.clearRect(0, 0, canvasInnerWidth, canvasInnerHeight); const lineOffsetMap = new Map(); - for (let i = 0; i < this._selections.length; i++) { - const selection = this._selections[i]; + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; for (let line = selection.startLineNumber; line <= selection.endLineNumber; line++) { this.renderDecorationOnLine(canvasContext, lineOffsetMap, selection, this._selectionColor, layout, line, lineHeight, lineHeight, tabSize, characterWidth); @@ -824,7 +1399,7 @@ export class Minimap extends ViewPart { continue; } - const decorationColor = (decoration.options.minimap).getColor(this._context.theme); + const decorationColor = (decoration.options.minimap).getColor(this._theme); for (let line = decoration.range.startLineNumber; line <= decoration.range.endLineNumber; line++) { switch (decoration.options.minimap.position) { @@ -856,7 +1431,7 @@ export class Minimap extends ViewPart { const y = (lineNumber - layout.startLineNumber) * lineHeight; // Skip rendering the line if it's vertically outside our viewport - if (y + height < 0 || y > this._options.canvasOuterHeight) { + if (y + height < 0 || y > this._model.options.canvasInnerHeight) { return; } @@ -864,7 +1439,7 @@ export class Minimap extends ViewPart { let lineIndexToXOffset = lineOffsetMap.get(lineNumber); const isFirstDecorationForLine = !lineIndexToXOffset; if (!lineIndexToXOffset) { - const lineData = this._context.model.getLineContent(lineNumber); + const lineData = this._model.getLineContent(lineNumber); lineIndexToXOffset = [MINIMAP_GUTTER_WIDTH]; for (let i = 1; i < lineData.length + 1; i++) { const charCode = lineData.charCodeAt(i - 1); @@ -909,11 +1484,9 @@ export class Minimap extends ViewPart { } private renderLines(layout: MinimapLayout): RenderData | null { - const renderMinimap = this._options.renderMinimap; - const charRenderer = this._options.charRenderer(); const startLineNumber = layout.startLineNumber; const endLineNumber = layout.endLineNumber; - const minimapLineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); + const minimapLineHeight = this._model.options.minimapLineHeight; // Check if nothing changed w.r.t. lines from last frame if (this._lastRenderData && this._lastRenderData.linesEquals(layout)) { @@ -931,7 +1504,7 @@ export class Minimap extends ViewPart { } // Render untouched lines by using last rendered data. - let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines( + let [_dirtyY1, _dirtyY2, needed] = InnerMinimap._renderUntouchedLines( imageData, startLineNumber, endLineNumber, @@ -940,27 +1513,39 @@ export class Minimap extends ViewPart { ); // Fetch rendering info from view model for rest of lines that need rendering. - const lineInfo = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); - const tabSize = lineInfo.tabSize; - const background = this._tokensColorTracker.getColor(ColorId.DefaultBackground); - const useLighterFont = this._tokensColorTracker.backgroundIsLight(); + const lineInfo = this._model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); + const tabSize = this._model.getOptions().tabSize; + const background = this._model.options.backgroundColor; + const tokensColorTracker = this._model.tokensColorTracker; + const useLighterFont = tokensColorTracker.backgroundIsLight(); + const renderMinimap = this._model.options.renderMinimap; + const charRenderer = this._model.options.charRenderer(); + const fontScale = this._model.options.fontScale; + const minimapCharWidth = this._model.options.minimapCharWidth; + + const baseCharHeight = (renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); + const renderMinimapLineHeight = baseCharHeight * fontScale; + const innerLinePadding = (minimapLineHeight > renderMinimapLineHeight ? Math.floor((minimapLineHeight - renderMinimapLineHeight) / 2) : 0); // Render the rest of lines let dy = 0; const renderedLines: MinimapLine[] = []; for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { if (needed[lineIndex]) { - Minimap._renderLine( + InnerMinimap._renderLine( imageData, background, useLighterFont, renderMinimap, - this._tokensColorTracker, + minimapCharWidth, + tokensColorTracker, charRenderer, dy, + innerLinePadding, tabSize, - lineInfo.data[lineIndex]!, - this._options.fontScale + lineInfo[lineIndex]!, + fontScale, + minimapLineHeight ); } renderedLines[lineIndex] = new MinimapLine(dy); @@ -1080,17 +1665,20 @@ export class Minimap extends ViewPart { backgroundColor: RGBA8, useLighterFont: boolean, renderMinimap: RenderMinimap, + charWidth: number, colorTracker: MinimapTokensColorTracker, minimapCharRenderer: MinimapCharRenderer, dy: number, + innerLinePadding: number, tabSize: number, lineData: ViewLineData, - fontScale: number + fontScale: number, + minimapLineHeight: number ): void { const content = lineData.content; const tokens = lineData.tokens; - const charWidth = getMinimapCharWidth(renderMinimap, fontScale); const maxDx = target.width - charWidth; + const force1pxHeight = (minimapLineHeight === 1); let dx = MINIMAP_GUTTER_WIDTH; let charIndex = 0; @@ -1122,9 +1710,9 @@ export class Minimap extends ViewPart { for (let i = 0; i < count; i++) { if (renderMinimap === RenderMinimap.Blocks) { - minimapCharRenderer.blockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); + minimapCharRenderer.blockRenderChar(target, dx, dy + innerLinePadding, tokenColor, backgroundColor, useLighterFont, force1pxHeight); } else { // RenderMinimap.Text - minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont); + minimapCharRenderer.renderChar(target, dx, dy + innerLinePadding, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, force1pxHeight); } dx += charWidth; @@ -1141,20 +1729,21 @@ export class Minimap extends ViewPart { } registerThemingParticipant((theme, collector) => { - const sliderBackground = theme.getColor(scrollbarSliderBackground); + const minimapBackgroundValue = theme.getColor(minimapBackground); + if (minimapBackgroundValue) { + collector.addRule(`.monaco-editor .minimap > canvas { opacity: ${minimapBackgroundValue.rgba.a}; will-change: opacity; }`); + } + const sliderBackground = theme.getColor(minimapSliderBackground); if (sliderBackground) { - const halfSliderBackground = sliderBackground.transparent(0.5); - collector.addRule(`.monaco-editor .minimap-slider, .monaco-editor .minimap-slider .minimap-slider-horizontal { background: ${halfSliderBackground}; }`); + collector.addRule(`.monaco-editor .minimap-slider .minimap-slider-horizontal { background: ${sliderBackground}; }`); } - const sliderHoverBackground = theme.getColor(scrollbarSliderHoverBackground); + const sliderHoverBackground = theme.getColor(minimapSliderHoverBackground); if (sliderHoverBackground) { - const halfSliderHoverBackground = sliderHoverBackground.transparent(0.5); - collector.addRule(`.monaco-editor .minimap-slider:hover, .monaco-editor .minimap-slider:hover .minimap-slider-horizontal { background: ${halfSliderHoverBackground}; }`); + collector.addRule(`.monaco-editor .minimap-slider:hover .minimap-slider-horizontal { background: ${sliderHoverBackground}; }`); } - const sliderActiveBackground = theme.getColor(scrollbarSliderActiveBackground); + const sliderActiveBackground = theme.getColor(minimapSliderActiveBackground); if (sliderActiveBackground) { - const halfSliderActiveBackground = sliderActiveBackground.transparent(0.5); - collector.addRule(`.monaco-editor .minimap-slider.active, .monaco-editor .minimap-slider.active .minimap-slider-horizontal { background: ${halfSliderActiveBackground}; }`); + collector.addRule(`.monaco-editor .minimap-slider.active .minimap-slider-horizontal { background: ${sliderActiveBackground}; }`); } const shadow = theme.getColor(scrollbarShadow); if (shadow) { diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index cc8a37fcf6b..c30b720c207 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -5,6 +5,7 @@ import { RGBA8 } from 'vs/editor/common/core/rgba'; import { Constants, getCharIndex } from './minimapCharSheet'; +import { toUint8 } from 'vs/base/common/uint'; export class MinimapCharRenderer { _minimapCharRendererBrand: void; @@ -20,7 +21,7 @@ export class MinimapCharRenderer { private static soften(input: Uint8ClampedArray, ratio: number): Uint8ClampedArray { let result = new Uint8ClampedArray(input.length); for (let i = 0, len = input.length; i < len; i++) { - result[i] = input[i] * ratio; + result[i] = toUint8(input[i] * ratio); } return result; } @@ -33,11 +34,13 @@ export class MinimapCharRenderer { color: RGBA8, backgroundColor: RGBA8, fontScale: number, - useLighterFont: boolean + useLighterFont: boolean, + force1pxHeight: boolean ): void { const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; - if (dx + charWidth > target.width || dy + charHeight > target.height) { + const renderHeight = (force1pxHeight ? 1 : charHeight); + if (dx + charWidth > target.width || dy + renderHeight > target.height) { console.warn('bad render request outside image data'); return; } @@ -59,7 +62,7 @@ export class MinimapCharRenderer { let sourceOffset = charIndex * charWidth * charHeight; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; - for (let y = 0; y < charHeight; y++) { + for (let y = 0; y < renderHeight; y++) { let column = row; for (let x = 0; x < charWidth; x++) { const c = charData[sourceOffset++] / 255; @@ -79,11 +82,13 @@ export class MinimapCharRenderer { dy: number, color: RGBA8, backgroundColor: RGBA8, - useLighterFont: boolean + useLighterFont: boolean, + force1pxHeight: boolean ): void { const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; - if (dx + charWidth > target.width || dy + charHeight > target.height) { + const renderHeight = (force1pxHeight ? 1 : charHeight); + if (dx + charWidth > target.width || dy + renderHeight > target.height) { console.warn('bad render request outside image data'); return; } @@ -107,7 +112,7 @@ export class MinimapCharRenderer { const dest = target.data; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; - for (let y = 0; y < charHeight; y++) { + for (let y = 0; y < renderHeight; y++) { let column = row; for (let x = 0; x < charWidth; x++) { dest[column++] = colorR; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts index 585f5d01509..46c6c8e27a9 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts @@ -7,6 +7,7 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimap import { allCharCodes } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { prebakedMiniMaps } from 'vs/editor/browser/viewParts/minimap/minimapPreBaked'; import { Constants } from './minimapCharSheet'; +import { toUint8 } from 'vs/base/common/uint'; /** * Creates character renderers. It takes a 'scale' that determines how large @@ -135,7 +136,7 @@ export class MinimapCharRendererFactory { const final = value / samples; brightest = Math.max(brightest, final); - dest[targetIndex++] = final; + dest[targetIndex++] = toUint8(final); } } diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts index 925ed44d28a..b51be8aaba8 100644 --- a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts @@ -42,7 +42,7 @@ export class ViewOverlayWidgets extends ViewPart { this._widgets = {}; this._verticalScrollbarWidth = layoutInfo.verticalScrollbarWidth; - this._minimapWidth = layoutInfo.minimapWidth; + this._minimapWidth = layoutInfo.minimap.minimapWidth; this._horizontalScrollbarHeight = layoutInfo.horizontalScrollbarHeight; this._editorHeight = layoutInfo.height; this._editorWidth = layoutInfo.width; @@ -68,7 +68,7 @@ export class ViewOverlayWidgets extends ViewPart { const layoutInfo = options.get(EditorOption.layoutInfo); this._verticalScrollbarWidth = layoutInfo.verticalScrollbarWidth; - this._minimapWidth = layoutInfo.minimapWidth; + this._minimapWidth = layoutInfo.minimap.minimapWidth; this._horizontalScrollbarHeight = layoutInfo.horizontalScrollbarHeight; this._editorHeight = layoutInfo.height; this._editorWidth = layoutInfo.width; diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 3e57c3f2f94..287011babea 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -10,11 +10,10 @@ import { ViewPart } from 'vs/editor/browser/view/viewPart'; import { Position } from 'vs/editor/common/core/position'; import { IConfiguration } from 'vs/editor/common/editorCommon'; import { TokenizationRegistry } from 'vs/editor/common/modes'; -import { editorCursorForeground, editorOverviewRulerBorder } from 'vs/editor/common/view/editorColorRegistry'; +import { editorCursorForeground, editorOverviewRulerBorder, editorOverviewRulerBackground } from 'vs/editor/common/view/editorColorRegistry'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ITheme } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; class Settings { @@ -42,7 +41,7 @@ class Settings { public readonly x: number[]; public readonly w: number[]; - constructor(config: IConfiguration, theme: ITheme) { + constructor(config: IConfiguration, theme: EditorTheme) { const options = config.options; this.lineHeight = options.get(EditorOption.lineHeight); this.pixelRatio = options.get(EditorOption.pixelRatio); @@ -61,7 +60,10 @@ class Settings { const minimapOpts = options.get(EditorOption.minimap); const minimapEnabled = minimapOpts.enabled; const minimapSide = minimapOpts.side; - const backgroundColor = (minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null); + const backgroundColor = minimapEnabled + ? theme.getColor(editorOverviewRulerBackground) || TokenizationRegistry.getDefaultBackground() + : null; + if (backgroundColor === null || minimapSide === 'left') { this.backgroundColor = null; } else { @@ -277,7 +279,10 @@ export class DecorationsOverviewRuler extends ViewPart { return true; } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - return true; + if (e.affectsOverviewRuler) { + return true; + } + return false; } public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { return true; diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts index 0f22de06a73..696e088de58 100644 --- a/src/vs/editor/browser/viewParts/rulers/rulers.ts +++ b/src/vs/editor/browser/viewParts/rulers/rulers.ts @@ -11,13 +11,13 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EditorOption, IRulerOption } from 'vs/editor/common/config/editorOptions'; export class Rulers extends ViewPart { public domNode: FastDomNode; private readonly _renderedRulers: FastDomNode[]; - private _rulers: number[]; + private _rulers: IRulerOption[]; private _typicalHalfwidthCharacterWidth: number; constructor(context: ViewContext) { @@ -64,7 +64,7 @@ export class Rulers extends ViewPart { } if (currentCount < desiredCount) { - const { tabSize } = this._context.model.getOptions(); + const { tabSize } = this._context.model.getTextModelOptions(); const rulerWidth = tabSize; let addCount = desiredCount - currentCount; while (addCount > 0) { @@ -92,9 +92,11 @@ export class Rulers extends ViewPart { for (let i = 0, len = this._rulers.length; i < len; i++) { const node = this._renderedRulers[i]; + const ruler = this._rulers[i]; + node.setBoxShadow(ruler.color ? `1px 0 0 0 ${ruler.color} inset` : ``); node.setHeight(Math.min(ctx.scrollHeight, 1000000)); - node.setLeft(this._rulers[i] * this._typicalHalfwidthCharacterWidth); + node.setLeft(ruler.column * this._typicalHalfwidthCharacterWidth); } } } diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts index 8adefd837dd..12e4df8b465 100644 --- a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts @@ -58,10 +58,10 @@ export class ScrollDecorationViewPart extends ViewPart { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); - if (layoutInfo.renderMinimap === 0 || (layoutInfo.minimapWidth > 0 && layoutInfo.minimapLeft === 0)) { + if (layoutInfo.minimap.renderMinimap === 0 || (layoutInfo.minimap.minimapWidth > 0 && layoutInfo.minimap.minimapLeft === 0)) { this._width = layoutInfo.width; } else { - this._width = layoutInfo.width - layoutInfo.minimapWidth - layoutInfo.verticalScrollbarWidth; + this._width = layoutInfo.width - layoutInfo.minimap.minimapWidth - layoutInfo.verticalScrollbarWidth; } } diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index d50b0f5679b..cdb00570ce0 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -60,7 +60,7 @@ function toStyled(item: LineVisibleRanges): LineVisibleRangesWithStyle { // TODO@Alex: Remove this once IE11 fixes Bug #524217 // The problem in IE11 is that it does some sort of auto-zooming to accomodate for displays with different pixel density. // Unfortunately, this auto-zooming is buggy around dealing with rounded borders -const isIEWithZoomingIssuesNearRoundedBorders = browser.isEdgeOrIE; +const isIEWithZoomingIssuesNearRoundedBorders = browser.isEdge; export class SelectionsOverlay extends DynamicViewOverlay { @@ -217,7 +217,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { endStyle.top = CornerStyle.INTERN; } } else if (previousFrameTop) { - // Accept some hick-ups near the viewport edges to save on repaints + // Accept some hiccups near the viewport edges to save on repaints startStyle.top = previousFrameTop.startStyle!.top; endStyle.top = previousFrameTop.endStyle!.top; } @@ -239,7 +239,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { endStyle.bottom = CornerStyle.INTERN; } } else if (previousFrameBottom) { - // Accept some hick-ups near the viewport edges to save on repaints + // Accept some hiccups near the viewport edges to save on repaints startStyle.bottom = previousFrameBottom.startStyle!.bottom; endStyle.bottom = previousFrameBottom.endStyle!.bottom; } @@ -419,7 +419,7 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .selected-text { background-color: ${editorInactiveSelectionColor}; }`); } const editorSelectionForegroundColor = theme.getColor(editorSelectionForeground); - if (editorSelectionForegroundColor) { + if (editorSelectionForegroundColor && !editorSelectionForegroundColor.isTransparent()) { collector.addRule(`.monaco-editor .view-line span.inline-selected-text { color: ${editorSelectionForegroundColor}; }`); } }); diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index 6173ba7a6fa..7456eb657ea 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -13,6 +13,7 @@ import { Range } from 'vs/editor/common/core/range'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; export interface IViewCursorRenderData { domNode: HTMLElement; @@ -63,7 +64,7 @@ export class ViewCursor { // Create the dom node this._domNode = createFastDomNode(document.createElement('div')); - this._domNode.setClassName('cursor'); + this._domNode.setClassName(`cursor ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); this._domNode.setHeight(this._lineHeight); this._domNode.setTop(0); this._domNode.setLeft(0); @@ -200,7 +201,7 @@ export class ViewCursor { this._domNode.domNode.textContent = this._lastRenderedContent; } - this._domNode.setClassName('cursor ' + this._renderData.textContentClassName); + this._domNode.setClassName(`cursor ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME} ${this._renderData.textContentClassName}`); this._domNode.setDisplay('block'); this._domNode.setTop(this._renderData.top); diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css index 59b084e5922..484b79befe0 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -9,7 +9,6 @@ .monaco-editor .cursors-layer > .cursor { position: absolute; - cursor: text; overflow: hidden; } diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index a703cbafd2b..ef3f6e301b4 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -358,7 +358,7 @@ registerThemingParticipant((theme, collector) => { if (!caretBackground) { caretBackground = caret.opposite(); } - collector.addRule(`.monaco-editor .cursor { background-color: ${caret}; border-color: ${caret}; color: ${caretBackground}; }`); + collector.addRule(`.monaco-editor .cursors-layer .cursor { background-color: ${caret}; border-color: ${caret}; color: ${caretBackground}; }`); if (theme.type === 'hc') { collector.addRule(`.monaco-editor .cursors-layer.has-selection .cursor { border-left: 1px solid ${caretBackground}; border-right: 1px solid ${caretBackground}; }`); } diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index 5e28b7785ec..a73026a3f9b 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -79,9 +79,8 @@ export class ViewZones extends ViewPart { for (const whitespace of whitespaces) { oldWhitespaces.set(whitespace.id, whitespace); } - return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { - let hadAChange = false; - + let hadAChange = false; + this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { const keys = Object.keys(this._zones); for (let i = 0, len = keys.length; i < len; i++) { const id = keys[i]; @@ -94,9 +93,8 @@ export class ViewZones extends ViewPart { hadAChange = true; } } - - return hadAChange; }); + return hadAChange; } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { @@ -115,11 +113,7 @@ export class ViewZones extends ViewPart { } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { - const hadAChange = this._recomputeWhitespacesProps(); - if (hadAChange) { - this._context.viewLayout.onHeightMaybeChanged(); - } - return hadAChange; + return this._recomputeWhitespacesProps(); } public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { @@ -199,9 +193,9 @@ export class ViewZones extends ViewPart { } public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { + let zonesHaveChanged = false; - return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { - let zonesHaveChanged = false; + this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { const changeAccessor: IViewZoneChangeAccessor = { addZone: (zone: IViewZone): string => { @@ -228,9 +222,9 @@ export class ViewZones extends ViewPart { changeAccessor.addZone = invalidFunc; changeAccessor.removeZone = invalidFunc; changeAccessor.layoutZone = invalidFunc; - - return zonesHaveChanged; }); + + return zonesHaveChanged; } private _addZone(whitespaceAccessor: IWhitespaceChangeAccessor, zone: IViewZone): string { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index f690983fb77..17539558c91 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -15,16 +15,15 @@ import { hash } from 'vs/base/common/hash'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { Configuration } from 'vs/editor/browser/config/configuration'; -import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICommandDelegate } from 'vs/editor/browser/view/viewController'; import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; -import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; -import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; -import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon'; +import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents'; +import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -32,7 +31,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model'; +import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, ICursorStateComputer, IWordAtPosition } from 'vs/editor/common/model'; import { ClassName } from 'vs/editor/common/model/intervalTree'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; @@ -52,6 +51,9 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { withNullAsUndefined } from 'vs/base/common/types'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer'; +import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; let EDITOR_ID = 0; @@ -78,15 +80,13 @@ export interface ICodeEditorWidgetOptions { class ModelData { public readonly model: ITextModel; public readonly viewModel: ViewModel; - public readonly cursor: Cursor; public readonly view: View; public readonly hasRealView: boolean; public readonly listenersToRemove: IDisposable[]; - constructor(model: ITextModel, viewModel: ViewModel, cursor: Cursor, view: View, hasRealView: boolean, listenersToRemove: IDisposable[]) { + constructor(model: ITextModel, viewModel: ViewModel, view: View, hasRealView: boolean, listenersToRemove: IDisposable[]) { this.model = model; this.viewModel = viewModel; - this.cursor = cursor; this.view = view; this.hasRealView = hasRealView; this.listenersToRemove = listenersToRemove; @@ -98,7 +98,6 @@ class ModelData { if (this.hasRealView) { this.view.dispose(); } - this.cursor.dispose(); this.viewModel.dispose(); } } @@ -209,6 +208,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _telemetryData?: object; private readonly _domElement: HTMLElement; + private readonly _overflowWidgetsDomNode: HTMLElement | undefined; private readonly _id: number; private readonly _configuration: editorCommon.IConfiguration; @@ -238,7 +238,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE constructor( domElement: HTMLElement, - options: IEditorConstructionOptions, + options: editorBrowser.IEditorConstructionOptions, codeEditorWidgetOptions: ICodeEditorWidgetOptions, @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @@ -249,14 +249,17 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE @IAccessibilityService accessibilityService: IAccessibilityService ) { super(); + + options = options || {}; + this._domElement = domElement; + this._overflowWidgetsDomNode = options.overflowWidgetsDomNode; this._id = (++EDITOR_ID); this._decorationTypeKeysToIds = {}; this._decorationTypeSubtypes = {}; this.isSimpleWidget = codeEditorWidgetOptions.isSimpleWidget || false; this._telemetryData = codeEditorWidgetOptions.telemetryData; - options = options || {}; this._configuration = this._register(this._createConfiguration(options, accessibilityService)); this._register(this._configuration.onDidChange((e) => { this._onDidChangeConfiguration.fire(e); @@ -325,7 +328,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._codeEditorService.addCodeEditor(this); } - protected _createConfiguration(options: IEditorConstructionOptions, accessibilityService: IAccessibilityService): editorCommon.IConfiguration { + protected _createConfiguration(options: editorBrowser.IEditorConstructionOptions, accessibilityService: IAccessibilityService): editorCommon.IConfiguration { return new Configuration(this.isSimpleWidget, options, this._domElement, accessibilityService); } @@ -376,6 +379,17 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._configuration.getRawOptions(); } + public getOverflowWidgetsDomNode(): HTMLElement | undefined { + return this._overflowWidgetsDomNode; + } + + public getConfiguredWordAtPosition(position: Position): IWordAtPosition | null { + if (!this._modelData) { + return null; + } + return WordOperations.getWordAtPosition(this._modelData.model, this._configuration.options.get(EditorOption.wordSeparators), position); + } + public getValue(options: { preserveBOM: boolean; lineEnding: string; } | null = null): string { if (!this._modelData) { return ''; @@ -452,6 +466,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.viewModel.getVisibleRanges(); } + public getVisibleRangesPlusViewportAboveBelow(): Range[] { + if (!this._modelData) { + return []; + } + return this._modelData.viewModel.getVisibleRangesPlusViewportAboveBelow(); + } + public getWhitespaces(): IEditorWhitespace[] { if (!this._modelData) { return []; @@ -514,7 +535,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return null; } - return this._modelData.cursor.getPosition(); + return this._modelData.viewModel.getPosition(); } public setPosition(position: IPosition): void { @@ -524,7 +545,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!Position.isIPosition(position)) { throw new Error('Invalid arguments'); } - this._modelData.cursor.setSelections('api', [{ + this._modelData.viewModel.setSelections('api', [{ selectionStartLineNumber: position.lineNumber, selectionStartColumn: position.column, positionLineNumber: position.lineNumber, @@ -542,7 +563,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE const validatedModelRange = this._modelData.model.validateRange(modelRange); const viewRange = this._modelData.viewModel.coordinatesConverter.convertModelRangeToViewRange(validatedModelRange); - this._modelData.cursor.emitCursorRevealRange('api', viewRange, verticalType, revealHorizontal, scrollType); + this._modelData.viewModel.revealRange('api', revealHorizontal, viewRange, verticalType, scrollType); } public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { @@ -557,6 +578,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._revealLine(lineNumber, VerticalRevealType.CenterIfOutsideViewport, scrollType); } + public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLine(lineNumber, VerticalRevealType.NearTop, scrollType); + } + private _revealLine(lineNumber: number, revealType: VerticalRevealType, scrollType: editorCommon.ScrollType): void { if (typeof lineNumber !== 'number') { throw new Error('Invalid arguments'); @@ -597,6 +622,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE ); } + public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealPosition( + position, + VerticalRevealType.NearTop, + true, + scrollType + ); + } + private _revealPosition(position: IPosition, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { if (!Position.isIPosition(position)) { throw new Error('Invalid arguments'); @@ -614,14 +648,14 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return null; } - return this._modelData.cursor.getSelection(); + return this._modelData.viewModel.getSelection(); } public getSelections(): Selection[] | null { if (!this._modelData) { return null; } - return this._modelData.cursor.getSelections(); + return this._modelData.viewModel.getSelections(); } public setSelection(range: IRange): void; @@ -655,7 +689,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return; } const selection = new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); - this._modelData.cursor.setSelections('api', [selection]); + this._modelData.viewModel.setSelections('api', [selection]); } public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { @@ -685,6 +719,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE ); } + public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLines( + startLineNumber, + endLineNumber, + VerticalRevealType.NearTop, + scrollType + ); + } + private _revealLines(startLineNumber: number, endLineNumber: number, verticalType: VerticalRevealType, scrollType: editorCommon.ScrollType): void { if (typeof startLineNumber !== 'number' || typeof endLineNumber !== 'number') { throw new Error('Invalid arguments'); @@ -725,6 +768,24 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE ); } + public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealRange( + range, + VerticalRevealType.NearTop, + true, + scrollType + ); + } + + public revealRangeNearTopIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealRange( + range, + VerticalRevealType.NearTopIfOutsideViewport, + true, + scrollType + ); + } + public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { this._revealRange( range, @@ -759,7 +820,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE throw new Error('Invalid arguments'); } } - this._modelData.cursor.setSelections(source, ranges); + this._modelData.viewModel.setSelections(source, ranges); } public getContentWidth(): number { @@ -802,33 +863,33 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.viewModel.viewLayout.getCurrentScrollTop(); } - public setScrollLeft(newScrollLeft: number): void { + public setScrollLeft(newScrollLeft: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Immediate): void { if (!this._modelData) { return; } if (typeof newScrollLeft !== 'number') { throw new Error('Invalid arguments'); } - this._modelData.viewModel.viewLayout.setScrollPositionNow({ + this._modelData.viewModel.setScrollPosition({ scrollLeft: newScrollLeft - }); + }, scrollType); } - public setScrollTop(newScrollTop: number): void { + public setScrollTop(newScrollTop: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Immediate): void { if (!this._modelData) { return; } if (typeof newScrollTop !== 'number') { throw new Error('Invalid arguments'); } - this._modelData.viewModel.viewLayout.setScrollPositionNow({ + this._modelData.viewModel.setScrollPosition({ scrollTop: newScrollTop - }); + }, scrollType); } - public setScrollPosition(position: editorCommon.INewScrollPosition): void { + public setScrollPosition(position: editorCommon.INewScrollPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Immediate): void { if (!this._modelData) { return; } - this._modelData.viewModel.viewLayout.setScrollPositionNow(position); + this._modelData.viewModel.setScrollPosition(position, scrollType); } public saveViewState(): editorCommon.ICodeEditorViewState | null { @@ -845,7 +906,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } } - const cursorState = this._modelData.cursor.saveState(); + const cursorState = this._modelData.viewModel.saveCursorState(); const viewState = this._modelData.viewModel.saveState(); return { cursorState: cursorState, @@ -862,10 +923,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (codeEditorState && codeEditorState.cursorState && codeEditorState.viewState) { const cursorState = codeEditorState.cursorState; if (Array.isArray(cursorState)) { - this._modelData.cursor.restoreState(cursorState); + this._modelData.viewModel.restoreCursorState(cursorState); } else { // Backwards compatibility - this._modelData.cursor.restoreState([cursorState]); + this._modelData.viewModel.restoreCursorState([cursorState]); } const contributionsState = codeEditorState.contributionsState || {}; @@ -920,43 +981,34 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._actions[id] || null; } - public trigger(source: string, handlerId: string, payload: any): void { + public trigger(source: string | null | undefined, handlerId: string, payload: any): void { payload = payload || {}; - // Special case for typing - if (handlerId === editorCommon.Handler.Type) { - if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) { - // nothing to do + switch (handlerId) { + case editorCommon.Handler.CompositionStart: + this._startComposition(); + return; + case editorCommon.Handler.CompositionEnd: + this._endComposition(source); + return; + case editorCommon.Handler.Type: { + const args = >payload; + this._type(source, args.text || ''); return; } - if (source === 'keyboard') { - this._onWillType.fire(payload.text); - } - this._modelData.cursor.trigger(source, handlerId, payload); - if (source === 'keyboard') { - this._onDidType.fire(payload.text); - } - return; - } - - // Special case for pasting - if (handlerId === editorCommon.Handler.Paste) { - if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) { - // nothing to do + case editorCommon.Handler.ReplacePreviousChar: { + const args = >payload; + this._replacePreviousChar(source, args.text || '', args.replaceCharCnt || 0); return; } - const startPosition = this._modelData.cursor.getSelection().getStartPosition(); - this._modelData.cursor.trigger(source, handlerId, payload); - const endPosition = this._modelData.cursor.getSelection().getStartPosition(); - if (source === 'keyboard') { - this._onDidPaste.fire( - { - range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), - mode: payload.mode - } - ); + case editorCommon.Handler.Paste: { + const args = >payload; + this._paste(source, args.text || '', args.pasteOnNewLine || false, args.multicursorText || null, args.mode || null); + return; } - return; + case editorCommon.Handler.Cut: + this._cut(source); + return; } const action = this.getAction(handlerId); @@ -972,18 +1024,67 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (this._triggerEditorCommand(source, handlerId, payload)) { return; } + } - this._modelData.cursor.trigger(source, handlerId, payload); - - if (handlerId === editorCommon.Handler.CompositionStart) { - this._onDidCompositionStart.fire(); + private _startComposition(): void { + if (!this._modelData) { + return; } - if (handlerId === editorCommon.Handler.CompositionEnd) { - this._onDidCompositionEnd.fire(); + this._modelData.viewModel.startComposition(); + this._onDidCompositionStart.fire(); + } + + private _endComposition(source: string | null | undefined): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.endComposition(source); + this._onDidCompositionEnd.fire(); + } + + private _type(source: string | null | undefined, text: string): void { + if (!this._modelData || text.length === 0) { + return; + } + if (source === 'keyboard') { + this._onWillType.fire(text); + } + this._modelData.viewModel.type(text, source); + if (source === 'keyboard') { + this._onDidType.fire(text); } } - private _triggerEditorCommand(source: string, handlerId: string, payload: any): boolean { + private _replacePreviousChar(source: string | null | undefined, text: string, replaceCharCnt: number): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.replacePreviousChar(text, replaceCharCnt, source); + } + + private _paste(source: string | null | undefined, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void { + if (!this._modelData || text.length === 0) { + return; + } + const startPosition = this._modelData.viewModel.getSelection().getStartPosition(); + this._modelData.viewModel.paste(text, pasteOnNewLine, multicursorText, source); + const endPosition = this._modelData.viewModel.getSelection().getStartPosition(); + if (source === 'keyboard') { + this._onDidPaste.fire({ + range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), + mode: mode + }); + } + } + + private _cut(source: string | null | undefined): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.cut(source); + } + + private _triggerEditorCommand(source: string | null | undefined, handlerId: string, payload: any): boolean { const command = EditorExtensionsRegistry.getEditorCommand(handlerId); if (command) { payload = payload || {}; @@ -997,11 +1098,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return false; } - public _getCursors(): ICursors | null { + public _getViewModel(): IViewModel | null { if (!this._modelData) { return null; } - return this._modelData.cursor; + return this._modelData.viewModel; } public pushUndoStop(): boolean { @@ -1016,7 +1117,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return true; } - public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean { + public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean { if (!this._modelData) { return false; } @@ -1034,22 +1135,22 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE cursorStateComputer = endCursorState; } - this._modelData.cursor.executeEdits(source, edits, cursorStateComputer); + this._modelData.viewModel.executeEdits(source, edits, cursorStateComputer); return true; } - public executeCommand(source: string, command: editorCommon.ICommand): void { + public executeCommand(source: string | null | undefined, command: editorCommon.ICommand): void { if (!this._modelData) { return; } - this._modelData.cursor.trigger(source, editorCommon.Handler.ExecuteCommand, command); + this._modelData.viewModel.executeCommand(command, source); } - public executeCommands(source: string, commands: editorCommon.ICommand[]): void { + public executeCommands(source: string | null | undefined, commands: editorCommon.ICommand[]): void { if (!this._modelData) { return; } - this._modelData.cursor.trigger(source, editorCommon.Handler.ExecuteCommands, commands); + this._modelData.viewModel.executeCommands(commands, source); } public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { @@ -1291,10 +1392,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData || !this._modelData.hasRealView) { return; } - const hasChanges = this._modelData.view.change(callback); - if (hasChanges) { - this._onDidChangeViewZones.fire(); - } + this._modelData.view.change(callback); } public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null { @@ -1382,43 +1480,59 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE // Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model listenersToRemove.push(model.onWillDispose(() => this.setModel(null))); - const cursor = new Cursor(this._configuration, model, viewModel); + listenersToRemove.push(viewModel.onEvent((e) => { + switch (e.kind) { + case OutgoingViewModelEventKind.ContentSizeChanged: + this._onDidContentSizeChange.fire(e); + break; + case OutgoingViewModelEventKind.FocusChanged: + this._editorTextFocus.setValue(e.hasFocus); + break; + case OutgoingViewModelEventKind.ScrollChanged: + this._onDidScrollChange.fire(e); + break; + case OutgoingViewModelEventKind.ViewZonesChanged: + this._onDidChangeViewZones.fire(); + break; + case OutgoingViewModelEventKind.ReadOnlyEditAttempt: + this._onDidAttemptReadOnlyEdit.fire(); + break; + case OutgoingViewModelEventKind.CursorStateChanged: { + if (e.reachedMaxCursorCount) { + this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT)); + } - listenersToRemove.push(cursor.onDidReachMaxCursorCount(() => { - this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT)); - })); + const positions: Position[] = []; + for (let i = 0, len = e.selections.length; i < len; i++) { + positions[i] = e.selections[i].getPosition(); + } - listenersToRemove.push(cursor.onDidAttemptReadOnlyEdit(() => { - this._onDidAttemptReadOnlyEdit.fire(undefined); - })); + const e1: ICursorPositionChangedEvent = { + position: positions[0], + secondaryPositions: positions.slice(1), + reason: e.reason, + source: e.source + }; + this._onDidChangeCursorPosition.fire(e1); + + const e2: ICursorSelectionChangedEvent = { + selection: e.selections[0], + secondarySelections: e.selections.slice(1), + modelVersionId: e.modelVersionId, + oldSelections: e.oldSelections, + oldModelVersionId: e.oldModelVersionId, + source: e.source, + reason: e.reason + }; + this._onDidChangeCursorSelection.fire(e2); + + break; + } - listenersToRemove.push(cursor.onDidChange((e: CursorStateChangedEvent) => { - const positions: Position[] = []; - for (let i = 0, len = e.selections.length; i < len; i++) { - positions[i] = e.selections[i].getPosition(); } - - const e1: ICursorPositionChangedEvent = { - position: positions[0], - secondaryPositions: positions.slice(1), - reason: e.reason, - source: e.source - }; - this._onDidChangeCursorPosition.fire(e1); - - const e2: ICursorSelectionChangedEvent = { - selection: e.selections[0], - secondarySelections: e.selections.slice(1), - modelVersionId: e.modelVersionId, - oldSelections: e.oldSelections, - oldModelVersionId: e.oldModelVersionId, - source: e.source, - reason: e.reason - }; - this._onDidChangeCursorSelection.fire(e2); })); - const [view, hasRealView] = this._createView(viewModel, cursor); + const [view, hasRealView] = this._createView(viewModel); if (hasRealView) { this._domElement.appendChild(view.domNode.domNode); @@ -1438,94 +1552,77 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE view.domNode.domNode.setAttribute('data-uri', model.uri.toString()); } - this._modelData = new ModelData(model, viewModel, cursor, view, hasRealView, listenersToRemove); + this._modelData = new ModelData(model, viewModel, view, hasRealView, listenersToRemove); } - protected _createView(viewModel: ViewModel, cursor: Cursor): [View, boolean] { + protected _createView(viewModel: ViewModel): [View, boolean] { let commandDelegate: ICommandDelegate; if (this.isSimpleWidget) { commandDelegate = { - executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => { - editorCommand.runCoreEditorCommand(cursor, args); + paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { + this._paste('keyboard', text, pasteOnNewLine, multicursorText, mode); }, - paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { - this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText, mode }); + type: (text: string) => { + this._type('keyboard', text); }, - type: (source: string, text: string) => { - this.trigger(source, editorCommon.Handler.Type, { text }); + replacePreviousChar: (text: string, replaceCharCnt: number) => { + this._replacePreviousChar('keyboard', text, replaceCharCnt); }, - replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this.trigger(source, editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt }); + startComposition: () => { + this._startComposition(); }, - compositionStart: (source: string) => { - this.trigger(source, editorCommon.Handler.CompositionStart, undefined); + endComposition: () => { + this._endComposition('keyboard'); }, - compositionEnd: (source: string) => { - this.trigger(source, editorCommon.Handler.CompositionEnd, undefined); - }, - cut: (source: string) => { - this.trigger(source, editorCommon.Handler.Cut, undefined); + cut: () => { + this._cut('keyboard'); } }; } else { commandDelegate = { - executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => { - editorCommand.runCoreEditorCommand(cursor, args); + paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { + const payload: editorCommon.PastePayload = { text, pasteOnNewLine, multicursorText, mode }; + this._commandService.executeCommand(editorCommon.Handler.Paste, payload); }, - paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { - this._commandService.executeCommand(editorCommon.Handler.Paste, { - text: text, - pasteOnNewLine: pasteOnNewLine, - multicursorText: multicursorText, - mode - }); + type: (text: string) => { + const payload: editorCommon.TypePayload = { text }; + this._commandService.executeCommand(editorCommon.Handler.Type, payload); }, - type: (source: string, text: string) => { - this._commandService.executeCommand(editorCommon.Handler.Type, { - text: text - }); + replacePreviousChar: (text: string, replaceCharCnt: number) => { + const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt }; + this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload); }, - replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, { - text: text, - replaceCharCnt: replaceCharCnt - }); - }, - compositionStart: (source: string) => { + startComposition: () => { this._commandService.executeCommand(editorCommon.Handler.CompositionStart, {}); }, - compositionEnd: (source: string) => { + endComposition: () => { this._commandService.executeCommand(editorCommon.Handler.CompositionEnd, {}); }, - cut: (source: string) => { + cut: () => { this._commandService.executeCommand(editorCommon.Handler.Cut, {}); } }; } - const viewOutgoingEvents = new ViewOutgoingEvents(viewModel); - viewOutgoingEvents.onDidContentSizeChange = (e) => this._onDidContentSizeChange.fire(e); - viewOutgoingEvents.onDidScroll = (e) => this._onDidScrollChange.fire(e); - viewOutgoingEvents.onDidGainFocus = () => this._editorTextFocus.setValue(true); - viewOutgoingEvents.onDidLoseFocus = () => this._editorTextFocus.setValue(false); - viewOutgoingEvents.onContextMenu = (e) => this._onContextMenu.fire(e); - viewOutgoingEvents.onMouseDown = (e) => this._onMouseDown.fire(e); - viewOutgoingEvents.onMouseUp = (e) => this._onMouseUp.fire(e); - viewOutgoingEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e); - viewOutgoingEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e); - viewOutgoingEvents.onKeyUp = (e) => this._onKeyUp.fire(e); - viewOutgoingEvents.onMouseMove = (e) => this._onMouseMove.fire(e); - viewOutgoingEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e); - viewOutgoingEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e); - viewOutgoingEvents.onKeyDown = (e) => this._onKeyDown.fire(e); + const viewUserInputEvents = new ViewUserInputEvents(viewModel.coordinatesConverter); + viewUserInputEvents.onKeyDown = (e) => this._onKeyDown.fire(e); + viewUserInputEvents.onKeyUp = (e) => this._onKeyUp.fire(e); + viewUserInputEvents.onContextMenu = (e) => this._onContextMenu.fire(e); + viewUserInputEvents.onMouseMove = (e) => this._onMouseMove.fire(e); + viewUserInputEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e); + viewUserInputEvents.onMouseDown = (e) => this._onMouseDown.fire(e); + viewUserInputEvents.onMouseUp = (e) => this._onMouseUp.fire(e); + viewUserInputEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e); + viewUserInputEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e); + viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e); const view = new View( commandDelegate, this._configuration, this._themeService, viewModel, - cursor, - viewOutgoingEvents + viewUserInputEvents, + this._overflowWidgetsDomNode ); return [view, true]; @@ -1548,7 +1645,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData = null; this._domElement.removeAttribute('data-mode-id'); - if (removeDomNode) { + if (removeDomNode && this._domElement.contains(removeDomNode)) { this._domElement.removeChild(removeDomNode); } @@ -1613,11 +1710,13 @@ export class BooleanEventEmitter extends Disposable { class EditorContextKeysManager extends Disposable { private readonly _editor: CodeEditorWidget; + private readonly _editorSimpleInput: IContextKey; private readonly _editorFocus: IContextKey; private readonly _textInputFocus: IContextKey; private readonly _editorTextFocus: IContextKey; private readonly _editorTabMovesFocus: IContextKey; private readonly _editorReadonly: IContextKey; + private readonly _editorColumnSelection: IContextKey; private readonly _hasMultipleSelections: IContextKey; private readonly _hasNonEmptySelection: IContextKey; private readonly _canUndo: IContextKey; @@ -1632,11 +1731,14 @@ class EditorContextKeysManager extends Disposable { this._editor = editor; contextKeyService.createKey('editorId', editor.getId()); + + this._editorSimpleInput = EditorContextKeys.editorSimpleInput.bindTo(contextKeyService); this._editorFocus = EditorContextKeys.focus.bindTo(contextKeyService); this._textInputFocus = EditorContextKeys.textInputFocus.bindTo(contextKeyService); this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService); this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); + this._editorColumnSelection = EditorContextKeys.columnSelection.bindTo(contextKeyService); this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService); this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService); this._canUndo = EditorContextKeys.canUndo.bindTo(contextKeyService); @@ -1655,6 +1757,8 @@ class EditorContextKeysManager extends Disposable { this._updateFromSelection(); this._updateFromFocus(); this._updateFromModel(); + + this._editorSimpleInput.set(this._editor.isSimpleWidget); } private _updateFromConfig(): void { @@ -1662,6 +1766,7 @@ class EditorContextKeysManager extends Disposable { this._editorTabMovesFocus.set(options.get(EditorOption.tabFocusMode)); this._editorReadonly.set(options.get(EditorOption.readOnly)); + this._editorColumnSelection.set(options.get(EditorOption.columnSelection)); } private _updateFromSelection(): void { @@ -1734,7 +1839,7 @@ export class EditorModeContext extends Disposable { this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(_contextKeyService); this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(_contextKeyService); this._hasMultipleDocumentSelectionFormattingProvider = EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider.bindTo(_contextKeyService); - this._isInWalkThrough = EditorContextKeys.isInEmbeddedEditor.bindTo(_contextKeyService); + this._isInWalkThrough = EditorContextKeys.isInWalkThroughSnippet.bindTo(_contextKeyService); const update = () => this._update(); @@ -1915,5 +2020,5 @@ registerThemingParticipant((theme, collector) => { } const deprecatedForeground = theme.getColor(editorForeground) || 'inherit'; - collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: line-through; text-decoration-color: ${deprecatedForeground}}`); + collector.addRule(`.monaco-editor.showDeprecated .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: line-through; text-decoration-color: ${deprecatedForeground}}`); }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index fe47a578263..ada211784bf 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -7,8 +7,8 @@ import 'vs/css!./media/diffEditor'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; -import { RunOnceScheduler, IntervalTimer } from 'vs/base/common/async'; +import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState, Orientation } from 'vs/base/browser/ui/sash/sash'; +import { RunOnceScheduler } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -38,8 +38,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffDiagonalFill } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -47,6 +47,9 @@ import { Constants } from 'vs/base/common/uint'; import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; @@ -67,10 +70,10 @@ interface IEditorsZones { modified: IMyViewZone[]; } -interface IDiffEditorWidgetStyle { +export interface IDiffEditorWidgetStyle { getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; - applyColors(theme: ITheme): boolean; + applyColors(theme: IColorTheme): boolean; layout(): number; dispose(): void; } @@ -83,7 +86,7 @@ class VisualEditorState { constructor( private _contextMenuService: IContextMenuService, - private _clipboardService: IClipboardService | null + private _clipboardService: IClipboardService ) { this._zones = []; this.inlineDiffMargins = []; @@ -133,7 +136,7 @@ class VisualEditorState { this._zones.push(zoneId); this._zonesMap[String(zoneId)] = true; - if (newDecorations.zones[i].diff && viewZone.marginDomNode && this._clipboardService) { + if (newDecorations.zones[i].diff && viewZone.marginDomNode) { viewZone.suppressMouseDown = false; this.inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); } @@ -156,6 +159,10 @@ class VisualEditorState { let DIFF_EDITOR_ID = 0; + +const diffInsertIcon = registerIcon('diff-insert', Codicon.add); +const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove); + export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor { private static readonly ONE_OVERVIEW_WIDTH = 15; @@ -168,6 +175,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _onDidUpdateDiff: Emitter = this._register(new Emitter()); public readonly onDidUpdateDiff: Event = this._onDidUpdateDiff.event; + private readonly _onDidContentSizeChange: Emitter = this._register(new Emitter()); + public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; + private readonly id: number; private _state: editorBrowser.DiffEditorState; private _updatingDiffProgress: IProgressRunner | null; @@ -177,10 +187,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _overviewDomElement: HTMLElement; private readonly _overviewViewportDomElement: FastDomNode; - private _width: number; - private _height: number; - private _reviewHeight: number; - private readonly _measureDomElementToken: IntervalTimer | null; + private readonly _elementSizeObserver: ElementSizeObserver; private readonly originalEditor: CodeEditorWidget; private readonly _originalDomNode: HTMLElement; @@ -202,6 +209,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _ignoreTrimWhitespace: boolean; private _originalIsEditable: boolean; + private _originalCodeLens: boolean; + private _modifiedCodeLens: boolean; private _renderSideBySide: boolean; private _maxComputationTime: number; @@ -221,8 +230,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE constructor( domElement: HTMLElement, - options: IDiffEditorOptions, - clipboardService: IClipboardService | null, + options: editorBrowser.IDiffEditorConstructionOptions, + @IClipboardService clipboardService: IClipboardService, @IEditorWorkerService editorWorkerService: IEditorWorkerService, @IContextKeyService contextKeyService: IContextKeyService, @IInstantiationService instantiationService: IInstantiationService, @@ -277,10 +286,20 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._originalIsEditable = Boolean(options.originalEditable); } + this._originalCodeLens = false; + if (typeof options.originalCodeLens !== 'undefined') { + this._originalCodeLens = Boolean(options.originalCodeLens); + } + + this._modifiedCodeLens = false; + if (typeof options.modifiedCodeLens !== 'undefined') { + this._modifiedCodeLens = Boolean(options.modifiedCodeLens); + } + this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0)); this._containerDomElement = document.createElement('div'); - this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide); + this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._renderSideBySide); this._containerDomElement.style.position = 'relative'; this._containerDomElement.style.height = '100%'; this._domElement.appendChild(this._containerDomElement); @@ -324,28 +343,27 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._isVisible = true; this._isHandlingScrollEvent = false; - this._width = 0; - this._height = 0; - this._reviewHeight = 0; + this._elementSizeObserver = this._register(new ElementSizeObserver(this._containerDomElement, undefined, () => this._onDidContainerSizeChanged())); + if (options.automaticLayout) { + this._elementSizeObserver.startObserving(); + } this._diffComputationResult = null; const leftContextKeyService = this._contextKeyService.createScoped(); - leftContextKeyService.createKey('isInDiffLeftEditor', true); const leftServices = new ServiceCollection(); leftServices.set(IContextKeyService, leftContextKeyService); const leftScopedInstantiationService = instantiationService.createChild(leftServices); const rightContextKeyService = this._contextKeyService.createScoped(); - rightContextKeyService.createKey('isInDiffRightEditor', true); const rightServices = new ServiceCollection(); rightServices.set(IContextKeyService, rightContextKeyService); const rightScopedInstantiationService = instantiationService.createChild(rightServices); - this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService); - this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService); + this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService); + this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService); this._originalOverviewRuler = null; this._modifiedOverviewRuler = null; @@ -355,13 +373,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._containerDomElement.appendChild(this._reviewPane.shadow.domNode); this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode); - if (options.automaticLayout) { - this._measureDomElementToken = new IntervalTimer(); - this._measureDomElementToken.cancelAndSet(() => this._measureDomElement(false), 100); - } else { - this._measureDomElementToken = null; - } - // enableSplitViewResizing this._enableSplitViewResizing = true; if (typeof options.enableSplitViewResizing !== 'undefined') { @@ -374,11 +385,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing)); } - this._register(themeService.onThemeChange(t => { + this._register(themeService.onDidColorThemeChange(t => { if (this._strategy && this._strategy.applyColors(t)) { this._updateDecorationsRunner.schedule(); } - this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide); + this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._renderSideBySide); })); const contributions: IDiffEditorContributionDescription[] = EditorExtensionsRegistry.getDiffEditorContributions(); @@ -409,6 +420,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return this._renderIndicators; } + public getContentHeight(): number { + return this.modifiedEditor.getContentHeight(); + } + private _setState(newState: editorBrowser.DiffEditorState): void { if (this._state === newState) { return; @@ -437,7 +452,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._reviewPane.prev(); } - private static _getClassName(theme: ITheme, renderSideBySide: boolean): string { + private static _getClassName(theme: IColorTheme, renderSideBySide: boolean): string { let result = 'monaco-diff-editor monaco-editor-background '; if (renderSideBySide) { result += 'side-by-side '; @@ -468,8 +483,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._layoutOverviewRulers(); } - private _createLeftHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable)); + private _createLeftHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { + const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable, this._originalCodeLens)); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -498,11 +513,27 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); + const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); + this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + + this._register(editor.onDidContentSizeChange(e => { + const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); + + this._onDidContentSizeChange.fire({ + contentHeight: height, + contentWidth: width, + contentHeightChanged: e.contentHeightChanged, + contentWidthChanged: e.contentWidthChanged + }); + })); + return editor; } - private _createRightHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options)); + private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { + const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options, this._modifiedCodeLens)); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -543,6 +574,22 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } })); + const isInDiffRightEditorKey = contextKeyService.createKey('isInDiffRightEditor', undefined); + this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); + + this._register(editor.onDidContentSizeChange(e => { + const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); + + this._onDidContentSizeChange.fire({ + contentHeight: height, + contentWidth: width, + contentHeightChanged: e.contentHeightChanged, + contentWidthChanged: e.contentWidthChanged + }); + })); + return editor; } @@ -558,10 +605,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._beginUpdateDecorationsTimeout = -1; } - if (this._measureDomElementToken) { - this._measureDomElementToken.dispose(); - } - this._cleanViewZonesAndDecorations(); if (this._originalOverviewRuler) { @@ -666,9 +709,15 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE if (typeof newOptions.originalEditable !== 'undefined') { this._originalIsEditable = Boolean(newOptions.originalEditable); } + if (typeof newOptions.originalCodeLens !== 'undefined') { + this._originalCodeLens = Boolean(newOptions.originalCodeLens); + } + if (typeof newOptions.modifiedCodeLens !== 'undefined') { + this._modifiedCodeLens = Boolean(newOptions.modifiedCodeLens); + } - this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions)); - this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable)); + this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions, this._modifiedCodeLens)); + this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable, this._originalCodeLens)); // enableSplitViewResizing if (typeof newOptions.enableSplitViewResizing !== 'undefined') { @@ -684,7 +733,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing)); } // Update class name - this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide); + this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._renderSideBySide); } } @@ -763,6 +812,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); } + public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLineNearTop(lineNumber, scrollType); + } + public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { this.modifiedEditor.revealPosition(position, scrollType); } @@ -775,6 +828,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); } + public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealPositionNearTop(position, scrollType); + } + public getSelection(): Selection | null { return this.modifiedEditor.getSelection(); } @@ -807,6 +864,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); } + public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); + } + public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); } @@ -819,6 +880,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); } + public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealRangeNearTop(range, scrollType); + } + + public revealRangeNearTopIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealRangeNearTopIfOutsideViewport(range, scrollType); + } + public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { this.modifiedEditor.revealRangeAtTop(range, scrollType); } @@ -845,7 +914,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public layout(dimension?: editorCommon.IDimension): void { - this._measureDomElement(false, dimension); + this._elementSizeObserver.observe(dimension); } public focus(): void { @@ -872,7 +941,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._cleanViewZonesAndDecorations(); } - public trigger(source: string, handlerId: string, payload: any): void { + public trigger(source: string | null | undefined, handlerId: string, payload: any): void { this.modifiedEditor.trigger(source, handlerId, payload); } @@ -886,35 +955,21 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE //------------ begin layouting methods - private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void { - dimensions = dimensions || { - width: this._containerDomElement.clientWidth, - height: this._containerDomElement.clientHeight - }; - - if (dimensions.width <= 0) { - this._width = 0; - this._height = 0; - this._reviewHeight = 0; - return; - } - - if (!forceDoLayoutCall && dimensions.width === this._width && dimensions.height === this._height) { - // Nothing has changed - return; - } - - this._width = dimensions.width; - this._height = dimensions.height; - this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0; - + private _onDidContainerSizeChanged(): void { this._doLayout(); } + private _getReviewHeight(): number { + return this._reviewPane.isVisible() ? this._elementSizeObserver.getHeight() : 0; + } + private _layoutOverviewRulers(): void { if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) { return; } + const height = this._elementSizeObserver.getHeight(); + const reviewHeight = this._getReviewHeight(); + let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; let layoutInfo = this.modifiedEditor.getLayoutInfo(); if (layoutInfo) { @@ -922,13 +977,13 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE top: 0, width: DiffEditorWidget.ONE_OVERVIEW_WIDTH, right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH, - height: (this._height - this._reviewHeight) + height: (height - reviewHeight) }); this._modifiedOverviewRuler.setLayout({ top: 0, right: 0, width: DiffEditorWidget.ONE_OVERVIEW_WIDTH, - height: (this._height - this._reviewHeight) + height: (height - reviewHeight) }); } } @@ -1038,8 +1093,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } } - private _adjustOptionsForSubEditor(options: IDiffEditorOptions): IDiffEditorOptions { - let clonedOptions: IDiffEditorOptions = objects.deepClone(options || {}); + private _adjustOptionsForSubEditor(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IDiffEditorConstructionOptions { + let clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {}); clonedOptions.inDiffEditor = true; clonedOptions.wordWrap = 'off'; clonedOptions.wordWrapMinified = false; @@ -1049,6 +1104,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE clonedOptions.folding = false; clonedOptions.codeLens = false; clonedOptions.fixedOverflowWidgets = true; + clonedOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode; // clonedOptions.lineDecorationsWidth = '2ch'; if (!clonedOptions.minimap) { clonedOptions.minimap = {}; @@ -1057,15 +1113,21 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return clonedOptions; } - private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean): IEditorOptions { + private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isEditable: boolean, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { let result = this._adjustOptionsForSubEditor(options); + if (isCodeLensEnabled) { + result.codeLens = true; + } result.readOnly = !isEditable; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return result; } - private _adjustOptionsForRightHandSide(options: IDiffEditorOptions): IEditorOptions { + private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { let result = this._adjustOptionsForSubEditor(options); + if (isCodeLensEnabled) { + result.codeLens = true; + } result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; result.extraEditorClassName = 'modified-in-monaco-diff-editor'; @@ -1073,33 +1135,38 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public doLayout(): void { - this._measureDomElement(true); + this._elementSizeObserver.observe(); + this._doLayout(); } private _doLayout(): void { + const width = this._elementSizeObserver.getWidth(); + const height = this._elementSizeObserver.getHeight(); + const reviewHeight = this._getReviewHeight(); + let splitPoint = this._strategy.layout(); this._originalDomNode.style.width = splitPoint + 'px'; this._originalDomNode.style.left = '0px'; - this._modifiedDomNode.style.width = (this._width - splitPoint) + 'px'; + this._modifiedDomNode.style.width = (width - splitPoint) + 'px'; this._modifiedDomNode.style.left = splitPoint + 'px'; this._overviewDomElement.style.top = '0px'; - this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px'; + this._overviewDomElement.style.height = (height - reviewHeight) + 'px'; this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px'; - this._overviewDomElement.style.left = (this._width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px'; + this._overviewDomElement.style.left = (width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px'; this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH); this._overviewViewportDomElement.setHeight(30); - this.originalEditor.layout({ width: splitPoint, height: (this._height - this._reviewHeight) }); - this.modifiedEditor.layout({ width: this._width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (this._height - this._reviewHeight) }); + this.originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) }); + this.modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) }); if (this._originalOverviewRuler || this._modifiedOverviewRuler) { this._layoutOverviewRulers(); } - this._reviewPane.layout(this._height - this._reviewHeight, this._width, this._reviewHeight); + this._reviewPane.layout(height - reviewHeight, width, reviewHeight); this._layoutOverviewViewport(); } @@ -1140,11 +1207,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _createDataSource(): IDataSource { return { getWidth: () => { - return this._width; + return this._elementSizeObserver.getWidth(); }, getHeight: () => { - return (this._height - this._reviewHeight); + return (this._elementSizeObserver.getHeight() - this._getReviewHeight()); }, getContainerDomNode: () => { @@ -1171,14 +1238,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } this._strategy = newStrategy; - newStrategy.applyColors(this._themeService.getTheme()); + newStrategy.applyColors(this._themeService.getColorTheme()); if (this._diffComputationResult) { this._updateDecorations(); } // Just do a layout, the strategy might need it - this._measureDomElement(true); + this._doLayout(); } private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange | null { @@ -1295,7 +1362,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi this._removeColor = null; } - public applyColors(theme: ITheme): boolean { + public applyColors(theme: IColorTheme): boolean { let newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); let newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); let hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); @@ -1579,14 +1646,14 @@ abstract class ViewZonesComputer { protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null; } -function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { +export function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { return { range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), options: options }; } -const DECORATIONS = { +export const DECORATIONS = { charDelete: ModelDecorationOptions.register({ className: 'char-delete' @@ -1611,7 +1678,7 @@ const DECORATIONS = { }), lineInsertWithSign: ModelDecorationOptions.register({ className: 'line-insert', - linesDecorationsClassName: 'insert-sign codicon codicon-add', + linesDecorationsClassName: 'insert-sign ' + diffInsertIcon.classNames, marginClassName: 'line-insert', isWholeLine: true }), @@ -1623,7 +1690,7 @@ const DECORATIONS = { }), lineDeleteWithSign: ModelDecorationOptions.register({ className: 'line-delete', - linesDecorationsClassName: 'delete-sign codicon codicon-remove', + linesDecorationsClassName: 'delete-sign ' + diffRemoveIcon.classNames, marginClassName: 'line-delete', isWholeLine: true @@ -1634,7 +1701,7 @@ const DECORATIONS = { }; -class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider { +export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider { static readonly MINIMUM_EDITOR_WIDTH = 100; @@ -1651,7 +1718,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE this._sashRatio = null; this._sashPosition = null; this._startSashPosition = null; - this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this)); + this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this, { orientation: Orientation.VERTICAL })); if (this._disableSash) { this._sash.state = SashState.Disabled; @@ -2066,7 +2133,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { } let sb = createStringBuilder(10000); - let marginHTML: string[] = []; + let marginDomNode = document.createElement('div'); const layoutInfo = this.modifiedEditorOptions.get(EditorOption.layoutInfo); const fontInfo = this.modifiedEditorOptions.get(EditorOption.fontInfo); const lineDecorationsWidth = layoutInfo.decorationsWidth; @@ -2081,21 +2148,20 @@ class InlineViewZonesComputer extends ViewZonesComputer { if (this.renderIndicators) { let index = lineNumber - lineChange.originalStartLineNumber; - marginHTML = marginHTML.concat([ - `
    ` - ]); + const marginElement = document.createElement('div'); + marginElement.className = `delete-sign ${diffRemoveIcon.classNames}`; + marginElement.setAttribute('style', `position:absolute;top:${index * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;`); + marginDomNode.appendChild(marginElement); } } maxCharsPerLine += this.modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn); let domNode = document.createElement('div'); - domNode.className = 'view-lines line-delete'; + domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; domNode.innerHTML = sb.build(); Configuration.applyFontInfoSlow(domNode, fontInfo); - let marginDomNode = document.createElement('div'); marginDomNode.className = 'inline-deleted-margin-view-zone'; - marginDomNode.innerHTML = marginHTML.join(''); Configuration.applyFontInfoSlow(marginDomNode, fontInfo); return { @@ -2147,6 +2213,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { 0, fontInfo.spaceWidth, fontInfo.middotWidth, + fontInfo.wsmiddotWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), @@ -2161,11 +2228,11 @@ class InlineViewZonesComputer extends ViewZonesComputer { } } -function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { +export function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { return lineChange.modifiedEndLineNumber > 0; } -function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { +export function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { return lineChange.originalEndLineNumber > 0; } @@ -2209,4 +2276,45 @@ registerThemingParticipant((theme, collector) => { if (border) { collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { border-left: 1px solid ${border}; }`); } + + const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground); + if (scrollbarSliderBackgroundColor) { + collector.addRule(` + .monaco-diff-editor .diffViewport { + background: ${scrollbarSliderBackgroundColor}; + } + `); + } + + const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); + if (scrollbarSliderHoverBackgroundColor) { + collector.addRule(` + .monaco-diff-editor .diffViewport:hover { + background: ${scrollbarSliderHoverBackgroundColor}; + } + `); + } + + const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); + if (scrollbarSliderActiveBackgroundColor) { + collector.addRule(` + .monaco-diff-editor .diffViewport:active { + background: ${scrollbarSliderActiveBackgroundColor}; + } + `); + } + + const diffDiagonalFillColor = theme.getColor(diffDiagonalFill); + collector.addRule(` + .monaco-editor .diagonal-fill { + background-image: linear-gradient( + -45deg, + ${diffDiagonalFillColor} 12.5%, + #0000 12.5%, #0000 50%, + ${diffDiagonalFillColor} 50%, ${diffDiagonalFillColor} 62.5%, + #0000 62.5%, #0000 100% + ); + background-size: 8px 8px; + } + `); }); diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 3540ddbaacd..693261364d0 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -30,6 +30,8 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Constants } from 'vs/base/common/uint'; +import { registerIcon, Codicon } from 'vs/base/common/codicons'; const DIFF_LINES_PADDING = 3; @@ -71,6 +73,10 @@ class Diff { } } +const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add); +const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove); +const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close); + export class DiffReview extends Disposable { private readonly _diffEditor: DiffEditorWidget; @@ -98,7 +104,7 @@ export class DiffReview extends Disposable { this.actionBarContainer.domNode )); - this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review', true, () => { + this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + diffReviewCloseIcon.classNames, true, () => { this.hide(); return Promise.resolve(null); }), { label: false, icon: true }); @@ -108,6 +114,7 @@ export class DiffReview extends Disposable { this._content = createFastDomNode(document.createElement('div')); this._content.setClassName('diff-review-content'); + this._content.setAttribute('role', 'code'); this.scrollbar = this._register(new DomScrollableElement(this._content.domNode, {})); this.domNode.domNode.appendChild(this.scrollbar.getDomNode()); @@ -124,16 +131,6 @@ export class DiffReview extends Disposable { } this._render(); })); - this._register(diffEditor.getOriginalEditor().onDidFocusEditorWidget(() => { - if (this._isVisible) { - this.hide(); - } - })); - this._register(diffEditor.getModifiedEditor().onDidFocusEditorWidget(() => { - if (this._isVisible) { - this.hide(); - } - })); this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'click', (e) => { e.preventDefault(); @@ -209,7 +206,9 @@ export class DiffReview extends Disposable { } index = index % this._diffs.length; - this._diffEditor.setPosition(new Position(this._diffs[index].entries[0].modifiedLineStart, 1)); + const entries = this._diffs[index].entries; + this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); + this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); this._isVisible = true; this._diffEditor.doLayout(); this._render(); @@ -242,7 +241,9 @@ export class DiffReview extends Disposable { } index = index % this._diffs.length; - this._diffEditor.setPosition(new Position(this._diffs[index].entries[0].modifiedLineStart, 1)); + const entries = this._diffs[index].entries; + this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); + this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); this._isVisible = true; this._diffEditor.doLayout(); this._render(); @@ -268,6 +269,7 @@ export class DiffReview extends Disposable { private hide(): void { this._isVisible = false; + this._diffEditor.updateOptions({ readOnly: false }); this._diffEditor.focus(); this._diffEditor.doLayout(); this._render(); @@ -540,6 +542,7 @@ export class DiffReview extends Disposable { return; } + this._diffEditor.updateOptions({ readOnly: true }); const diffIndex = this._findDiffIndex(this._diffEditor.getPosition()!); if (this._diffs[diffIndex] === this._currentDiff) { @@ -551,6 +554,7 @@ export class DiffReview extends Disposable { let container = document.createElement('div'); container.className = 'diff-review-table'; container.setAttribute('role', 'list'); + container.setAttribute('aria-label', 'Difference review. Use "Stage | Unstage | Revert Selected Ranges" commands'); Configuration.applyFontInfoSlow(container, modifiedOptions.get(EditorOption.fontInfo)); let minOriginalLine = 0; @@ -590,11 +594,11 @@ export class DiffReview extends Disposable { const getAriaLines = (lines: number) => { if (lines === 0) { - return nls.localize('no_lines', "no lines"); + return nls.localize('no_lines_changed', "no lines changed"); } else if (lines === 1) { - return nls.localize('one_line', "1 line"); + return nls.localize('one_line_changed', "1 line changed"); } else { - return nls.localize('more_lines', "{0} lines", lines); + return nls.localize('more_lines_changed', "{0} lines changed", lines); } }; @@ -608,19 +612,20 @@ export class DiffReview extends Disposable { 'That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.', 'Variables 0 and 1 refer to the diff index out of total number of diffs.', 'Variables 2 and 4 will be numbers (a line number).', - 'Variables 3 and 5 will be "no lines", "1 line" or "X lines", localized separately.' + 'Variables 3 and 5 will be "no lines changed", "1 line changed" or "X lines changed", localized separately.' ] - }, "Difference {0} of {1}: original {2}, {3}, modified {4}, {5}", (diffIndex + 1), this._diffs.length, minOriginalLine, originalChangedLinesCntAria, minModifiedLine, modifiedChangedLinesCntAria)); + }, "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", (diffIndex + 1), this._diffs.length, minOriginalLine, originalChangedLinesCntAria, minModifiedLine, modifiedChangedLinesCntAria)); header.appendChild(cell); // @@ -504,7 +517,7 @@ header.setAttribute('role', 'listitem'); container.appendChild(header); + const lineHeight = modifiedOptions.get(EditorOption.lineHeight); let modLine = minModifiedLine; for (let i = 0, len = diffs.length; i < len; i++) { const diffEntry = diffs[i]; - DiffReview._renderSection(container, diffEntry, modLine, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts); + DiffReview._renderSection(container, diffEntry, modLine, lineHeight, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts); if (diffEntry.modifiedLineStart !== 0) { modLine = diffEntry.modifiedLineEnd; } @@ -632,7 +637,7 @@ export class DiffReview extends Disposable { } private static _renderSection( - dest: HTMLElement, diffEntry: DiffEntry, modLine: number, width: number, + dest: HTMLElement, diffEntry: DiffEntry, modLine: number, lineHeight: number, width: number, originalOptions: IComputedEditorOptions, originalModel: ITextModel, originalModelOpts: TextModelResolvedOptions, modifiedOptions: IComputedEditorOptions, modifiedModel: ITextModel, modifiedModelOpts: TextModelResolvedOptions ): void { @@ -641,17 +646,18 @@ export class DiffReview extends Disposable { let rowClassName: string = 'diff-review-row'; let lineNumbersExtraClassName: string = ''; - let spacerClassName: string = 'diff-review-spacer'; + const spacerClassName: string = 'diff-review-spacer'; + let spacerIcon: Codicon | null = null; switch (type) { case DiffEntryType.Insert: rowClassName = 'diff-review-row line-insert'; lineNumbersExtraClassName = ' char-insert'; - spacerClassName = 'diff-review-spacer insert-sign'; + spacerIcon = diffReviewInsertIcon; break; case DiffEntryType.Delete: rowClassName = 'diff-review-row line-delete'; lineNumbersExtraClassName = ' char-delete'; - spacerClassName = 'diff-review-spacer delete-sign'; + spacerIcon = diffReviewRemoveIcon; break; } @@ -686,6 +692,7 @@ export class DiffReview extends Disposable { let cell = document.createElement('div'); cell.className = 'diff-review-cell'; + cell.style.height = `${lineHeight}px`; row.appendChild(cell); const originalLineNumber = document.createElement('span'); @@ -695,7 +702,7 @@ export class DiffReview extends Disposable { if (originalLine !== 0) { originalLineNumber.appendChild(document.createTextNode(String(originalLine))); } else { - originalLineNumber.innerHTML = ' '; + originalLineNumber.innerText = '\u00a0'; } cell.appendChild(originalLineNumber); @@ -707,13 +714,21 @@ export class DiffReview extends Disposable { if (modifiedLine !== 0) { modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine))); } else { - modifiedLineNumber.innerHTML = ' '; + modifiedLineNumber.innerText = '\u00a0'; } cell.appendChild(modifiedLineNumber); const spacer = document.createElement('span'); spacer.className = spacerClassName; - spacer.innerHTML = '  '; + + if (spacerIcon) { + const spacerCodicon = document.createElement('span'); + spacerCodicon.className = spacerIcon.classNames; + spacerCodicon.innerText = '\u00a0\u00a0'; + spacer.appendChild(spacerCodicon); + } else { + spacer.innerText = '\u00a0\u00a0'; + } cell.appendChild(spacer); let lineContent: string; @@ -736,13 +751,17 @@ export class DiffReview extends Disposable { let ariaLabel: string = ''; switch (type) { case DiffEntryType.Equal: - ariaLabel = nls.localize('equalLine', "original {0}, modified {1}: {2}", originalLine, modifiedLine, lineContent); + if (originalLine === modifiedLine) { + ariaLabel = nls.localize({ key: 'unchangedLine', comment: ['The placeholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, originalLine); + } else { + ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine); + } break; case DiffEntryType.Insert: - ariaLabel = nls.localize('insertLine', "+ modified {0}: {1}", modifiedLine, lineContent); + ariaLabel = nls.localize('insertLine', "+ {0} modified line {1}", lineContent, modifiedLine); break; case DiffEntryType.Delete: - ariaLabel = nls.localize('deleteLine', "- original {0}: {1}", originalLine, lineContent); + ariaLabel = nls.localize('deleteLine', "- {0} original line {1}", lineContent, originalLine); break; } row.setAttribute('aria-label', ariaLabel); @@ -783,6 +802,7 @@ export class DiffReview extends Disposable { 0, fontInfo.spaceWidth, fontInfo.middotWidth, + fontInfo.wsmiddotWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), @@ -857,9 +877,14 @@ class DiffReviewPrev extends EditorAction { function findFocusedDiffEditor(accessor: ServicesAccessor): DiffEditorWidget | null { const codeEditorService = accessor.get(ICodeEditorService); const diffEditors = codeEditorService.listDiffEditors(); + const activeCodeEditor = codeEditorService.getActiveCodeEditor(); + if (!activeCodeEditor) { + return null; + } + for (let i = 0, len = diffEditors.length; i < len; i++) { const diffEditor = diffEditors[i]; - if (diffEditor.hasWidgetFocus()) { + if (diffEditor.getModifiedEditor().getId() === activeCodeEditor.getId() || diffEditor.getOriginalEditor().getId() === activeCodeEditor.getId()) { return diffEditor; } } diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index 09a18fcf097..5dd98c444c7 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -37,7 +37,7 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { @INotificationService notificationService: INotificationService, @IAccessibilityService accessibilityService: IAccessibilityService ) { - super(domElement, parentEditor.getRawOptions(), {}, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService); + super(domElement, { ...parentEditor.getRawOptions(), overflowWidgetsDomNode: parentEditor.getOverflowWidgetsDomNode() }, {}, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService); this._parentEditor = parentEditor; this._overwriteOptions = options; diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index 9e61bc0b05e..7ae64d75c45 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -13,6 +13,7 @@ import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrow import { Range } from 'vs/editor/common/core/range'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Codicon } from 'vs/base/common/codicons'; export interface IDiffLinesChange { readonly originalStartLineNumber: number; @@ -57,7 +58,7 @@ export class InlineDiffMargin extends Disposable { this._marginDomNode.style.zIndex = '10'; this._diffActions = document.createElement('div'); - this._diffActions.className = 'codicon codicon-lightbulb lightbulb-glyph'; + this._diffActions.className = Codicon.lightBulb.classNames + ' lightbulb-glyph'; this._diffActions.style.position = 'absolute'; const lineHeight = editor.getOption(EditorOption.lineHeight); const lineFeed = editor.getModel()!.getEOL(); diff --git a/src/vs/editor/browser/widget/media/diagonal-fill.png b/src/vs/editor/browser/widget/media/diagonal-fill.png deleted file mode 100644 index 5cd481a8108..00000000000 Binary files a/src/vs/editor/browser/widget/media/diagonal-fill.png and /dev/null differ diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css index 317916a1078..e27e996b7b0 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -8,19 +8,14 @@ z-index: 9; } +.monaco-diff-editor .diffOverview .diffViewport { + z-index: 10; +} + /* colors not externalized: using transparancy on background */ .monaco-diff-editor.vs .diffOverview { background: rgba(0, 0, 0, 0.03); } .monaco-diff-editor.vs-dark .diffOverview { background: rgba(255, 255, 255, 0.01); } -.monaco-diff-editor .diffViewport { - box-shadow: inset 0px 0px 1px 0px #B9B9B9; - background: rgba(0, 0, 0, 0.10); -} - -.monaco-diff-editor.vs-dark .diffViewport, -.monaco-diff-editor.hc-black .diffViewport { - background: rgba(255, 255, 255, 0.10); -} .monaco-scrollable-element.modified-in-monaco-diff-editor.vs .scrollbar { background: rgba(0,0,0,0); } .monaco-scrollable-element.modified-in-monaco-diff-editor.vs-dark .scrollbar { background: rgba(0,0,0,0); } .monaco-scrollable-element.modified-in-monaco-diff-editor.hc-black .scrollbar { background: none; } @@ -56,16 +51,6 @@ text-align: right; } -.monaco-editor .diagonal-fill { - background: url('diagonal-fill.png'); -} -.monaco-editor.vs-dark .diagonal-fill { - opacity: 0.2; -} -.monaco-editor.hc-black .diagonal-fill { - background: none; -} - /* ---------- Inline Diff ---------- */ .monaco-editor .view-zones .view-lines .view-line span { diff --git a/src/vs/editor/browser/widget/media/diffReview.css b/src/vs/editor/browser/widget/media/diffReview.css index b2b17028489..56c6f15cbf6 100644 --- a/src/vs/editor/browser/widget/media/diffReview.css +++ b/src/vs/editor/browser/widget/media/diffReview.css @@ -37,13 +37,14 @@ width: 100%; } -.monaco-diff-editor .diff-review-cell { - display: table-cell; -} - .monaco-diff-editor .diff-review-spacer { display: inline-block; width: 10px; + vertical-align: middle; +} + +.monaco-diff-editor .diff-review-spacer > .codicon { + font-size: 9px !important; } .monaco-diff-editor .diff-review-actions { diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 742ad42fa9c..52473fd2793 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -103,14 +103,14 @@ export class ShiftCommand implements ICommand { const { tabSize, indentSize, insertSpaces } = this._opts; const shouldIndentEmptyLines = (startLine === endLine); - // if indenting or outdenting on a whitespace only line - if (this._selection.isEmpty()) { - if (/^\s*$/.test(model.getLineContent(startLine))) { - this._useLastEditRangeForCursorEndPosition = true; - } - } - if (this._opts.useTabStops) { + // if indenting or outdenting on a whitespace only line + if (this._selection.isEmpty()) { + if (/^\s*$/.test(model.getLineContent(startLine))) { + this._useLastEditRangeForCursorEndPosition = true; + } + } + // keep track of previous line's "miss-alignment" let previousLineExtraSpaces = 0, extraSpaces = 0; for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++, previousLineExtraSpaces = extraSpaces) { @@ -188,6 +188,11 @@ export class ShiftCommand implements ICommand { } } else { + // if indenting or outdenting on a whitespace only line + if (!this._opts.isUnshift && this._selection.isEmpty() && model.getLineLength(startLine) === 0) { + this._useLastEditRangeForCursorEndPosition = true; + } + const oneIndent = (insertSpaces ? cachedStringRepeat(' ', indentSize) : '\t'); for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) { diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 7a4bc82f8f1..3bde5dfd839 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as arrays from 'vs/base/common/arrays'; -import { IEditorOptions, editorOptionsRegistry, ValidatedEditorOptions, IEnvironmentalOptions, IComputedEditorOptions, ConfigurationChangedEvent, EDITOR_MODEL_DEFAULTS, EditorOption, FindComputedEditorOptionValueById } from 'vs/editor/common/config/editorOptions'; +import { IEditorOptions, editorOptionsRegistry, ValidatedEditorOptions, IEnvironmentalOptions, IComputedEditorOptions, ConfigurationChangedEvent, EDITOR_MODEL_DEFAULTS, EditorOption, FindComputedEditorOptionValueById, ComputeOptionsMemory } from 'vs/editor/common/config/editorOptions'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { IConfiguration, IDimension } from 'vs/editor/common/editorCommon'; @@ -283,10 +283,15 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC private _onDidChange = this._register(new Emitter()); public readonly onDidChange: Event = this._onDidChange.event; + private _onDidChangeFast = this._register(new Emitter()); + public readonly onDidChangeFast: Event = this._onDidChangeFast.event; + public readonly isSimpleWidget: boolean; + private _computeOptionsMemory: ComputeOptionsMemory; public options!: ComputedEditorOptions; private _isDominatedByLongLines: boolean; + private _viewLineCount: number; private _lineNumbersDigitCount: number; private _rawOptions: IEditorOptions; @@ -298,6 +303,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC this.isSimpleWidget = isSimpleWidget; this._isDominatedByLongLines = false; + this._computeOptionsMemory = new ComputeOptionsMemory(); + this._viewLineCount = 1; this._lineNumbersDigitCount = 1; this._rawOptions = deepCloneAndMigrateOptions(_options); @@ -330,6 +337,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC } this.options = newOptions; + this._onDidChangeFast.fire(changeEvent); this._onDidChange.fire(changeEvent); } } @@ -342,11 +350,13 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC const partialEnv = this._getEnvConfiguration(); const bareFontInfo = BareFontInfo.createFromValidatedSettings(this._validatedOptions, partialEnv.zoomLevel, this.isSimpleWidget); const env: IEnvironmentalOptions = { + memory: this._computeOptionsMemory, outerWidth: partialEnv.outerWidth, outerHeight: partialEnv.outerHeight, fontInfo: this.readConfiguration(bareFontInfo), extraEditorClassName: partialEnv.extraEditorClassName, isDominatedByLongLines: this._isDominatedByLongLines, + viewLineCount: this._viewLineCount, lineNumbersDigitCount: this._lineNumbersDigitCount, emptySelectionClipboard: partialEnv.emptySelectionClipboard, pixelRatio: partialEnv.pixelRatio, @@ -371,7 +381,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC } continue; } - if (typeof baseValue === 'object' && typeof subsetValue === 'object') { + if (baseValue && typeof baseValue === 'object' && subsetValue && typeof subsetValue === 'object') { if (!this._subsetEquals(baseValue, subsetValue)) { return false; } @@ -405,11 +415,19 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC } public setMaxLineNumber(maxLineNumber: number): void { - let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber); - if (this._lineNumbersDigitCount === digitCount) { + const lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber); + if (this._lineNumbersDigitCount === lineNumbersDigitCount) { return; } - this._lineNumbersDigitCount = digitCount; + this._lineNumbersDigitCount = lineNumbersDigitCount; + this._recomputeOptions(); + } + + public setViewLineCount(viewLineCount: number): void { + if (this._viewLineCount === viewLineCount) { + return; + } + this._viewLineCount = viewLineCount; this._recomputeOptions(); } @@ -484,6 +502,16 @@ const editorConfiguration: IConfigurationNode = { default: true, description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.") }, + 'editor.semanticHighlighting.enabled': { + enum: [true, false, 'configuredByTheme'], + enumDescriptions: [ + nls.localize('semanticHighlighting.true', 'Semantic highlighting enabled for all color themes.'), + nls.localize('semanticHighlighting.false', 'Semantic highlighting disabled for all color themes.'), + nls.localize('semanticHighlighting.configuredByTheme', 'Semantic highlighting is configured by the current color theme\'s `semanticHighlighting` setting.') + ], + default: 'configuredByTheme', + description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.") + }, 'editor.stablePeek': { type: 'boolean', default: false, @@ -507,12 +535,17 @@ const editorConfiguration: IConfigurationNode = { 'diffEditor.ignoreTrimWhitespace': { type: 'boolean', default: true, - description: nls.localize('ignoreTrimWhitespace', "Controls whether the diff editor shows changes in leading or trailing whitespace as diffs.") + description: nls.localize('ignoreTrimWhitespace', "When enabled, the diff editor ignores changes in leading or trailing whitespace.") }, 'diffEditor.renderIndicators': { type: 'boolean', default: true, description: nls.localize('renderIndicators', "Controls whether the diff editor shows +/- indicators for added/removed changes.") + }, + 'diffEditor.codeLens': { + type: 'boolean', + default: false, + description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") } } }; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 9de657dcd87..c2c0141e294 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -11,7 +11,6 @@ import { Constants } from 'vs/base/common/uint'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; -import { IDimension } from 'vs/editor/common/editorCommon'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; //#region typed options @@ -54,11 +53,15 @@ export interface IEditorOptions { * The aria label for the editor's textarea (when it is focused). */ ariaLabel?: string; + /** + * The `tabindex` property of the editor's textarea + */ + tabIndex?: number; /** * Render vertical lines at the specified columns. * Defaults to empty array. */ - rulers?: number[]; + rulers?: (number | IRulerOption)[]; /** * A string containing the word separators used when doing word navigation. * Defaults to `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/? @@ -74,7 +77,7 @@ export interface IEditorOptions { * If it is a function, it will be invoked when rendering a line number and the return value will be rendered. * Otherwise, if it is a truey, line numbers will be rendered normally (equivalent of using an identity function). * Otherwise, line numbers will not be rendered. - * Defaults to true. + * Defaults to `on`. */ lineNumbers?: LineNumbersType; /** @@ -93,6 +96,11 @@ export interface IEditorOptions { * Defaults to true. */ renderFinalNewline?: boolean; + /** + * Remove unusual line terminators like LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS). + * Defaults to 'prompt'. + */ + unusualLineTerminators?: 'auto' | 'off' | 'prompt'; /** * Should the corresponding line be selected when clicking on the line number? * Defaults to true. @@ -135,6 +143,11 @@ export interface IEditorOptions { * Defaults to false. */ readOnly?: boolean; + /** + * Rename matching regions on type. + * Defaults to false. + */ + renameOnType?: boolean; /** * Should the editor render validation decorations. * Defaults to editable. @@ -267,10 +280,10 @@ export interface IEditorOptions { */ wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; /** - * Controls the wrapping algorithm to use. - * Defaults to 'monospace'. + * Controls the wrapping strategy to use. + * Defaults to 'simple'. */ - wrappingAlgorithm?: 'monospace' | 'dom'; + wrappingStrategy?: 'simple' | 'advanced'; /** * Configure word wrapping characters. A break will be introduced before these characters. * Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'. @@ -319,6 +332,16 @@ export interface IEditorOptions { * Defaults to 5. */ fastScrollSensitivity?: number; + /** + * Enable that the editor scrolls only the predominant axis. Prevents horizontal drift when scrolling vertically on a trackpad. + * Defaults to true. + */ + scrollPredominantAxis?: boolean; + /** + * Enable that the selection with the mouse and keys is doing column selection. + * Defaults to false. + */ + columnSelection?: boolean; /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' @@ -347,6 +370,10 @@ export interface IEditorOptions { * Suggest options. */ suggest?: ISuggestOptions; + /** + * Smart select opptions; + */ + smartSelect?: ISmartSelectOptions; /** * */ @@ -361,6 +388,10 @@ export interface IEditorOptions { * Defaults to 10 (ms) */ quickSuggestionsDelay?: number; + /** + * Controls the spacing around the editor. + */ + padding?: IEditorPaddingOptions; /** * Parameter hint options. */ @@ -385,8 +416,8 @@ export interface IEditorOptions { */ autoSurround?: EditorAutoSurroundStrategy; /** - * Enable auto indentation adjustment. - * Defaults to false. + * Controls whether the editor should automatically adjust the indentation when users type, paste, move or indent lines. + * Defaults to advanced. */ autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; /** @@ -492,6 +523,11 @@ export interface IEditorOptions { * Defaults to 'mouseover'. */ showFoldingControls?: 'always' | 'mouseover'; + /** + * Controls whether clicking on the empty content after a folded line will unfold the line. + * Defaults to false. + */ + unfoldOnClickAfterEndOfLine?: boolean; /** * Enable highlighting of matching brackets. * Defaults to 'always'. @@ -501,7 +537,7 @@ export interface IEditorOptions { * Enable rendering of whitespace. * Defaults to none. */ - renderWhitespace?: 'none' | 'boundary' | 'selection' | 'all'; + renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; /** * Enable rendering of control characters. * Defaults to false. @@ -522,6 +558,11 @@ export interface IEditorOptions { * Defaults to all. */ renderLineHighlight?: 'none' | 'gutter' | 'line' | 'all'; + /** + * Control if the current line highlight should be rendered only the editor is focused. + * Defaults to false. + */ + renderLineHighlightOnlyWhenFocus?: boolean; /** * Inserting and deleting whitespace follows tab stops. */ @@ -555,13 +596,15 @@ export interface IEditorOptions { * Defaults to false. */ peekWidgetDefaultFocus?: 'tree' | 'editor'; -} - -export interface IEditorConstructionOptions extends IEditorOptions { /** - * The initial editor dimension (to avoid measuring the container). + * Controls whether the definition link opens element in the peek widget. + * Defaults to false. */ - dimension?: IDimension; + definitionLinkOpensInPeek?: boolean; + /** + * Controls strikethrough deprecated variables. + */ + showDeprecated?: boolean; } /** @@ -604,6 +647,16 @@ export interface IDiffEditorOptions extends IEditorOptions { * Defaults to false. */ originalEditable?: boolean; + /** + * Original editor should be have code lens enabled? + * Defaults to false. + */ + originalCodeLens?: boolean; + /** + * Modified editor should be have code lens enabled? + * Defaults to false. + */ + modifiedCodeLens?: boolean; } //#endregion @@ -619,9 +672,6 @@ export class ConfigurationChangedEvent { constructor(values: boolean[]) { this._values = values; } - /** - * @internal - */ public hasChanged(id: EditorOption): boolean { return this._values[id]; } @@ -656,11 +706,13 @@ export interface IComputedEditorOptions { * @internal */ export interface IEnvironmentalOptions { + readonly memory: ComputeOptionsMemory | null; readonly outerWidth: number; readonly outerHeight: number; readonly fontInfo: FontInfo; readonly extraEditorClassName: string; readonly isDominatedByLongLines: boolean; + readonly viewLineCount: number; readonly lineNumbersDigitCount: number; readonly emptySelectionClipboard: boolean; readonly pixelRatio: number; @@ -668,6 +720,22 @@ export interface IEnvironmentalOptions { readonly accessibilitySupport: AccessibilitySupport; } +/** + * @internal + */ +export class ComputeOptionsMemory { + + public stableMinimapLayoutInput: IMinimapLayoutInput | null; + public stableFitMaxMinimapScale: number; + public stableFitRemainingWidth: number; + + constructor() { + this.stableMinimapLayoutInput = null; + this.stableFitMaxMinimapScale = 0; + this.stableFitRemainingWidth = 0; + } +} + export interface IEditorOption { readonly id: K1; readonly name: string; @@ -792,15 +860,13 @@ class EditorBooleanOption extends SimpleEditorOption extends SimpleEditorOption { - public static clampedInt(value: any, defaultValue: number, minimum: number, maximum: number): number { - let r: number; + public static clampedInt(value: any, defaultValue: T, minimum: number, maximum: number): number | T { if (typeof value === 'undefined') { - r = defaultValue; - } else { - r = parseInt(value, 10); - if (isNaN(r)) { - r = defaultValue; - } + return defaultValue; + } + let r = parseInt(value, 10); + if (isNaN(r)) { + return defaultValue; } r = Math.max(minimum, r); r = Math.min(maximum, r); @@ -972,11 +1038,11 @@ class EditorAccessibilitySupport extends BaseEditorOption>; @@ -1021,6 +1092,7 @@ class EditorComments extends BaseEditorOption>; @@ -1229,14 +1321,21 @@ class EditorFind extends BaseEditorOption constructor() { const defaults: EditorFindOptions = { + cursorMoveOnType: true, seedSearchStringFromSelection: true, autoFindInSelection: 'never', globalFindClipboard: false, - addExtraSpaceOnTop: true + addExtraSpaceOnTop: true, + loop: true }; super( EditorOption.find, 'find', defaults, { + 'editor.find.cursorMoveOnType': { + type: 'boolean', + default: defaults.cursorMoveOnType, + description: nls.localize('find.cursorMoveOnType', "Controls whether the cursor should jump to find matches while typing.") + }, 'editor.find.seedSearchStringFromSelection': { type: 'boolean', default: defaults.seedSearchStringFromSelection, @@ -1251,7 +1350,7 @@ class EditorFind extends BaseEditorOption nls.localize('editor.find.autoFindInSelection.always', 'Always turn on Find in selection automatically'), nls.localize('editor.find.autoFindInSelection.multiline', 'Turn on Find in selection automatically when multiple lines of content are selected.') ], - description: nls.localize('find.autoFindInSelection', "Controls whether the find operation is carried out on selected text or the entire file in the editor.") + description: nls.localize('find.autoFindInSelection', "Controls the condition for turning on find in selection automatically.") }, 'editor.find.globalFindClipboard': { type: 'boolean', @@ -1263,23 +1362,31 @@ class EditorFind extends BaseEditorOption type: 'boolean', default: defaults.addExtraSpaceOnTop, description: nls.localize('find.addExtraSpaceOnTop', "Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible.") - } + }, + 'editor.find.loop': { + type: 'boolean', + default: defaults.loop, + description: nls.localize('find.loop', "Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found.") + }, + } ); } public validate(_input: any): EditorFindOptions { - if (typeof _input !== 'object') { + if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as IEditorFindOptions; return { + cursorMoveOnType: EditorBooleanOption.boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), autoFindInSelection: typeof _input.autoFindInSelection === 'boolean' ? (_input.autoFindInSelection ? 'always' : 'never') : EditorStringEnumOption.stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']), globalFindClipboard: EditorBooleanOption.boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard), - addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop) + addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop), + loop: EditorBooleanOption.boolean(input.loop, this.defaultValue.loop), }; } } @@ -1310,7 +1417,7 @@ export class EditorFontLigatures extends BaseEditorOption { //#endregion +//#region fontWeight + +class EditorFontWeight extends BaseEditorOption { + private static SUGGESTION_VALUES = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; + private static MINIMUM_VALUE = 1; + private static MAXIMUM_VALUE = 1000; + + constructor() { + super( + EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight, + { + anyOf: [ + { + type: 'number', + minimum: EditorFontWeight.MINIMUM_VALUE, + maximum: EditorFontWeight.MAXIMUM_VALUE, + errorMessage: nls.localize('fontWeightErrorMessage', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.") + }, + { + type: 'string', + pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$' + }, + { + enum: EditorFontWeight.SUGGESTION_VALUES + } + ], + default: EDITOR_FONT_DEFAULTS.fontWeight, + description: nls.localize('fontWeight', "Controls the font weight. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000.") + } + ); + } + + public validate(input: any): string { + if (input === 'normal' || input === 'bold') { + return input; + } + return String(EditorIntOption.clampedInt(input, EDITOR_FONT_DEFAULTS.fontWeight, EditorFontWeight.MINIMUM_VALUE, EditorFontWeight.MAXIMUM_VALUE)); + } +} + +//#endregion + //#region gotoLocation export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto'; @@ -1494,7 +1643,7 @@ class EditorGoToLocation extends BaseEditorOption>; - -class EditorSemanticHighlighting extends BaseEditorOption { - - constructor() { - const defaults: EditorSemanticHighlightingOptions = { - enabled: true - }; - super( - EditorOption.semanticHighlighting, 'semanticHighlighting', defaults, - { - 'editor.semanticHighlighting.enabled': { - type: 'boolean', - default: defaults.enabled, - description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.") - } - } - ); - } - - public validate(_input: any): EditorSemanticHighlightingOptions { - if (typeof _input !== 'object') { - return this.defaultValue; - } - const input = _input as IEditorSemanticHighlightingOptions; - return { - enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled) - }; - } -} - -//#endregion - //#region layoutInfo /** @@ -1716,24 +1816,19 @@ export interface EditorLayoutInfo { readonly contentWidth: number; /** - * The position for the minimap + * Layout information for the minimap */ - readonly minimapLeft: number; - /** - * The width of the minimap - */ - readonly minimapWidth: number; - - /** - * Minimap render type - */ - readonly renderMinimap: RenderMinimap; + readonly minimap: EditorMinimapLayoutInfo; /** * The number of columns (of typical characters) fitting on a viewport line. */ readonly viewportColumn: number; + readonly isWordWrapMinified: boolean; + readonly isViewportWrapping: boolean; + readonly wrappingColumn: number; + /** * The width of the vertical scrollbar. */ @@ -1749,17 +1844,80 @@ export interface EditorLayoutInfo { readonly overviewRuler: OverviewRulerPosition; } +/** + * The internal layout details of the editor. + */ +export interface EditorMinimapLayoutInfo { + readonly renderMinimap: RenderMinimap; + readonly minimapLeft: number; + readonly minimapWidth: number; + readonly minimapHeightIsEditorHeight: boolean; + readonly minimapIsSampling: boolean; + readonly minimapScale: number; + readonly minimapLineHeight: number; + readonly minimapCanvasInnerWidth: number; + readonly minimapCanvasInnerHeight: number; + readonly minimapCanvasOuterWidth: number; + readonly minimapCanvasOuterHeight: number; +} + /** * @internal */ export interface EditorLayoutInfoComputerEnv { - outerWidth: number; - outerHeight: number; - lineHeight: number; - lineNumbersDigitCount: number; - typicalHalfwidthCharacterWidth: number; - maxDigitWidth: number; - pixelRatio: number; + readonly memory: ComputeOptionsMemory | null; + readonly outerWidth: number; + readonly outerHeight: number; + readonly isDominatedByLongLines: boolean; + readonly lineHeight: number; + readonly viewLineCount: number; + readonly lineNumbersDigitCount: number; + readonly typicalHalfwidthCharacterWidth: number; + readonly maxDigitWidth: number; + readonly pixelRatio: number; +} + +/** + * @internal + */ +export interface IEditorLayoutComputerInput { + readonly outerWidth: number; + readonly outerHeight: number; + readonly isDominatedByLongLines: boolean; + readonly lineHeight: number; + readonly lineNumbersDigitCount: number; + readonly typicalHalfwidthCharacterWidth: number; + readonly maxDigitWidth: number; + readonly pixelRatio: number; + readonly glyphMargin: boolean; + readonly lineDecorationsWidth: string | number; + readonly folding: boolean; + readonly minimap: Readonly>; + readonly scrollbar: InternalEditorScrollbarOptions; + readonly lineNumbers: InternalEditorRenderLineNumbersOptions; + readonly lineNumbersMinChars: number; + readonly scrollBeyondLastLine: boolean; + readonly wordWrap: 'wordWrapColumn' | 'on' | 'off' | 'bounded'; + readonly wordWrapColumn: number; + readonly wordWrapMinified: boolean; + readonly accessibilitySupport: AccessibilitySupport; +} + +/** + * @internal + */ +export interface IMinimapLayoutInput { + readonly outerWidth: number; + readonly outerHeight: number; + readonly lineHeight: number; + readonly typicalHalfwidthCharacterWidth: number; + readonly pixelRatio: number; + readonly scrollBeyondLastLine: boolean; + readonly minimap: Readonly>; + readonly verticalScrollbarWidth: number; + readonly viewLineCount: number; + readonly remainingWidth: number; + readonly isViewportWrapping: boolean; } /** @@ -1770,15 +1928,24 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption= 2 ? Math.round(input.minimap.scale * 2) : input.minimap.scale); + const minimapMaxColumn = input.minimap.maxColumn; + const minimapSize = input.minimap.size; + const minimapSide = input.minimap.side; + const verticalScrollbarWidth = input.verticalScrollbarWidth; + const viewLineCount = input.viewLineCount; + const remainingWidth = input.remainingWidth; + const isViewportWrapping = input.isViewportWrapping; + + const baseCharHeight = minimapRenderCharacters ? 2 : 3; + let minimapCanvasInnerHeight = Math.floor(pixelRatio * outerHeight); + const minimapCanvasOuterHeight = minimapCanvasInnerHeight / pixelRatio; + let minimapHeightIsEditorHeight = false; + let minimapIsSampling = false; + let minimapLineHeight = baseCharHeight * minimapScale; + let minimapCharWidth = minimapScale / pixelRatio; + let minimapWidthMultiplier: number = 1; + + if (minimapSize === 'fill' || minimapSize === 'fit') { + const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({ + viewLineCount: viewLineCount, + scrollBeyondLastLine: scrollBeyondLastLine, + height: outerHeight, + lineHeight: lineHeight, + pixelRatio: pixelRatio + }); + // ratio is intentionally not part of the layout to avoid the layout changing all the time + // when doing sampling + const ratio = viewLineCount / minimapLineCount; + + if (ratio > 1) { + minimapHeightIsEditorHeight = true; + minimapIsSampling = true; + minimapScale = 1; + minimapLineHeight = 1; + minimapCharWidth = minimapScale / pixelRatio; + } else { + let fitBecomesFill = false; + let maxMinimapScale = minimapScale + 1; + + if (minimapSize === 'fit') { + const effectiveMinimapHeight = Math.ceil((viewLineCount + extraLinesBeyondLastLine) * minimapLineHeight); + if (isViewportWrapping && couldUseMemory && remainingWidth <= memory.stableFitRemainingWidth) { + // There is a loop when using `fit` and viewport wrapping: + // - view line count impacts minimap layout + // - minimap layout impacts viewport width + // - viewport width impacts view line count + // To break the loop, once we go to a smaller minimap scale, we try to stick with it. + fitBecomesFill = true; + maxMinimapScale = memory.stableFitMaxMinimapScale; + } else { + fitBecomesFill = (effectiveMinimapHeight > minimapCanvasInnerHeight); + if (isViewportWrapping && fitBecomesFill) { + // remember for next time + memory.stableMinimapLayoutInput = input; + memory.stableFitRemainingWidth = remainingWidth; + } else { + memory.stableMinimapLayoutInput = null; + memory.stableFitRemainingWidth = 0; + } + } + } + + if (minimapSize === 'fill' || fitBecomesFill) { + minimapHeightIsEditorHeight = true; + const configuredMinimapScale = minimapScale; + minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio))); + minimapScale = Math.min(maxMinimapScale, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); + if (minimapScale > configuredMinimapScale) { + minimapWidthMultiplier = Math.min(2, minimapScale / configuredMinimapScale); + } + minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; + minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, viewLineCount + extraLinesBeyondLastLine)) * minimapLineHeight); + if (isViewportWrapping && fitBecomesFill) { + memory.stableFitMaxMinimapScale = minimapScale; + } + } + } + } + + // Given: + // (leaving 2px for the cursor to have space after the last character) + // viewportColumn = (contentWidth - verticalScrollbarWidth - 2) / typicalHalfwidthCharacterWidth + // minimapWidth = viewportColumn * minimapCharWidth + // contentWidth = remainingWidth - minimapWidth + // What are good values for contentWidth and minimapWidth ? + + // minimapWidth = ((contentWidth - verticalScrollbarWidth - 2) / typicalHalfwidthCharacterWidth) * minimapCharWidth + // typicalHalfwidthCharacterWidth * minimapWidth = (contentWidth - verticalScrollbarWidth - 2) * minimapCharWidth + // typicalHalfwidthCharacterWidth * minimapWidth = (remainingWidth - minimapWidth - verticalScrollbarWidth - 2) * minimapCharWidth + // (typicalHalfwidthCharacterWidth + minimapCharWidth) * minimapWidth = (remainingWidth - verticalScrollbarWidth - 2) * minimapCharWidth + // minimapWidth = ((remainingWidth - verticalScrollbarWidth - 2) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth) + + const minimapMaxWidth = Math.floor(minimapMaxColumn * minimapCharWidth); + const minimapWidth = Math.min(minimapMaxWidth, Math.max(0, Math.floor(((remainingWidth - verticalScrollbarWidth - 2) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth))) + MINIMAP_GUTTER_WIDTH); + + let minimapCanvasInnerWidth = Math.floor(pixelRatio * minimapWidth); + const minimapCanvasOuterWidth = minimapCanvasInnerWidth / pixelRatio; + minimapCanvasInnerWidth = Math.floor(minimapCanvasInnerWidth * minimapWidthMultiplier); + + const renderMinimap = (minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks); + const minimapLeft = (minimapSide === 'left' ? 0 : (outerWidth - minimapWidth - verticalScrollbarWidth)); + + return { + renderMinimap, + minimapLeft, + minimapWidth, + minimapHeightIsEditorHeight, + minimapIsSampling, + minimapScale, + minimapLineHeight, + minimapCanvasInnerWidth, + minimapCanvasInnerHeight, + minimapCanvasOuterWidth, + minimapCanvasOuterHeight, + }; + } + public static computeLayout(options: IComputedEditorOptions, env: EditorLayoutInfoComputerEnv): EditorLayoutInfo { const outerWidth = env.outerWidth | 0; const outerHeight = env.outerHeight | 0; @@ -1794,22 +2141,25 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption= 2 ? Math.round(minimap.scale * 2) : minimap.scale); - const minimapMaxColumn = minimap.maxColumn | 0; const scrollbar = options.get(EditorOption.scrollbar); - const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0; + const verticalScrollbarWidth = scrollbar.verticalScrollbarSize; const verticalScrollbarHasArrows = scrollbar.verticalHasArrows; - const scrollbarArrowSize = scrollbar.arrowSize | 0; - const horizontalScrollbarHeight = scrollbar.horizontalScrollbarSize | 0; + const scrollbarArrowSize = scrollbar.arrowSize; + const horizontalScrollbarHeight = scrollbar.horizontalScrollbarSize; const rawLineDecorationsWidth = options.get(EditorOption.lineDecorationsWidth); const folding = options.get(EditorOption.folding); @@ -1843,57 +2193,62 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption minimapMaxColumn) { - minimapWidth = Math.floor(minimapMaxColumn * minimapCharWidth); - } - contentWidth = remainingWidth - minimapWidth; - - if (minimapSide === 'left') { - minimapLeft = 0; - glyphMarginLeft += minimapWidth; - lineNumbersLeft += minimapWidth; - decorationsLeft += minimapWidth; - contentLeft += minimapWidth; - } else { - minimapLeft = outerWidth - minimapWidth - verticalScrollbarWidth; + if (accessibilitySupport !== AccessibilitySupport.Enabled) { + // See https://github.com/microsoft/vscode/issues/27766 + // Never enable wrapping when a screen reader is attached + // because arrow down etc. will not move the cursor in the way + // a screen reader expects. + if (wordWrapMinified && isDominatedByLongLines) { + // Force viewport width wrapping if model is dominated by long lines + isWordWrapMinified = true; + isViewportWrapping = true; + } else if (wordWrap === 'on' || wordWrap === 'bounded') { + isViewportWrapping = true; + } else if (wordWrap === 'wordWrapColumn') { + wrappingColumn = wordWrapColumn; } } + const minimapLayout = EditorLayoutInfoComputer._computeMinimapLayout({ + outerWidth: outerWidth, + outerHeight: outerHeight, + lineHeight: lineHeight, + typicalHalfwidthCharacterWidth: typicalHalfwidthCharacterWidth, + pixelRatio: pixelRatio, + scrollBeyondLastLine: scrollBeyondLastLine, + minimap: minimap, + verticalScrollbarWidth: verticalScrollbarWidth, + viewLineCount: viewLineCount, + remainingWidth: remainingWidth, + isViewportWrapping: isViewportWrapping, + }, env.memory || new ComputeOptionsMemory()); + + if (minimapLayout.renderMinimap !== RenderMinimap.None && minimapLayout.minimapLeft === 0) { + // the minimap is rendered to the left, so move everything to the right + glyphMarginLeft += minimapLayout.minimapWidth; + lineNumbersLeft += minimapLayout.minimapWidth; + decorationsLeft += minimapLayout.minimapWidth; + contentLeft += minimapLayout.minimapWidth; + } + const contentWidth = remainingWidth - minimapLayout.minimapWidth; + // (leaving 2px for the cursor to have space after the last character) const viewportColumn = Math.max(1, Math.floor((contentWidth - verticalScrollbarWidth - 2) / typicalHalfwidthCharacterWidth)); const verticalArrowSize = (verticalScrollbarHasArrows ? scrollbarArrowSize : 0); + if (isViewportWrapping) { + // compute the actual wrappingColumn + wrappingColumn = Math.max(1, viewportColumn); + if (wordWrap === 'bounded') { + wrappingColumn = Math.min(wrappingColumn, wordWrapColumn); + } + } + return { width: outerWidth, height: outerHeight, @@ -1910,12 +2265,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']), side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters), @@ -2118,6 +2493,65 @@ function _multiCursorModifierFromString(multiCursorModifier: 'ctrlCmd' | 'alt'): //#endregion +//#region padding + +/** + * Configuration options for editor padding + */ +export interface IEditorPaddingOptions { + /** + * Spacing between top edge of editor and first line. + */ + top?: number; + /** + * Spacing between bottom edge of editor and last line. + */ + bottom?: number; +} + +export interface InternalEditorPaddingOptions { + readonly top: number; + readonly bottom: number; +} + +class EditorPadding extends BaseEditorOption { + + constructor() { + super( + EditorOption.padding, 'padding', { top: 0, bottom: 0 }, + { + 'editor.padding.top': { + type: 'number', + default: 0, + minimum: 0, + maximum: 1000, + description: nls.localize('padding.top', "Controls the amount of space between the top edge of the editor and the first line.") + }, + 'editor.padding.bottom': { + type: 'number', + default: 0, + minimum: 0, + maximum: 1000, + description: nls.localize('padding.bottom', "Controls the amount of space between the bottom edge of the editor and the last line.") + } + } + ); + } + + public validate(_input: any): InternalEditorPaddingOptions { + if (!_input || typeof _input !== 'object') { + return this.defaultValue; + } + const input = _input as IEditorPaddingOptions; + + return { + top: EditorIntOption.clampedInt(input.top, 0, 0, 1000), + bottom: EditorIntOption.clampedInt(input.bottom, 0, 0, 1000) + }; + } +} +//#endregion + //#region parameterHints /** @@ -2163,7 +2597,7 @@ class EditorParameterHints extends BaseEditorOption>; @@ -2253,7 +2687,7 @@ class EditorQuickSuggestions extends BaseEditorOption { +export interface IRulerOption { + readonly column: number; + readonly color: string | null; +} + +class EditorRulers extends BaseEditorOption { constructor() { - const defaults: number[] = []; + const defaults: IRulerOption[] = []; + const columnSchema: IJSONSchema = { type: 'number', description: nls.localize('rulers.size', "Number of monospace characters at which this editor ruler will render.") }; super( EditorOption.rulers, 'rulers', defaults, { type: 'array', items: { - type: 'number' + anyOf: [ + columnSchema, + { + type: [ + 'object' + ], + properties: { + column: columnSchema, + color: { + type: 'string', + description: nls.localize('rulers.color', "Color of this editor ruler."), + format: 'color-hex' + } + } + } + ] }, default: defaults, description: nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty.") @@ -2373,13 +2828,24 @@ class EditorRulers extends SimpleEditorOption { ); } - public validate(input: any): number[] { + public validate(input: any): IRulerOption[] { if (Array.isArray(input)) { - let rulers: number[] = []; - for (let value of input) { - rulers.push(EditorIntOption.clampedInt(value, 0, 0, 10000)); + let rulers: IRulerOption[] = []; + for (let _element of input) { + if (typeof _element === 'number') { + rulers.push({ + column: EditorIntOption.clampedInt(_element, 0, 0, 10000), + color: null + }); + } else if (_element && typeof _element === 'object') { + const element = _element as IRulerOption; + rulers.push({ + column: EditorIntOption.clampedInt(element.column, 0, 0, 10000), + color: element.color + }); + } } - rulers.sort((a, b) => a - b); + rulers.sort((a, b) => a.column - b.column); return rulers; } return this.defaultValue; @@ -2494,8 +2960,8 @@ class EditorScrollbar extends BaseEditorOption>; + +class SmartSelect extends BaseEditorOption { + + constructor() { + super( + EditorOption.smartSelect, 'smartSelect', + { + selectLeadingAndTrailingWhitespace: true + }, + { + 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { + description: nls.localize('selectLeadingAndTrailingWhitespace', "Whether leading and trailing whitespace should always be selected."), + default: true, + type: 'boolean' + } + } + ); + } + + public validate(input: any): Readonly> { + if (!input || typeof input !== 'object') { + return this.defaultValue; + } + return { + selectLeadingAndTrailingWhitespace: EditorBooleanOption.boolean((input as ISmartSelectOptions).selectLeadingAndTrailingWhitespace, this.defaultValue.selectLeadingAndTrailingWhitespace) }; } } @@ -3006,67 +3535,17 @@ export interface EditorWrappingInfo { class EditorWrappingInfoComputer extends ComputedEditorOption { constructor() { - super(EditorOption.wrappingInfo, [EditorOption.wordWrap, EditorOption.wordWrapColumn, EditorOption.wordWrapMinified, EditorOption.layoutInfo, EditorOption.accessibilitySupport]); + super(EditorOption.wrappingInfo, [EditorOption.layoutInfo]); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: EditorWrappingInfo): EditorWrappingInfo { - const wordWrap = options.get(EditorOption.wordWrap); - const wordWrapColumn = options.get(EditorOption.wordWrapColumn); - const wordWrapMinified = options.get(EditorOption.wordWrapMinified); const layoutInfo = options.get(EditorOption.layoutInfo); - const accessibilitySupport = options.get(EditorOption.accessibilitySupport); - - let bareWrappingInfo: { isWordWrapMinified: boolean; isViewportWrapping: boolean; wrappingColumn: number; } | null = null; - { - if (accessibilitySupport === AccessibilitySupport.Enabled) { - // See https://github.com/Microsoft/vscode/issues/27766 - // Never enable wrapping when a screen reader is attached - // because arrow down etc. will not move the cursor in the way - // a screen reader expects. - bareWrappingInfo = { - isWordWrapMinified: false, - isViewportWrapping: false, - wrappingColumn: -1 - }; - } else if (wordWrapMinified && env.isDominatedByLongLines) { - // Force viewport width wrapping if model is dominated by long lines - bareWrappingInfo = { - isWordWrapMinified: true, - isViewportWrapping: true, - wrappingColumn: Math.max(1, layoutInfo.viewportColumn) - }; - } else if (wordWrap === 'on') { - bareWrappingInfo = { - isWordWrapMinified: false, - isViewportWrapping: true, - wrappingColumn: Math.max(1, layoutInfo.viewportColumn) - }; - } else if (wordWrap === 'bounded') { - bareWrappingInfo = { - isWordWrapMinified: false, - isViewportWrapping: true, - wrappingColumn: Math.min(Math.max(1, layoutInfo.viewportColumn), wordWrapColumn) - }; - } else if (wordWrap === 'wordWrapColumn') { - bareWrappingInfo = { - isWordWrapMinified: false, - isViewportWrapping: false, - wrappingColumn: wordWrapColumn - }; - } else { - bareWrappingInfo = { - isWordWrapMinified: false, - isViewportWrapping: false, - wrappingColumn: -1 - }; - } - } return { isDominatedByLongLines: env.isDominatedByLongLines, - isWordWrapMinified: bareWrappingInfo.isWordWrapMinified, - isViewportWrapping: bareWrappingInfo.isViewportWrapping, - wrappingColumn: bareWrappingInfo.wrappingColumn, + isWordWrapMinified: layoutInfo.isWordWrapMinified, + isViewportWrapping: layoutInfo.isViewportWrapping, + wrappingColumn: layoutInfo.wrappingColumn, }; } } @@ -3128,6 +3607,7 @@ export const enum EditorOption { autoSurround, codeLens, colorDecorators, + columnSelection, comments, contextmenu, copyWithSyntaxHighlighting, @@ -3148,6 +3628,7 @@ export const enum EditorOption { folding, foldingStrategy, foldingHighlight, + unfoldOnClickAfterEndOfLine, fontFamily, fontInfo, fontLigatures, @@ -3179,15 +3660,19 @@ export const enum EditorOption { occurrencesHighlight, overviewRulerBorder, overviewRulerLanes, + padding, parameterHints, peekWidgetDefaultFocus, + definitionLinkOpensInPeek, quickSuggestions, quickSuggestionsDelay, readOnly, + renameOnType, renderControlCharacters, renderIndentGuides, renderFinalNewline, renderLineHighlight, + renderLineHighlightOnlyWhenFocus, renderValidationDecorations, renderWhitespace, revealHorizontalRightPadding, @@ -3196,13 +3681,14 @@ export const enum EditorOption { scrollbar, scrollBeyondLastColumn, scrollBeyondLastLine, + scrollPredominantAxis, selectionClipboard, selectionHighlight, selectOnLineNumbers, - semanticHighlighting, showFoldingControls, showUnused, snippetSuggestions, + smartSelect, smoothScrolling, stopRenderingLineAfter, suggest, @@ -3211,6 +3697,8 @@ export const enum EditorOption { suggestOnTriggerCharacters, suggestSelection, tabCompletion, + tabIndex, + unusualLineTerminators, useTabStops, wordSeparators, wordWrap, @@ -3219,7 +3707,8 @@ export const enum EditorOption { wordWrapColumn, wordWrapMinified, wrappingIndent, - wrappingAlgorithm, + wrappingStrategy, + showDeprecated, // Leave these at the end (because they have dependencies!) editorClassName, @@ -3339,7 +3828,7 @@ export const EditorOptions = { nls.localize('editor.autoSurround.brackets', "Surround with brackets but not quotes."), '' ], - description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections.") + description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections when typing quotes or brackets.") } )), codeLens: register(new EditorBooleanOption( @@ -3350,6 +3839,10 @@ export const EditorOptions = { EditorOption.colorDecorators, 'colorDecorators', true, { description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") } )), + columnSelection: register(new EditorBooleanOption( + EditorOption.columnSelection, 'columnSelection', false, + { description: nls.localize('columnSelection', "Enable that the selection with the mouse and keys is doing column selection.") } + )), comments: register(new EditorComments()), contextmenu: register(new EditorBooleanOption( EditorOption.contextmenu, 'contextmenu', true, @@ -3379,7 +3872,7 @@ export const EditorOptions = { cursorSurroundingLines: register(new EditorIntOption( EditorOption.cursorSurroundingLines, 'cursorSurroundingLines', 0, 0, Constants.MAX_SAFE_SMALL_INTEGER, - { description: nls.localize('cursorSurroundingLines', "Controls the minimal number of visible leading and trailing lines surrounding the cursor. Known as 'scrollOff' or `scrollOffset` in some other editors.") } + { description: nls.localize('cursorSurroundingLines', "Controls the minimal number of visible leading and trailing lines surrounding the cursor. Known as 'scrollOff' or 'scrollOffset' in some other editors.") } )), cursorSurroundingLinesStyle: register(new EditorStringEnumOption( EditorOption.cursorSurroundingLinesStyle, 'cursorSurroundingLinesStyle', @@ -3429,12 +3922,22 @@ export const EditorOptions = { EditorOption.foldingStrategy, 'foldingStrategy', 'auto' as 'auto' | 'indentation', ['auto', 'indentation'] as const, - { markdownDescription: nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges. `auto` uses a language specific folding strategy, if available. `indentation` uses the indentation based folding strategy.") } + { + enumDescriptions: [ + nls.localize('foldingStrategy.auto', "Use a language-specific folding strategy if available, else the indentation-based one."), + nls.localize('foldingStrategy.indentation', "Use the indentation-based folding strategy."), + ], + description: nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges.") + } )), foldingHighlight: register(new EditorBooleanOption( EditorOption.foldingHighlight, 'foldingHighlight', true, { description: nls.localize('foldingHighlight', "Controls whether the editor should highlight folded ranges.") } )), + unfoldOnClickAfterEndOfLine: register(new EditorBooleanOption( + EditorOption.unfoldOnClickAfterEndOfLine, 'unfoldOnClickAfterEndOfLine', false, + { description: nls.localize('unfoldOnClickAfterEndOfLine', "Controls whether clicking on the empty content after a folded line will unfold the line.") } + )), fontFamily: register(new EditorStringOption( EditorOption.fontFamily, 'fontFamily', EDITOR_FONT_DEFAULTS.fontFamily, { description: nls.localize('fontFamily', "Controls the font family.") } @@ -3442,13 +3945,7 @@ export const EditorOptions = { fontInfo: register(new EditorFontInfo()), fontLigatures2: register(new EditorFontLigatures()), fontSize: register(new EditorFontSize()), - fontWeight: register(new EditorStringOption( - EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight, - { - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: nls.localize('fontWeight', "Controls the font weight.") - } - )), + fontWeight: register(new EditorFontWeight()), formatOnPaste: register(new EditorBooleanOption( EditorOption.formatOnPaste, 'formatOnPaste', false, { description: nls.localize('formatOnPaste', "Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.") } @@ -3559,6 +4056,7 @@ export const EditorOptions = { EditorOption.overviewRulerLanes, 'overviewRulerLanes', 3, 0, 3 )), + padding: register(new EditorPadding()), parameterHints: register(new EditorParameterHints()), peekWidgetDefaultFocus: register(new EditorStringEnumOption( EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus', @@ -3566,12 +4064,16 @@ export const EditorOptions = { ['tree', 'editor'] as const, { enumDescriptions: [ - nls.localize('peekWidgetDefaultFocus.tree', "Focus the tree when openeing peek"), + nls.localize('peekWidgetDefaultFocus.tree', "Focus the tree when opening peek"), nls.localize('peekWidgetDefaultFocus.editor', "Focus the editor when opening peek") ], description: nls.localize('peekWidgetDefaultFocus', "Controls whether to focus the inline editor or the tree in the peek widget.") } )), + definitionLinkOpensInPeek: register(new EditorBooleanOption( + EditorOption.definitionLinkOpensInPeek, 'definitionLinkOpensInPeek', false, + { description: nls.localize('definitionLinkOpensInPeek', "Controls whether the Go to Definition mouse gesture always opens the peek widget.") } + )), quickSuggestions: register(new EditorQuickSuggestions()), quickSuggestionsDelay: register(new EditorIntOption( EditorOption.quickSuggestionsDelay, 'quickSuggestionsDelay', @@ -3581,6 +4083,10 @@ export const EditorOptions = { readOnly: register(new EditorBooleanOption( EditorOption.readOnly, 'readOnly', false, )), + renameOnType: register(new EditorBooleanOption( + EditorOption.renameOnType, 'renameOnType', false, + { description: nls.localize('renameOnType', "Controls whether the editor auto renames on type.") } + )), renderControlCharacters: register(new EditorBooleanOption( EditorOption.renderControlCharacters, 'renderControlCharacters', false, { description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters.") } @@ -3607,6 +4113,10 @@ export const EditorOptions = { description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight.") } )), + renderLineHighlightOnlyWhenFocus: register(new EditorBooleanOption( + EditorOption.renderLineHighlightOnlyWhenFocus, 'renderLineHighlightOnlyWhenFocus', false, + { description: nls.localize('renderLineHighlightOnlyWhenFocus', "Controls if the editor should render the current line highlight only when the editor is focused") } + )), renderValidationDecorations: register(new EditorStringEnumOption( EditorOption.renderValidationDecorations, 'renderValidationDecorations', 'editable' as 'editable' | 'on' | 'off', @@ -3614,13 +4124,14 @@ export const EditorOptions = { )), renderWhitespace: register(new EditorStringEnumOption( EditorOption.renderWhitespace, 'renderWhitespace', - 'none' as 'none' | 'boundary' | 'selection' | 'all', - ['none', 'boundary', 'selection', 'all'] as const, + 'selection' as 'selection' | 'none' | 'boundary' | 'trailing' | 'all', + ['none', 'boundary', 'selection', 'trailing', 'all'] as const, { enumDescriptions: [ '', nls.localize('renderWhitespace.boundary', "Render whitespace characters except for single spaces between words."), nls.localize('renderWhitespace.selection', "Render whitespace characters only on selected text."), + nls.localize('renderWhitespace.trailing', "Render only trailing whitespace characters"), '' ], description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.") @@ -3645,6 +4156,10 @@ export const EditorOptions = { EditorOption.scrollBeyondLastLine, 'scrollBeyondLastLine', true, { description: nls.localize('scrollBeyondLastLine', "Controls whether the editor will scroll beyond the last line.") } )), + scrollPredominantAxis: register(new EditorBooleanOption( + EditorOption.scrollPredominantAxis, 'scrollPredominantAxis', true, + { description: nls.localize('scrollPredominantAxis', "Scroll only along the predominant axis when scrolling both vertically and horizontally at the same time. Prevents horizontal drift when scrolling vertically on a trackpad.") } + )), selectionClipboard: register(new EditorBooleanOption( EditorOption.selectionClipboard, 'selectionClipboard', true, { @@ -3659,17 +4174,26 @@ export const EditorOptions = { selectOnLineNumbers: register(new EditorBooleanOption( EditorOption.selectOnLineNumbers, 'selectOnLineNumbers', true, )), - semanticHighlighting: register(new EditorSemanticHighlighting()), showFoldingControls: register(new EditorStringEnumOption( EditorOption.showFoldingControls, 'showFoldingControls', 'mouseover' as 'always' | 'mouseover', ['always', 'mouseover'] as const, - { description: nls.localize('showFoldingControls', "Controls whether the fold controls on the gutter are automatically hidden.") } + { + enumDescriptions: [ + nls.localize('showFoldingControls.always', "Always show the folding controls."), + nls.localize('showFoldingControls.mouseover', "Only show the folding controls when the mouse is over the gutter."), + ], + description: nls.localize('showFoldingControls', "Controls when the folding controls on the gutter are shown.") + } )), showUnused: register(new EditorBooleanOption( EditorOption.showUnused, 'showUnused', true, { description: nls.localize('showUnused', "Controls fading out of unused code.") } )), + showDeprecated: register(new EditorBooleanOption( + EditorOption.showDeprecated, 'showDeprecated', true, + { description: nls.localize('showDeprecated', "Controls strikethrough deprecated variables.") } + )), snippetSuggestions: register(new EditorStringEnumOption( EditorOption.snippetSuggestions, 'snippetSuggestions', 'inline' as 'top' | 'bottom' | 'inline' | 'none', @@ -3684,6 +4208,7 @@ export const EditorOptions = { description: nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.") } )), + smartSelect: register(new SmartSelect()), smoothScrolling: register(new EditorBooleanOption( EditorOption.smoothScrolling, 'smoothScrolling', false, { description: nls.localize('smoothScrolling', "Controls whether the editor will scroll using an animation.") } @@ -3733,6 +4258,23 @@ export const EditorOptions = { description: nls.localize('tabCompletion', "Enables tab completions.") } )), + tabIndex: register(new EditorIntOption( + EditorOption.tabIndex, 'tabIndex', + 0, -1, Constants.MAX_SAFE_SMALL_INTEGER + )), + unusualLineTerminators: register(new EditorStringEnumOption( + EditorOption.unusualLineTerminators, 'unusualLineTerminators', + 'prompt' as 'auto' | 'off' | 'prompt', + ['auto', 'off', 'prompt'] as const, + { + enumDescriptions: [ + nls.localize('unusualLineTerminators.auto', "Unusual line terminators are automatically removed."), + nls.localize('unusualLineTerminators.off', "Unusual line terminators are ignored."), + nls.localize('unusualLineTerminators.prompt', "Unusual line terminators prompt to be removed."), + ], + description: nls.localize('unusualLineTerminators', "Remove unusual line terminators that might cause problems.") + } + )), useTabStops: register(new EditorBooleanOption( EditorOption.useTabStops, 'useTabStops', true, { description: nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.") } @@ -3811,16 +4353,16 @@ export const EditorOptions = { description: nls.localize('wrappingIndent', "Controls the indentation of wrapped lines."), } )), - wrappingAlgorithm: register(new EditorStringEnumOption( - EditorOption.wrappingAlgorithm, 'wrappingAlgorithm', - 'monospace' as 'monospace' | 'dom', - ['monospace', 'dom'] as const, + wrappingStrategy: register(new EditorStringEnumOption( + EditorOption.wrappingStrategy, 'wrappingStrategy', + 'simple' as 'simple' | 'advanced', + ['simple', 'advanced'] as const, { enumDescriptions: [ - nls.localize('wrappingAlgorithm.monospace', "Assumes that all characters are of the same width. This is a fast algorithm."), - nls.localize('wrappingAlgorithm.dom', "Delegates wrapping points computation to the DOM. This is a slow algorithm, that might cause freezes for large files.") + nls.localize('wrappingStrategy.simple', "Assumes that all characters are of the same width. This is a fast algorithm that works correctly for monospace fonts and certain scripts (like Latin characters) where glyphs are of equal width."), + nls.localize('wrappingStrategy.advanced', "Delegates wrapping points computation to the browser. This is a slow algorithm, that might cause freezes for large files, but it works correctly in all cases.") ], - description: nls.localize('wrappingAlgorithm', "Controls the algorithm that computes wrapping points.") + description: nls.localize('wrappingStrategy', "Controls the algorithm that computes wrapping points.") } )), diff --git a/src/vs/editor/common/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts index 1e393d5d10f..e5d21cb623a 100644 --- a/src/vs/editor/common/config/fontInfo.ts +++ b/src/vs/editor/common/config/fontInfo.ts @@ -135,6 +135,7 @@ export class FontInfo extends BareFontInfo { readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; readonly middotWidth: number; + readonly wsmiddotWidth: number; readonly maxDigitWidth: number; /** @@ -154,6 +155,7 @@ export class FontInfo extends BareFontInfo { canUseHalfwidthRightwardsArrow: boolean; spaceWidth: number; middotWidth: number; + wsmiddotWidth: number; maxDigitWidth: number; }, isTrusted: boolean) { super(opts); @@ -164,6 +166,7 @@ export class FontInfo extends BareFontInfo { this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow; this.spaceWidth = opts.spaceWidth; this.middotWidth = opts.middotWidth; + this.wsmiddotWidth = opts.wsmiddotWidth; this.maxDigitWidth = opts.maxDigitWidth; } @@ -183,6 +186,7 @@ export class FontInfo extends BareFontInfo { && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.spaceWidth === other.spaceWidth && this.middotWidth === other.middotWidth + && this.wsmiddotWidth === other.wsmiddotWidth && this.maxDigitWidth === other.maxDigitWidth ); } diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 2b4be5b293c..7a966ed223e 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -4,69 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; import * as strings from 'vs/base/common/strings'; import { CursorCollection } from 'vs/editor/common/controller/cursorCollection'; -import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, PartialCursorState, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { TypeOperations, TypeWithAutoClosingCommand } from 'vs/editor/common/controller/cursorTypeOperations'; import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer } from 'vs/editor/common/model'; -import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { dispose } from 'vs/base/common/lifecycle'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; - -function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean { - for (let i = 0, len = events.length; i < len; i++) { - if (events[i].type === viewEvents.ViewEventType.ViewLineMappingChanged) { - return true; - } - } - return false; -} - -export class CursorStateChangedEvent { - /** - * The new selections. - * The primary selection is always at index 0. - */ - readonly selections: Selection[]; - /** - * The new model version id that `selections` apply to. - */ - readonly modelVersionId: number; - /** - * The old selections. - */ - readonly oldSelections: Selection[] | null; - /** - * The model version id the that `oldSelections` apply to. - */ - readonly oldModelVersionId: number; - /** - * Source of the call that caused the event. - */ - readonly source: string; - /** - * Reason. - */ - readonly reason: CursorChangeReason; - - constructor(selections: Selection[], modelVersionId: number, oldSelections: Selection[] | null, oldModelVersionId: number, source: string, reason: CursorChangeReason) { - this.selections = selections; - this.modelVersionId = modelVersionId; - this.oldSelections = oldSelections; - this.oldModelVersionId = oldModelVersionId; - this.source = source; - this.reason = reason; - } -} +import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model'; +import { RawContentChangedType, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { VerticalRevealType, ViewCursorStateChangedEvent, ViewRevealRangeRequestEvent } from 'vs/editor/common/view/viewEvents'; +import { dispose, Disposable } from 'vs/base/common/lifecycle'; +import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; +import { CursorStateChangedEvent, ViewModelEventsCollector } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; /** * A snapshot of the cursor and the model state @@ -78,7 +31,7 @@ export class CursorModelState { constructor(model: ITextModel, cursor: Cursor) { this.modelVersionId = model.getVersionId(); - this.cursorState = cursor.getAll(); + this.cursorState = cursor.getCursorStates(); } public equals(other: CursorModelState | null): boolean { @@ -166,26 +119,18 @@ class AutoClosedAction { } } -export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { +export class Cursor extends Disposable { public static readonly MAX_CURSOR_COUNT = 10000; - private readonly _onDidReachMaxCursorCount: Emitter = this._register(new Emitter()); - public readonly onDidReachMaxCursorCount: Event = this._onDidReachMaxCursorCount.event; - - private readonly _onDidAttemptReadOnlyEdit: Emitter = this._register(new Emitter()); - public readonly onDidAttemptReadOnlyEdit: Event = this._onDidAttemptReadOnlyEdit.event; - - private readonly _onDidChange: Emitter = this._register(new Emitter()); - public readonly onDidChange: Event = this._onDidChange.event; - - private readonly _configuration: editorCommon.IConfiguration; private readonly _model: ITextModel; private _knownModelVersionId: number; - private readonly _viewModel: IViewModel; + private readonly _viewModel: ICursorSimpleModel; + private readonly _coordinatesConverter: ICoordinatesConverter; public context: CursorContext; private _cursors: CursorCollection; + private _hasFocus: boolean; private _isHandling: boolean; private _isDoingComposition: boolean; private _selectionsWhenCompositionStarted: Selection[] | null; @@ -193,69 +138,22 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { private _autoClosedActions: AutoClosedAction[]; private _prevEditOperationType: EditOperationType; - constructor(configuration: editorCommon.IConfiguration, model: ITextModel, viewModel: IViewModel) { + constructor(model: ITextModel, viewModel: ICursorSimpleModel, coordinatesConverter: ICoordinatesConverter, cursorConfig: CursorConfiguration) { super(); - this._configuration = configuration; this._model = model; this._knownModelVersionId = this._model.getVersionId(); this._viewModel = viewModel; - this.context = new CursorContext(this._configuration, this._model, this._viewModel); + this._coordinatesConverter = coordinatesConverter; + this.context = new CursorContext(this._model, this._coordinatesConverter, cursorConfig); this._cursors = new CursorCollection(this.context); + this._hasFocus = false; this._isHandling = false; this._isDoingComposition = false; this._selectionsWhenCompositionStarted = null; this._columnSelectData = null; this._autoClosedActions = []; this._prevEditOperationType = EditOperationType.Other; - - this._register(this._model.onDidChangeRawContent((e) => { - this._knownModelVersionId = e.versionId; - if (this._isHandling) { - return; - } - - let hadFlushEvent = e.containsEvent(RawContentChangedType.Flush); - this._onModelContentChanged(hadFlushEvent); - })); - - this._register(viewModel.addEventListener((events: viewEvents.ViewEvent[]) => { - if (!containsLineMappingChanged(events)) { - return; - } - - if (this._knownModelVersionId !== this._model.getVersionId()) { - // There are model change events that I didn't yet receive. - // - // This can happen when editing the model, and the view model receives the change events first, - // and the view model emits line mapping changed events, all before the cursor gets a chance to - // recover from markers. - // - // The model change listener above will be called soon and we'll ensure a valid cursor state there. - return; - } - // Ensure valid state - this.setStates('viewModel', CursorChangeReason.NotSet, this.getAll()); - })); - - const updateCursorContext = () => { - this.context = new CursorContext(this._configuration, this._model, this._viewModel); - this._cursors.updateContext(this.context); - }; - this._register(this._model.onDidChangeLanguage((e) => { - updateCursorContext(); - })); - this._register(this._model.onDidChangeLanguageConfiguration(() => { - updateCursorContext(); - })); - this._register(this._model.onDidChangeOptions(() => { - updateCursorContext(); - })); - this._register(this._configuration.onDidChange((e) => { - if (CursorConfiguration.shouldRecreate(e)) { - updateCursorContext(); - } - })); } public dispose(): void { @@ -264,6 +162,30 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { super.dispose(); } + public updateConfiguration(cursorConfig: CursorConfiguration): void { + this.context = new CursorContext(this._model, this._coordinatesConverter, cursorConfig); + this._cursors.updateContext(this.context); + } + + public onLineMappingChanged(eventsCollector: ViewModelEventsCollector): void { + if (this._knownModelVersionId !== this._model.getVersionId()) { + // There are model change events that I didn't yet receive. + // + // This can happen when editing the model, and the view model receives the change events first, + // and the view model emits line mapping changed events, all before the cursor gets a chance to + // recover from markers. + // + // The model change listener above will be called soon and we'll ensure a valid cursor state there. + return; + } + // Ensure valid state + this.setStates(eventsCollector, 'viewModel', CursorChangeReason.NotSet, this.getCursorStates()); + } + + public setHasFocus(hasFocus: boolean): void { + this._hasFocus = hasFocus; + } + private _validateAutoClosedActions(): void { if (this._autoClosedActions.length > 0) { let selections: Range[] = this._cursors.getSelections(); @@ -280,7 +202,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { // ------ some getters/setters - public getPrimaryCursor(): CursorState { + public getPrimaryCursorState(): CursorState { return this._cursors.getPrimaryCursor(); } @@ -288,14 +210,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return this._cursors.getLastAddedCursorIndex(); } - public getAll(): CursorState[] { + public getCursorStates(): CursorState[] { return this._cursors.getAll(); } - public setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): void { + public setStates(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean { + let reachedMaxCursorCount = false; if (states !== null && states.length > Cursor.MAX_CURSOR_COUNT) { states = states.slice(0, Cursor.MAX_CURSOR_COUNT); - this._onDidReachMaxCursorCount.fire(undefined); + reachedMaxCursorCount = true; } const oldState = new CursorModelState(this._model, this); @@ -306,25 +229,38 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._validateAutoClosedActions(); - this._emitStateChangedIfNecessary(source, reason, oldState); + return this._emitStateChangedIfNecessary(eventsCollector, source, reason, oldState, reachedMaxCursorCount); } - public setColumnSelectData(columnSelectData: IColumnSelectData): void { + public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void { this._columnSelectData = columnSelectData; } - public reveal(source: string, horizontal: boolean, target: RevealTarget, scrollType: editorCommon.ScrollType): void { - this._revealRange(source, target, viewEvents.VerticalRevealType.Simple, horizontal, scrollType); + public revealPrimary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { + const viewPositions = this._cursors.getViewPositions(); + if (viewPositions.length > 1) { + this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), VerticalRevealType.Simple, revealHorizontal, scrollType); + return; + } else { + const viewPosition = viewPositions[0]; + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this._emitCursorRevealRange(eventsCollector, source, viewRange, null, VerticalRevealType.Simple, revealHorizontal, scrollType); + } } - public revealRange(source: string, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: editorCommon.ScrollType) { - this.emitCursorRevealRange(source, viewRange, verticalType, revealHorizontal, scrollType); + private _revealPrimaryCursor(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { + const viewPositions = this._cursors.getViewPositions(); + if (viewPositions.length > 1) { + this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType); + } else { + const viewPosition = viewPositions[0]; + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this._emitCursorRevealRange(eventsCollector, source, viewRange, null, verticalType, revealHorizontal, scrollType); + } } - public scrollTo(desiredScrollTop: number): void { - this._viewModel.viewLayout.setScrollPositionSmooth({ - scrollTop: desiredScrollTop - }); + private _emitCursorRevealRange(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) { + eventsCollector.emitViewEvent(new ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType)); } public saveState(): editorCommon.ICursorState[] { @@ -351,7 +287,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return result; } - public restoreState(states: editorCommon.ICursorState[]): void { + public restoreState(eventsCollector: ViewModelEventsCollector, states: editorCommon.ICursorState[]): void { let desiredSelections: ISelection[] = []; @@ -388,12 +324,18 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { }); } - this.setStates('restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections)); - this.reveal('restoreState', true, RevealTarget.Primary, editorCommon.ScrollType.Immediate); + this.setStates(eventsCollector, 'restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections)); + this.revealPrimary(eventsCollector, 'restoreState', true, editorCommon.ScrollType.Immediate); } - private _onModelContentChanged(hadFlushEvent: boolean): void { + public onModelContentChanged(eventsCollector: ViewModelEventsCollector, e: ModelRawContentChangedEvent): void { + this._knownModelVersionId = e.versionId; + if (this._isHandling) { + return; + } + + const hadFlushEvent = e.containsEvent(RawContentChangedType.Flush); this._prevEditOperationType = EditOperationType.Other; if (hadFlushEvent) { @@ -401,10 +343,17 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._cursors.dispose(); this._cursors = new CursorCollection(this.context); this._validateAutoClosedActions(); - this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null); + this._emitStateChangedIfNecessary(eventsCollector, 'model', CursorChangeReason.ContentFlush, null, false); } else { - const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); - this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); + if (this._hasFocus && e.resultingSelection && e.resultingSelection.length > 0) { + const cursorState = CursorState.fromModelSelections(e.resultingSelection); + if (this.setStates(eventsCollector, 'modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) { + this._revealPrimaryCursor(eventsCollector, 'modelChange', VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); + } + } else { + const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); + this.setStates(eventsCollector, 'modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); + } } } @@ -412,20 +361,27 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return this._cursors.getPrimaryCursor().modelState.selection; } - public getColumnSelectData(): IColumnSelectData { + public getTopMostViewPosition(): Position { + return this._cursors.getTopMostViewPosition(); + } + + public getBottomMostViewPosition(): Position { + return this._cursors.getBottomMostViewPosition(); + } + + public getCursorColumnSelectData(): IColumnSelectData { if (this._columnSelectData) { return this._columnSelectData; } const primaryCursor = this._cursors.getPrimaryCursor(); - const primaryPos = primaryCursor.viewState.selectionStart.getStartPosition(); - const viewLineNumber = primaryPos.lineNumber; - const viewVisualColumn = CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, primaryPos); + const viewSelectionStart = primaryCursor.viewState.selectionStart.getStartPosition(); + const viewPosition = primaryCursor.viewState.position; return { isReal: false, - fromViewLineNumber: viewLineNumber, - fromViewVisualColumn: viewVisualColumn, - toViewLineNumber: viewLineNumber, - toViewVisualColumn: viewVisualColumn, + fromViewLineNumber: viewSelectionStart.lineNumber, + fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewSelectionStart), + toViewLineNumber: viewPosition.lineNumber, + toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewPosition), }; } @@ -433,16 +389,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return this._cursors.getSelections(); } - public getViewSelections(): Selection[] { - return this._cursors.getViewSelections(); - } - public getPosition(): Position { return this._cursors.getPrimaryCursor().modelState.position; } - public setSelections(source: string, selections: readonly ISelection[]): void { - this.setStates(source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections)); + public setSelections(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, selections: readonly ISelection[]): void { + this.setStates(eventsCollector, source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections)); } public getPrevEditOperationType(): EditOperationType { @@ -533,7 +485,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { // ----------------------------------------------------------------------------------------------------------- // ----- emitting events - private _emitStateChangedIfNecessary(source: string, reason: CursorChangeReason, oldState: CursorModelState | null): boolean { + private _emitStateChangedIfNecessary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, oldState: CursorModelState | null, reachedMaxCursorCount: boolean): boolean { const newState = new CursorModelState(this._model, this); if (newState.equals(oldState)) { return false; @@ -543,12 +495,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { const viewSelections = this._cursors.getViewSelections(); // Let the view get the event first. - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections)); - } finally { - this._endEmit(); - } + eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections)); // Only after the view has been notified, let the rest of the world know... if (!oldState @@ -557,49 +504,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { ) { const oldSelections = oldState ? oldState.cursorState.map(s => s.modelState.selection) : null; const oldModelVersionId = oldState ? oldState.modelVersionId : 0; - this._onDidChange.fire(new CursorStateChangedEvent(selections, newState.modelVersionId, oldSelections, oldModelVersionId, source || 'keyboard', reason)); + eventsCollector.emitOutgoingEvent(new CursorStateChangedEvent(oldSelections, selections, oldModelVersionId, newState.modelVersionId, source || 'keyboard', reason, reachedMaxCursorCount)); } return true; } - private _revealRange(source: string, revealTarget: RevealTarget, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { - const viewPositions = this._cursors.getViewPositions(); - - let viewPosition = viewPositions[0]; - - if (revealTarget === RevealTarget.TopMost) { - for (let i = 1; i < viewPositions.length; i++) { - if (viewPositions[i].isBefore(viewPosition)) { - viewPosition = viewPositions[i]; - } - } - } else if (revealTarget === RevealTarget.BottomMost) { - for (let i = 1; i < viewPositions.length; i++) { - if (viewPosition.isBeforeOrEqual(viewPositions[i])) { - viewPosition = viewPositions[i]; - } - } - } else { - if (viewPositions.length > 1) { - // no revealing! - return; - } - } - - const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); - this.emitCursorRevealRange(source, viewRange, verticalType, revealHorizontal, scrollType); - } - - public emitCursorRevealRange(source: string, viewRange: Range, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, verticalType, revealHorizontal, scrollType)); - } finally { - this._endEmit(); - } - } - // ----------------------------------------------------------------------------------------------------------- // ----- handlers beyond this point @@ -621,7 +531,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { } const closeChar = m[1]; - const autoClosingPairsCandidates = this.context.config.autoClosingPairsClose2.get(closeChar); + const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairsClose2.get(closeChar); if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) { return null; } @@ -639,7 +549,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return indices; } - public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void { + public executeEdits(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void { let autoClosingIndices: [number, number][] | null = null; if (source === 'snippet') { autoClosingIndices = this._findAutoClosingPairs(edits); @@ -674,162 +584,117 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { }); if (selections) { this._isHandling = false; - this.setSelections(source, selections); + this.setSelections(eventsCollector, source, selections); } if (autoClosedCharactersRanges.length > 0) { this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges); } } - public trigger(source: string, handlerId: string, payload: any): void { - const H = editorCommon.Handler; - - if (handlerId === H.CompositionStart) { - this._isDoingComposition = true; - this._selectionsWhenCompositionStarted = this.getSelections().slice(0); - return; - } - - if (handlerId === H.CompositionEnd) { - this._isDoingComposition = false; - } - - if (this._configuration.options.get(EditorOption.readOnly)) { - // All the remaining handlers will try to edit the model, - // but we cannot edit when read only... - this._onDidAttemptReadOnlyEdit.fire(undefined); + private _executeEdit(callback: () => void, eventsCollector: ViewModelEventsCollector, source: string | null | undefined, cursorChangeReason: CursorChangeReason = CursorChangeReason.NotSet): void { + if (this.context.cursorConfig.readOnly) { + // we cannot edit when read only... return; } const oldState = new CursorModelState(this._model, this); - let cursorChangeReason = CursorChangeReason.NotSet; - - if (handlerId !== H.Undo && handlerId !== H.Redo) { - // TODO@Alex: if the undo/redo stack contains non-null selections - // it would also be OK to stop tracking selections here - this._cursors.stopTrackingSelections(); - } - - // ensure valid state on all cursors - this._cursors.ensureValidState(); - + this._cursors.stopTrackingSelections(); this._isHandling = true; try { - switch (handlerId) { - case H.Type: - this._type(source, payload.text); - break; - - case H.ReplacePreviousChar: - this._replacePreviousChar(payload.text, payload.replaceCharCnt); - break; - - case H.Paste: - cursorChangeReason = CursorChangeReason.Paste; - this._paste(payload.text, payload.pasteOnNewLine, payload.multicursorText || []); - break; - - case H.Cut: - this._cut(); - break; - - case H.Undo: - cursorChangeReason = CursorChangeReason.Undo; - this._interpretCommandResult(this._model.undo()); - break; - - case H.Redo: - cursorChangeReason = CursorChangeReason.Redo; - this._interpretCommandResult(this._model.redo()); - break; - - case H.ExecuteCommand: - this._externalExecuteCommand(payload); - break; - - case H.ExecuteCommands: - this._externalExecuteCommands(payload); - break; - - case H.CompositionEnd: - this._interpretCompositionEnd(source); - break; - } + this._cursors.ensureValidState(); + callback(); } catch (err) { onUnexpectedError(err); } this._isHandling = false; - - if (handlerId !== H.Undo && handlerId !== H.Redo) { - this._cursors.startTrackingSelections(); - } - + this._cursors.startTrackingSelections(); this._validateAutoClosedActions(); - - if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) { - this._revealRange(source, RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); + if (this._emitStateChangedIfNecessary(eventsCollector, source, cursorChangeReason, oldState, false)) { + this._revealPrimaryCursor(eventsCollector, source, VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); } } - private _interpretCompositionEnd(source: string) { - if (!this._isDoingComposition && source === 'keyboard') { - // composition finishes, let's check if we need to auto complete if necessary. - const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); - this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters)); - this._selectionsWhenCompositionStarted = null; - } + public setIsDoingComposition(isDoingComposition: boolean): void { + this._isDoingComposition = isDoingComposition; } - private _type(source: string, text: string): void { - if (!this._isDoingComposition && source === 'keyboard') { - // If this event is coming straight from the keyboard, look for electric characters and enter + public startComposition(eventsCollector: ViewModelEventsCollector): void { + this._selectionsWhenCompositionStarted = this.getSelections().slice(0); + } - const len = text.length; - let offset = 0; - while (offset < len) { - const charLength = strings.nextCharLength(text, offset); - const chr = text.substr(offset, charLength); - - // Here we must interpret each typed character individually + public endComposition(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void { + this._executeEdit(() => { + if (source === 'keyboard') { + // composition finishes, let's check if we need to auto complete if necessary. const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); - this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr)); - - offset += charLength; + this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters)); + this._selectionsWhenCompositionStarted = null; } - - } else { - this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text)); - } + }, eventsCollector, source); } - private _replacePreviousChar(text: string, replaceCharCnt: number): void { - this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt)); + public type(eventsCollector: ViewModelEventsCollector, text: string, source?: string | null | undefined): void { + this._executeEdit(() => { + if (source === 'keyboard') { + // If this event is coming straight from the keyboard, look for electric characters and enter + + const len = text.length; + let offset = 0; + while (offset < len) { + const charLength = strings.nextCharLength(text, offset); + const chr = text.substr(offset, charLength); + + // Here we must interpret each typed character individually + const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); + this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), autoClosedCharacters, chr)); + + offset += charLength; + } + + } else { + this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text)); + } + }, eventsCollector, source); } - private _paste(text: string, pasteOnNewLine: boolean, multicursorText: string[]): void { - this._executeEditOperation(TypeOperations.paste(this.context.config, this.context.model, this.getSelections(), text, pasteOnNewLine, multicursorText)); + public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt)); + }, eventsCollector, source); } - private _cut(): void { - this._executeEditOperation(DeleteOperations.cut(this.context.config, this.context.model, this.getSelections())); + public paste(eventsCollector: ViewModelEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), text, pasteOnNewLine, multicursorText || [])); + }, eventsCollector, source, CursorChangeReason.Paste); } - private _externalExecuteCommand(command: editorCommon.ICommand): void { - this._cursors.killSecondaryCursors(); - - this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: false - })); + public cut(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(DeleteOperations.cut(this.context.cursorConfig, this._model, this.getSelections())); + }, eventsCollector, source); } - private _externalExecuteCommands(commands: editorCommon.ICommand[]): void { - this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: false - })); + public executeCommand(eventsCollector: ViewModelEventsCollector, command: editorCommon.ICommand, source?: string | null | undefined): void { + this._executeEdit(() => { + this._cursors.killSecondaryCursors(); + + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + }, eventsCollector, source); + } + + public executeCommands(eventsCollector: ViewModelEventsCollector, commands: editorCommon.ICommand[], source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + }, eventsCollector, source); } } @@ -903,8 +768,8 @@ class CommandExecutor { if (commandsData.hadTrackedEditOperation && filteredOperations.length > 0) { filteredOperations[0]._isTracked = true; } - let selectionsAfter = ctx.model.pushEditOperations(ctx.selectionsBefore, filteredOperations, (inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] => { - let groupedInverseEditOperations: IIdentifiedSingleEditOperation[][] = []; + let selectionsAfter = ctx.model.pushEditOperations(ctx.selectionsBefore, filteredOperations, (inverseEditOperations: IValidEditOperation[]): Selection[] => { + let groupedInverseEditOperations: IValidEditOperation[][] = []; for (let i = 0; i < ctx.selectionsBefore.length; i++) { groupedInverseEditOperations[i] = []; } @@ -915,7 +780,7 @@ class CommandExecutor { } groupedInverseEditOperations[op.identifier.major].push(op); } - const minorBasedSorter = (a: IIdentifiedSingleEditOperation, b: IIdentifiedSingleEditOperation) => { + const minorBasedSorter = (a: IValidEditOperation, b: IValidEditOperation) => { return a.identifier!.minor - b.identifier!.minor; }; let cursorSelections: Selection[] = []; @@ -1000,8 +865,8 @@ class CommandExecutor { let operations: IIdentifiedSingleEditOperation[] = []; let operationMinor = 0; - const addEditOperation = (selection: Range, text: string | null, forceMoveMarkers: boolean = false) => { - if (selection.isEmpty() && text === '') { + const addEditOperation = (range: IRange, text: string | null, forceMoveMarkers: boolean = false) => { + if (Range.isEmpty(range) && text === '') { // This command wants to add a no-op => no thank you return; } @@ -1010,7 +875,7 @@ class CommandExecutor { major: majorIdentifier, minor: operationMinor++ }, - range: selection, + range: range, text: text, forceMoveMarkers: forceMoveMarkers, isAutoWhitespaceEdit: command.insertsAutoWhitespace @@ -1018,12 +883,13 @@ class CommandExecutor { }; let hadTrackedEditOperation = false; - const addTrackedEditOperation = (selection: Range, text: string | null, forceMoveMarkers?: boolean) => { + const addTrackedEditOperation = (selection: IRange, text: string | null, forceMoveMarkers?: boolean) => { hadTrackedEditOperation = true; addEditOperation(selection, text, forceMoveMarkers); }; - const trackSelection = (selection: Selection, trackPreviousOnEmpty?: boolean) => { + const trackSelection = (_selection: ISelection, trackPreviousOnEmpty?: boolean) => { + const selection = Selection.liftSelection(_selection); let stickiness: TrackedRangeStickiness; if (selection.isEmpty()) { if (typeof trackPreviousOnEmpty === 'boolean') { @@ -1093,7 +959,7 @@ class CommandExecutor { const previousOp = operations[i - 1]; const currentOp = operations[i]; - if (previousOp.range.getStartPosition().isBefore(currentOp.range.getEndPosition())) { + if (Range.getStartPosition(previousOp.range).isBefore(Range.getEndPosition(currentOp.range))) { let loserMajor: number; diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts index 63cc38e4862..746039533ad 100644 --- a/src/vs/editor/common/controller/cursorCollection.ts +++ b/src/vs/editor/common/controller/cursorCollection.ts @@ -82,6 +82,28 @@ export class CursorCollection { return result; } + public getTopMostViewPosition(): Position { + let result = this.primaryCursor.viewState.position; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + const viewPosition = this.secondaryCursors[i].viewState.position; + if (viewPosition.isBefore(result)) { + result = viewPosition; + } + } + return result; + } + + public getBottomMostViewPosition(): Position { + let result = this.primaryCursor.viewState.position; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + const viewPosition = this.secondaryCursors[i].viewState.position; + if (result.isBeforeOrEqual(viewPosition)) { + result = viewPosition; + } + } + return result; + } + public getSelections(): Selection[] { let result: Selection[] = []; result[0] = this.primaryCursor.modelState.selection; @@ -204,7 +226,7 @@ export class CursorCollection { const currentSelection = current.selection; const nextSelection = next.selection; - if (!this.context.config.multiCursorMergeOverlapping) { + if (!this.context.cursorConfig.multiCursorMergeOverlapping) { continue; } diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index ac269997918..92d36e239ad 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -7,18 +7,16 @@ import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; +import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; import { Constants } from 'vs/base/common/uint'; export interface IColumnSelectData { @@ -46,25 +44,6 @@ export const enum EditOperationType { DeletingRight = 3 } -export interface ICursors { - readonly context: CursorContext; - getPrimaryCursor(): CursorState; - getLastAddedCursorIndex(): number; - getAll(): CursorState[]; - - getColumnSelectData(): IColumnSelectData; - setColumnSelectData(columnSelectData: IColumnSelectData): void; - - setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): void; - reveal(source: string, horizontal: boolean, target: RevealTarget, scrollType: ScrollType): void; - revealRange(source: string, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void; - - scrollTo(desiredScrollTop: number): void; - - getPrevEditOperationType(): EditOperationType; - setPrevEditOperationType(type: EditOperationType): void; -} - export interface CharacterMap { [char: string]: string; } @@ -76,14 +55,6 @@ const autoCloseAlways = () => true; const autoCloseNever = () => false; const autoCloseBeforeWhitespace = (chr: string) => (chr === ' ' || chr === '\t'); -function appendEntry(target: Map, key: K, value: V): void { - if (target.has(key)) { - target.get(key)!.push(value); - } else { - target.set(key, [value]); - } -} - export class CursorConfiguration { _cursorMoveConfigurationBrand: void; @@ -157,8 +128,6 @@ export class CursorConfiguration { this.autoSurround = options.get(EditorOption.autoSurround); this.autoIndent = options.get(EditorOption.autoIndent); - this.autoClosingPairsOpen2 = new Map(); - this.autoClosingPairsClose2 = new Map(); this.surroundingPairs = {}; this._electricChars = null; @@ -167,15 +136,9 @@ export class CursorConfiguration { bracket: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingBrackets) }; - let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier); - if (autoClosingPairs) { - for (const pair of autoClosingPairs) { - appendEntry(this.autoClosingPairsOpen2, pair.open.charAt(pair.open.length - 1), pair); - if (pair.close.length === 1) { - appendEntry(this.autoClosingPairsClose2, pair.close, pair); - } - } - } + const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); + this.autoClosingPairsOpen2 = autoClosingPairs.autoClosingPairsOpen; + this.autoClosingPairsClose2 = autoClosingPairs.autoClosingPairsClose; let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier); if (surroundingPairs) { @@ -211,15 +174,6 @@ export class CursorConfiguration { } } - private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): StandardAutoClosingPairConditional[] | null { - try { - return LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); - } catch (e) { - onUnexpectedError(e); - return null; - } - } - private static _getShouldAutoClose(languageIdentifier: LanguageIdentifier, autoCloseConfig: EditorAutoClosingStrategy): (ch: string) => boolean { switch (autoCloseConfig) { case 'beforeWhitespace': @@ -357,62 +311,13 @@ export class CursorContext { _cursorContextBrand: void; public readonly model: ITextModel; - public readonly viewModel: IViewModel; - public readonly config: CursorConfiguration; + public readonly coordinatesConverter: ICoordinatesConverter; + public readonly cursorConfig: CursorConfiguration; - constructor(configuration: IConfiguration, model: ITextModel, viewModel: IViewModel) { + constructor(model: ITextModel, coordinatesConverter: ICoordinatesConverter, cursorConfig: CursorConfiguration) { this.model = model; - this.viewModel = viewModel; - this.config = new CursorConfiguration( - this.model.getLanguageIdentifier(), - this.model.getOptions(), - configuration - ); - } - - public validateViewPosition(viewPosition: Position, modelPosition: Position): Position { - return this.viewModel.coordinatesConverter.validateViewPosition(viewPosition, modelPosition); - } - - public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { - return this.viewModel.coordinatesConverter.validateViewRange(viewRange, expectedModelRange); - } - - public convertViewRangeToModelRange(viewRange: Range): Range { - return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); - } - - public convertViewPositionToModelPosition(lineNumber: number, column: number): Position { - return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, column)); - } - - public convertModelPositionToViewPosition(modelPosition: Position): Position { - return this.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); - } - - public convertModelRangeToViewRange(modelRange: Range): Range { - return this.viewModel.coordinatesConverter.convertModelRangeToViewRange(modelRange); - } - - public getCurrentScrollTop(): number { - return this.viewModel.viewLayout.getCurrentScrollTop(); - } - - public getCompletelyVisibleViewRange(): Range { - return this.viewModel.getCompletelyVisibleViewRange(); - } - - public getCompletelyVisibleModelRange(): Range { - const viewRange = this.viewModel.getCompletelyVisibleViewRange(); - return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); - } - - public getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range { - return this.viewModel.getCompletelyVisibleViewRangeAtScrollTop(scrollTop); - } - - public getVerticalOffsetForViewLine(viewLineNumber: number): number { - return this.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber); + this.coordinatesConverter = coordinatesConverter; + this.cursorConfig = cursorConfig; } } diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 3f5e80a3ee9..1147e78ae1c 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -5,11 +5,13 @@ import * as strings from 'vs/base/common/strings'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; +import { EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions'; import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from 'vs/editor/common/controller/cursorCommon'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand } from 'vs/editor/common/editorCommon'; +import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; export class DeleteOperations { @@ -47,8 +49,14 @@ export class DeleteOperations { return [shouldPushStackElementBefore, commands]; } - private static _isAutoClosingPairDelete(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): boolean { - if (config.autoClosingBrackets === 'never' && config.autoClosingQuotes === 'never') { + public static isAutoClosingPairDelete( + autoClosingBrackets: EditorAutoClosingStrategy, + autoClosingQuotes: EditorAutoClosingStrategy, + autoClosingPairsOpen: Map, + model: ICursorSimpleModel, + selections: Selection[] + ): boolean { + if (autoClosingBrackets === 'never' && autoClosingQuotes === 'never') { return false; } @@ -61,24 +69,27 @@ export class DeleteOperations { } const lineText = model.getLineContent(position.lineNumber); - const character = lineText[position.column - 2]; + if (position.column < 2 || position.column >= lineText.length + 1) { + return false; + } + const character = lineText.charAt(position.column - 2); - const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(character); + const autoClosingPairCandidates = autoClosingPairsOpen.get(character); if (!autoClosingPairCandidates) { return false; } if (isQuote(character)) { - if (config.autoClosingQuotes === 'never') { + if (autoClosingQuotes === 'never') { return false; } } else { - if (config.autoClosingBrackets === 'never') { + if (autoClosingBrackets === 'never') { return false; } } - const afterCharacter = lineText[position.column - 1]; + const afterCharacter = lineText.charAt(position.column - 1); let foundAutoClosingPair = false; for (const autoClosingPairCandidate of autoClosingPairCandidates) { @@ -111,7 +122,7 @@ export class DeleteOperations { public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array] { - if (this._isAutoClosingPairDelete(config, model, selections)) { + if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairsOpen2, model, selections)) { return this._runAutoClosingPairDelete(config, model, selections); } diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index 759ad0e19fd..bc9bbb1c390 100644 --- a/src/vs/editor/common/controller/cursorMoveCommands.ts +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -4,131 +4,132 @@ *--------------------------------------------------------------------------------------------*/ import * as types from 'vs/base/common/types'; -import { CursorContext, CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; +import { CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; export class CursorMoveCommands { - public static addCursorDown(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] { + public static addCursorDown(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] { let result: PartialCursorState[] = [], resultLen = 0; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState); if (useLogicalLine) { - result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(context.config, context.model, cursor.modelState)); + result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel.model, cursor.modelState)); } else { - result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(context.config, context.viewModel, cursor.viewState)); + result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel, cursor.viewState)); } } return result; } - public static addCursorUp(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] { + public static addCursorUp(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] { let result: PartialCursorState[] = [], resultLen = 0; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState); if (useLogicalLine) { - result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(context.config, context.model, cursor.modelState)); + result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel.model, cursor.modelState)); } else { - result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(context.config, context.viewModel, cursor.viewState)); + result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel, cursor.viewState)); } } return result; } - public static moveToBeginningOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + public static moveToBeginningOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = this._moveToLineStart(context, cursor, inSelectionMode); + result[i] = this._moveToLineStart(viewModel, cursor, inSelectionMode); } return result; } - private static _moveToLineStart(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { + private static _moveToLineStart(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { const currentViewStateColumn = cursor.viewState.position.column; const currentModelStateColumn = cursor.modelState.position.column; const isFirstLineOfWrappedLine = currentViewStateColumn === currentModelStateColumn; const currentViewStatelineNumber = cursor.viewState.position.lineNumber; - const firstNonBlankColumn = context.viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber); + const firstNonBlankColumn = viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber); const isBeginningOfViewLine = currentViewStateColumn === firstNonBlankColumn; if (!isFirstLineOfWrappedLine && !isBeginningOfViewLine) { - return this._moveToLineStartByView(context, cursor, inSelectionMode); + return this._moveToLineStartByView(viewModel, cursor, inSelectionMode); } else { - return this._moveToLineStartByModel(context, cursor, inSelectionMode); + return this._moveToLineStartByModel(viewModel, cursor, inSelectionMode); } } - private static _moveToLineStartByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { + private static _moveToLineStartByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { return CursorState.fromViewState( - MoveOperations.moveToBeginningOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode) + MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode) ); } - private static _moveToLineStartByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { + private static _moveToLineStartByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { return CursorState.fromModelState( - MoveOperations.moveToBeginningOfLine(context.config, context.model, cursor.modelState, inSelectionMode) + MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode) ); } - public static moveToEndOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + public static moveToEndOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, sticky: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = this._moveToLineEnd(context, cursor, inSelectionMode); + result[i] = this._moveToLineEnd(viewModel, cursor, inSelectionMode, sticky); } return result; } - private static _moveToLineEnd(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { + private static _moveToLineEnd(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState { const viewStatePosition = cursor.viewState.position; - const viewModelMaxColumn = context.viewModel.getLineMaxColumn(viewStatePosition.lineNumber); + const viewModelMaxColumn = viewModel.getLineMaxColumn(viewStatePosition.lineNumber); const isEndOfViewLine = viewStatePosition.column === viewModelMaxColumn; const modelStatePosition = cursor.modelState.position; - const modelMaxColumn = context.model.getLineMaxColumn(modelStatePosition.lineNumber); + const modelMaxColumn = viewModel.model.getLineMaxColumn(modelStatePosition.lineNumber); const isEndLineOfWrappedLine = viewModelMaxColumn - viewStatePosition.column === modelMaxColumn - modelStatePosition.column; if (isEndOfViewLine || isEndLineOfWrappedLine) { - return this._moveToLineEndByModel(context, cursor, inSelectionMode); + return this._moveToLineEndByModel(viewModel, cursor, inSelectionMode, sticky); } else { - return this._moveToLineEndByView(context, cursor, inSelectionMode); + return this._moveToLineEndByView(viewModel, cursor, inSelectionMode, sticky); } } - private static _moveToLineEndByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { + private static _moveToLineEndByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState { return CursorState.fromViewState( - MoveOperations.moveToEndOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode) + MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, sticky) ); } - private static _moveToLineEndByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState { + private static _moveToLineEndByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState { return CursorState.fromModelState( - MoveOperations.moveToEndOfLine(context.config, context.model, cursor.modelState, inSelectionMode) + MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, sticky) ); } - public static expandLineSelection(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { + public static expandLineSelection(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const startLineNumber = cursor.modelState.selection.startLineNumber; - const lineCount = context.model.getLineCount(); + const lineCount = viewModel.model.getLineCount(); let endLineNumber = cursor.modelState.selection.endLineNumber; let endColumn: number; if (endLineNumber === lineCount) { - endColumn = context.model.getLineMaxColumn(lineCount); + endColumn = viewModel.model.getLineMaxColumn(lineCount); } else { endLineNumber++; endColumn = 1; @@ -142,27 +143,27 @@ export class CursorMoveCommands { return result; } - public static moveToBeginningOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + public static moveToBeginningOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode)); + result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)); } return result; } - public static moveToEndOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + public static moveToEndOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode)); + result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)); } return result; } - public static selectAll(context: CursorContext, cursor: CursorState): PartialCursorState { - const lineCount = context.model.getLineCount(); - const maxColumn = context.model.getLineMaxColumn(lineCount); + public static selectAll(viewModel: IViewModel, cursor: CursorState): PartialCursorState { + const lineCount = viewModel.model.getLineCount(); + const maxColumn = viewModel.model.getLineMaxColumn(lineCount); return CursorState.fromModelState(new SingleCursorState( new Range(1, 1, 1, 1), 0, @@ -170,23 +171,23 @@ export class CursorMoveCommands { )); } - public static line(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState { - const position = context.model.validatePosition(_position); + public static line(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState { + const position = viewModel.model.validatePosition(_position); const viewPosition = ( _viewPosition - ? context.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position) - : context.convertModelPositionToViewPosition(position) + ? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position) + : viewModel.coordinatesConverter.convertModelPositionToViewPosition(position) ); if (!inSelectionMode || !cursor.modelState.hasSelection()) { // Entering line selection for the first time - const lineCount = context.model.getLineCount(); + const lineCount = viewModel.model.getLineCount(); let selectToLineNumber = position.lineNumber + 1; let selectToColumn = 1; if (selectToLineNumber > lineCount) { selectToLineNumber = lineCount; - selectToColumn = context.model.getLineMaxColumn(selectToLineNumber); + selectToColumn = viewModel.model.getLineMaxColumn(selectToLineNumber); } return CursorState.fromModelState(new SingleCursorState( @@ -206,13 +207,13 @@ export class CursorMoveCommands { } else if (position.lineNumber > enteringLineNumber) { - const lineCount = context.viewModel.getLineCount(); + const lineCount = viewModel.getLineCount(); let selectToViewLineNumber = viewPosition.lineNumber + 1; let selectToViewColumn = 1; if (selectToViewLineNumber > lineCount) { selectToViewLineNumber = lineCount; - selectToViewColumn = context.viewModel.getLineMaxColumn(selectToViewLineNumber); + selectToViewColumn = viewModel.getLineMaxColumn(selectToViewLineNumber); } return CursorState.fromViewState(cursor.viewState.move( @@ -229,12 +230,12 @@ export class CursorMoveCommands { } } - public static word(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState { - const position = context.model.validatePosition(_position); - return CursorState.fromModelState(WordOperations.word(context.config, context.model, cursor.modelState, inSelectionMode, position)); + public static word(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState { + const position = viewModel.model.validatePosition(_position); + return CursorState.fromModelState(WordOperations.word(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, position)); } - public static cancelSelection(context: CursorContext, cursor: CursorState): PartialCursorState { + public static cancelSelection(viewModel: IViewModel, cursor: CursorState): PartialCursorState { if (!cursor.modelState.hasSelection()) { return new CursorState(cursor.modelState, cursor.viewState); } @@ -248,118 +249,117 @@ export class CursorMoveCommands { )); } - public static moveTo(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState { - const position = context.model.validatePosition(_position); + public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState { + const position = viewModel.model.validatePosition(_position); const viewPosition = ( _viewPosition - ? context.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position) - : context.convertModelPositionToViewPosition(position) + ? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position) + : viewModel.coordinatesConverter.convertModelPositionToViewPosition(position) ); return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0)); } - public static move(context: CursorContext, cursors: CursorState[], args: CursorMove.ParsedArguments): PartialCursorState[] | null { - const inSelectionMode = args.select; - const value = args.value; - - switch (args.direction) { + public static simpleMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null { + switch (direction) { case CursorMove.Direction.Left: { - if (args.unit === CursorMove.Unit.HalfLine) { + if (unit === CursorMove.Unit.HalfLine) { // Move left by half the current line length - return this._moveHalfLineLeft(context, cursors, inSelectionMode); + return this._moveHalfLineLeft(viewModel, cursors, inSelectionMode); } else { // Move left by `moveParams.value` columns - return this._moveLeft(context, cursors, inSelectionMode, value); + return this._moveLeft(viewModel, cursors, inSelectionMode, value); } } case CursorMove.Direction.Right: { - if (args.unit === CursorMove.Unit.HalfLine) { + if (unit === CursorMove.Unit.HalfLine) { // Move right by half the current line length - return this._moveHalfLineRight(context, cursors, inSelectionMode); + return this._moveHalfLineRight(viewModel, cursors, inSelectionMode); } else { // Move right by `moveParams.value` columns - return this._moveRight(context, cursors, inSelectionMode, value); + return this._moveRight(viewModel, cursors, inSelectionMode, value); } } case CursorMove.Direction.Up: { - if (args.unit === CursorMove.Unit.WrappedLine) { + if (unit === CursorMove.Unit.WrappedLine) { // Move up by view lines - return this._moveUpByViewLines(context, cursors, inSelectionMode, value); + return this._moveUpByViewLines(viewModel, cursors, inSelectionMode, value); } else { // Move up by model lines - return this._moveUpByModelLines(context, cursors, inSelectionMode, value); + return this._moveUpByModelLines(viewModel, cursors, inSelectionMode, value); } } case CursorMove.Direction.Down: { - if (args.unit === CursorMove.Unit.WrappedLine) { + if (unit === CursorMove.Unit.WrappedLine) { // Move down by view lines - return this._moveDownByViewLines(context, cursors, inSelectionMode, value); + return this._moveDownByViewLines(viewModel, cursors, inSelectionMode, value); } else { // Move down by model lines - return this._moveDownByModelLines(context, cursors, inSelectionMode, value); + return this._moveDownByModelLines(viewModel, cursors, inSelectionMode, value); } } case CursorMove.Direction.WrappedLineStart: { // Move to the beginning of the current view line - return this._moveToViewMinColumn(context, cursors, inSelectionMode); + return this._moveToViewMinColumn(viewModel, cursors, inSelectionMode); } case CursorMove.Direction.WrappedLineFirstNonWhitespaceCharacter: { // Move to the first non-whitespace column of the current view line - return this._moveToViewFirstNonWhitespaceColumn(context, cursors, inSelectionMode); + return this._moveToViewFirstNonWhitespaceColumn(viewModel, cursors, inSelectionMode); } case CursorMove.Direction.WrappedLineColumnCenter: { // Move to the "center" of the current view line - return this._moveToViewCenterColumn(context, cursors, inSelectionMode); + return this._moveToViewCenterColumn(viewModel, cursors, inSelectionMode); } case CursorMove.Direction.WrappedLineEnd: { // Move to the end of the current view line - return this._moveToViewMaxColumn(context, cursors, inSelectionMode); + return this._moveToViewMaxColumn(viewModel, cursors, inSelectionMode); } case CursorMove.Direction.WrappedLineLastNonWhitespaceCharacter: { // Move to the last non-whitespace column of the current view line - return this._moveToViewLastNonWhitespaceColumn(context, cursors, inSelectionMode); + return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode); } + default: + return null; + } + + } + + public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null { + const visibleViewRange = viewModel.getCompletelyVisibleViewRange(); + const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange); + switch (direction) { case CursorMove.Direction.ViewPortTop: { // Move to the nth line start in the viewport (from the top) - const cursor = cursors[0]; - const visibleModelRange = context.getCompletelyVisibleModelRange(); - const modelLineNumber = this._firstLineNumberInRange(context.model, visibleModelRange, value); - const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber); - return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)]; + const modelLineNumber = this._firstLineNumberInRange(viewModel.model, visibleModelRange, value); + const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber); + return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)]; } case CursorMove.Direction.ViewPortBottom: { // Move to the nth line start in the viewport (from the bottom) - const cursor = cursors[0]; - const visibleModelRange = context.getCompletelyVisibleModelRange(); - const modelLineNumber = this._lastLineNumberInRange(context.model, visibleModelRange, value); - const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber); - return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)]; + const modelLineNumber = this._lastLineNumberInRange(viewModel.model, visibleModelRange, value); + const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber); + return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)]; } case CursorMove.Direction.ViewPortCenter: { // Move to the line start in the viewport center - const cursor = cursors[0]; - const visibleModelRange = context.getCompletelyVisibleModelRange(); const modelLineNumber = Math.round((visibleModelRange.startLineNumber + visibleModelRange.endLineNumber) / 2); - const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber); - return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)]; + const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber); + return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)]; } case CursorMove.Direction.ViewPortIfOutside: { // Move to a position inside the viewport - const visibleViewRange = context.getCompletelyVisibleViewRange(); let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = this.findPositionInViewportIfOutside(context, cursor, visibleViewRange, inSelectionMode); + result[i] = this.findPositionInViewportIfOutside(viewModel, cursor, visibleViewRange, inSelectionMode); } return result; } + default: + return null; } - - return null; } - - public static findPositionInViewportIfOutside(context: CursorContext, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState { + public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState { let viewLineNumber = cursor.viewState.position.lineNumber; if (visibleViewRange.startLineNumber <= viewLineNumber && viewLineNumber <= visibleViewRange.endLineNumber - 1) { @@ -373,8 +373,8 @@ export class CursorMoveCommands { if (viewLineNumber < visibleViewRange.startLineNumber) { viewLineNumber = visibleViewRange.startLineNumber; } - const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber); - return this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber); + return this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn); } } @@ -404,19 +404,20 @@ export class CursorMoveCommands { return Math.max(startLineNumber, range.endLineNumber - count + 1); } - private static _moveLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] { + private static _moveLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] { + const hasMultipleCursors = (cursors.length > 1); let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; + const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection(); + let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns); - let newViewState = MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns); - - if (noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { + if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { // moved over to the previous view line - const newViewModelPosition = context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); + const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) { // stayed on the same model line => pass wrapping point where 2 view positions map to a single model position - newViewState = MoveOperations.moveLeft(context.config, context.viewModel, newViewState, inSelectionMode, 1); + newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1); } } @@ -425,29 +426,31 @@ export class CursorMoveCommands { return result; } - private static _moveHalfLineLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveHalfLineLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2); - result[i] = CursorState.fromViewState(MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine)); + const halfLine = Math.round(viewModel.getLineContent(viewLineNumber).length / 2); + result[i] = CursorState.fromViewState(MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine)); } return result; } - private static _moveRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] { + private static _moveRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] { + const hasMultipleCursors = (cursors.length > 1); let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - let newViewState = MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns); + const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection(); + let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns); - if (noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { + if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { // moved over to the next view line - const newViewModelPosition = context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); + const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) { // stayed on the same model line => pass wrapping point where 2 view positions map to a single model position - newViewState = MoveOperations.moveRight(context.config, context.viewModel, newViewState, inSelectionMode, 1); + newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1); } } @@ -456,112 +459,112 @@ export class CursorMoveCommands { return result; } - private static _moveHalfLineRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveHalfLineRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2); - result[i] = CursorState.fromViewState(MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine)); + const halfLine = Math.round(viewModel.getLineContent(viewLineNumber).length / 2); + result[i] = CursorState.fromViewState(MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine)); } return result; } - private static _moveDownByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { + private static _moveDownByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = CursorState.fromViewState(MoveOperations.moveDown(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount)); + result[i] = CursorState.fromViewState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount)); } return result; } - private static _moveDownByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { + private static _moveDownByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = CursorState.fromModelState(MoveOperations.moveDown(context.config, context.model, cursor.modelState, inSelectionMode, linesCount)); + result[i] = CursorState.fromModelState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount)); } return result; } - private static _moveUpByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { + private static _moveUpByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = CursorState.fromViewState(MoveOperations.moveUp(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount)); + result[i] = CursorState.fromViewState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount)); } return result; } - private static _moveUpByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { + private static _moveUpByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; - result[i] = CursorState.fromModelState(MoveOperations.moveUp(context.config, context.model, cursor.modelState, inSelectionMode, linesCount)); + result[i] = CursorState.fromModelState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount)); } return result; } - private static _moveToViewPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState { + private static _moveToViewPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState { return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, toViewLineNumber, toViewColumn, 0)); } - private static _moveToModelPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState { + private static _moveToModelPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState { return CursorState.fromModelState(cursor.modelState.move(inSelectionMode, toModelLineNumber, toModelColumn, 0)); } - private static _moveToViewMinColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveToViewMinColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const viewColumn = context.viewModel.getLineMinColumn(viewLineNumber); - result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + const viewColumn = viewModel.getLineMinColumn(viewLineNumber); + result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn); } return result; } - private static _moveToViewFirstNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveToViewFirstNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber); - result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber); + result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn); } return result; } - private static _moveToViewCenterColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveToViewCenterColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const viewColumn = Math.round((context.viewModel.getLineMaxColumn(viewLineNumber) + context.viewModel.getLineMinColumn(viewLineNumber)) / 2); - result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + const viewColumn = Math.round((viewModel.getLineMaxColumn(viewLineNumber) + viewModel.getLineMinColumn(viewLineNumber)) / 2); + result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn); } return result; } - private static _moveToViewMaxColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveToViewMaxColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const viewColumn = context.viewModel.getLineMaxColumn(viewLineNumber); - result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + const viewColumn = viewModel.getLineMaxColumn(viewLineNumber); + result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn); } return result; } - private static _moveToViewLastNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { + private static _moveToViewLastNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] { let result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const viewLineNumber = cursor.viewState.position.lineNumber; - const viewColumn = context.viewModel.getLineLastNonWhitespaceColumn(viewLineNumber); - result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn); + const viewColumn = viewModel.getLineLastNonWhitespaceColumn(viewLineNumber); + result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn); } return result; } @@ -767,6 +770,13 @@ export namespace CursorMove { value: number; } + export interface SimpleMoveArguments { + direction: SimpleMoveDirection; + unit: Unit; + select: boolean; + value: number; + } + export const enum Direction { Left, Right, @@ -786,6 +796,25 @@ export namespace CursorMove { ViewPortIfOutside, } + export type SimpleMoveDirection = ( + Direction.Left + | Direction.Right + | Direction.Up + | Direction.Down + | Direction.WrappedLineStart + | Direction.WrappedLineFirstNonWhitespaceCharacter + | Direction.WrappedLineColumnCenter + | Direction.WrappedLineEnd + | Direction.WrappedLineLastNonWhitespaceCharacter + ); + + export type ViewportDirection = ( + Direction.ViewPortTop + | Direction.ViewPortCenter + | Direction.ViewPortBottom + | Direction.ViewPortIfOutside + ); + export const enum Unit { None, Line, diff --git a/src/vs/editor/common/controller/cursorMoveOperations.ts b/src/vs/editor/common/controller/cursorMoveOperations.ts index 1d414dd1f32..20ca28947b2 100644 --- a/src/vs/editor/common/controller/cursorMoveOperations.ts +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -7,6 +7,7 @@ import { CursorColumns, CursorConfiguration, ICursorSimpleModel, SingleCursorSta import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import * as strings from 'vs/base/common/strings'; +import { Constants } from 'vs/base/common/uint'; export class CursorPosition { _cursorPositionBrand: void; @@ -90,9 +91,10 @@ export class MoveOperations { public static down(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): CursorPosition { const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + const lineCount = model.getLineCount(); + const wasOnLastPosition = (lineNumber === lineCount && column === model.getLineMaxColumn(lineNumber)); lineNumber = lineNumber + count; - let lineCount = model.getLineCount(); if (lineNumber > lineCount) { lineNumber = lineCount; if (allowMoveOnLastLine) { @@ -104,7 +106,11 @@ export class MoveOperations { column = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, currentVisibleColumn); } - leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + if (wasOnLastPosition) { + leftoverVisibleColumns = 0; + } else { + leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + } return new CursorPosition(lineNumber, column, leftoverVisibleColumns); } @@ -143,6 +149,7 @@ export class MoveOperations { public static up(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): CursorPosition { const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + const wasOnFirstPosition = (lineNumber === 1 && column === 1); lineNumber = lineNumber - count; if (lineNumber < 1) { @@ -156,7 +163,11 @@ export class MoveOperations { column = CursorColumns.columnFromVisibleColumn2(config, model, lineNumber, currentVisibleColumn); } - leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + if (wasOnFirstPosition) { + leftoverVisibleColumns = 0; + } else { + leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + } return new CursorPosition(lineNumber, column, leftoverVisibleColumns); } @@ -211,10 +222,10 @@ export class MoveOperations { return cursor.move(inSelectionMode, lineNumber, column, 0); } - public static moveToEndOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState { + public static moveToEndOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, sticky: boolean): SingleCursorState { let lineNumber = cursor.position.lineNumber; let maxColumn = model.getLineMaxColumn(lineNumber); - return cursor.move(inSelectionMode, lineNumber, maxColumn, 0); + return cursor.move(inSelectionMode, lineNumber, maxColumn, sticky ? Constants.MAX_SAFE_SMALL_INTEGER - maxColumn : 0); } public static moveToBeginningOfBuffer(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState { diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index aab378176b7..d383ffc63cd 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -228,7 +228,7 @@ export class TypeOperations { let goodIndent = this._goodIndentForLine(config, model, selection.startLineNumber); goodIndent = goodIndent || '\t'; let possibleTypeText = config.normalizeIndentation(goodIndent); - if (!strings.startsWith(lineText, possibleTypeText)) { + if (!lineText.startsWith(possibleTypeText)) { commands[i] = new ReplaceCommand(new Range(selection.startLineNumber, 1, selection.startLineNumber, lineText.length + 1), possibleTypeText, true); continue; } @@ -263,15 +263,21 @@ export class TypeOperations { for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; if (!selection.isEmpty()) { - // looks like https://github.com/Microsoft/vscode/issues/2773 + // looks like https://github.com/microsoft/vscode/issues/2773 // where a cursor operation occurred before a canceled composition // => ignore composition commands[i] = null; continue; } - let pos = selection.getPosition(); - let startColumn = Math.max(1, pos.column - replaceCharCnt); - let range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column); + const pos = selection.getPosition(); + const startColumn = Math.max(1, pos.column - replaceCharCnt); + const range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column); + const oldText = model.getValueInRange(range); + if (oldText === txt) { + // => ignore composition that doesn't do anything + commands[i] = null; + continue; + } commands[i] = new ReplaceCommand(range, txt); } return new EditOperationResult(EditOperationType.Typing, commands, { @@ -411,13 +417,13 @@ export class TypeOperations { const firstNonWhitespace = model.getLineFirstNonWhitespaceColumn(range.startLineNumber); if (firstNonWhitespace === 0) { return TypeOperations._typeCommand( - new Range(range.startLineNumber, 0, range.endLineNumber, range.endColumn), + new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn), config.normalizeIndentation(actualIndentation) + ch, false ); } else { return TypeOperations._typeCommand( - new Range(range.startLineNumber, 0, range.endLineNumber, range.endColumn), + new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn), config.normalizeIndentation(actualIndentation) + model.getLineContent(range.startLineNumber).substring(firstNonWhitespace - 1, range.startColumn - 1) + ch, false @@ -492,15 +498,20 @@ export class TypeOperations { }); } + private static _autoClosingPairIsSymmetric(autoClosingPair: StandardAutoClosingPairConditional): boolean { + const { open, close } = autoClosingPair; + return (open.indexOf(close) >= 0 || close.indexOf(open) >= 0); + } + private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) { const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter); if (!otherAutoClosingPairs) { return false; } - const thisBraceIsSymmetric = (autoClosingPair.open === autoClosingPair.close); + const thisBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(autoClosingPair); for (const otherAutoClosingPair of otherAutoClosingPairs) { - const otherBraceIsSymmetric = (otherAutoClosingPair.open === otherAutoClosingPair.close); + const otherBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(otherAutoClosingPair); if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { continue; } @@ -791,9 +802,9 @@ export class TypeOperations { return null; } - public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult { + public static typeWithInterceptors(isDoingComposition: boolean, prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult { - if (ch === '\n') { + if (!isDoingComposition && ch === '\n') { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { commands[i] = TypeOperations._enter(config, model, false, selections[i]); @@ -804,7 +815,7 @@ export class TypeOperations { }); } - if (this._isAutoIndentType(config, model, selections)) { + if (!isDoingComposition && this._isAutoIndentType(config, model, selections)) { let commands: Array = []; let autoIndentFails = false; for (let i = 0, len = selections.length; i < len; i++) { @@ -822,13 +833,15 @@ export class TypeOperations { } } - if (this._isAutoClosingOvertype(config, model, selections, autoClosedCharacters, ch)) { + if (!isDoingComposition && this._isAutoClosingOvertype(config, model, selections, autoClosedCharacters, ch)) { return this._runAutoClosingOvertype(prevEditOperationType, config, model, selections, ch); } - const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true); - if (autoClosingPairOpenCharType) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType); + if (!isDoingComposition) { + const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true); + if (autoClosingPairOpenCharType) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType); + } } if (this._isSurroundSelectionType(config, model, selections, ch)) { @@ -837,7 +850,7 @@ export class TypeOperations { // Electric characters make sense only when dealing with a single cursor, // as multiple cursors typing brackets for example would interfer with bracket matching - if (this._isTypeInterceptorElectricChar(config, model, selections)) { + if (!isDoingComposition && this._isTypeInterceptorElectricChar(config, model, selections)) { const r = this._typeInterceptorElectricChar(prevEditOperationType, config, model, selections[0], ch); if (r) { return r; diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 84a9dcd96d2..c67d8f968e4 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -5,11 +5,15 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; +import { EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions'; import { CursorConfiguration, ICursorSimpleModel, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { ITextModel, IWordAtPosition } from 'vs/editor/common/model'; +import { AutoClosingPairs } from 'vs/editor/common/modes/languageConfiguration'; interface IFindWordResult { /** @@ -43,6 +47,16 @@ export const enum WordNavigationType { WordAccessibility = 3 // Respect chrome defintion of a word } +export interface DeleteWordContext { + wordSeparators: WordCharacterClassifier; + model: ITextModel; + selection: Selection; + whitespaceHeuristics: boolean; + autoClosingBrackets: EditorAutoClosingStrategy; + autoClosingQuotes: EditorAutoClosingStrategy; + autoClosingPairs: AutoClosingPairs; +} + export class WordOperations { private static _createWord(lineContent: string, wordType: WordType, nextCharClass: WordCharacterClass, start: number, end: number): IFindWordResult { @@ -162,11 +176,9 @@ export class WordOperations { public static moveWordLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { let lineNumber = position.lineNumber; let column = position.column; - let movedToPreviousLine = false; if (column === 1) { if (lineNumber > 1) { - movedToPreviousLine = true; lineNumber = lineNumber - 1; column = model.getLineMaxColumn(lineNumber); } @@ -175,17 +187,6 @@ export class WordOperations { let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, column)); if (wordNavigationType === WordNavigationType.WordStart) { - - if (prevWordOnLine && !movedToPreviousLine) { - // Special case for Visual Studio compatibility: - // when starting in the trim whitespace at the end of a line, - // go to the end of the last word - const lastWhitespaceColumn = model.getLineLastNonWhitespaceColumn(lineNumber); - if (lastWhitespaceColumn < column) { - return new Position(lineNumber, prevWordOnLine.end + 1); - } - } - return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1); } @@ -237,7 +238,7 @@ export class WordOperations { const left = lineContent.charCodeAt(column - 2); const right = lineContent.charCodeAt(column - 1); - if (left !== CharCode.Underline && right === CharCode.Underline) { + if (left === CharCode.Underline && right !== CharCode.Underline) { // snake_case_variables return new Position(lineNumber, column); } @@ -339,7 +340,7 @@ export class WordOperations { const left = lineContent.charCodeAt(column - 2); const right = lineContent.charCodeAt(column - 1); - if (left === CharCode.Underline && right !== CharCode.Underline) { + if (left !== CharCode.Underline && right === CharCode.Underline) { // snake_case_variables return new Position(lineNumber, column); } @@ -373,11 +374,21 @@ export class WordOperations { return null; } - public static deleteWordLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range | null { + public static deleteWordLeft(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range | null { + const wordSeparators = ctx.wordSeparators; + const model = ctx.model; + const selection = ctx.selection; + const whitespaceHeuristics = ctx.whitespaceHeuristics; + if (!selection.isEmpty()) { return selection; } + if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpen, ctx.model, [ctx.selection])) { + const position = ctx.selection.getPosition(); + return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1); + } + const position = new Position(selection.positionLineNumber, selection.positionColumn); let lineNumber = position.lineNumber; @@ -459,7 +470,12 @@ export class WordOperations { return null; } - public static deleteWordRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range | null { + public static deleteWordRight(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range | null { + const wordSeparators = ctx.wordSeparators; + const model = ctx.model; + const selection = ctx.selection; + const whitespaceHeuristics = ctx.whitespaceHeuristics; + if (!selection.isEmpty()) { return selection; } @@ -535,6 +551,28 @@ export class WordOperations { return new Range(pos.lineNumber, pos.column, toPosition.lineNumber, toPosition.column); } + private static _createWordAtPosition(model: ITextModel, lineNumber: number, word: IFindWordResult): IWordAtPosition { + const range = new Range(lineNumber, word.start + 1, lineNumber, word.end + 1); + return { + word: model.getValueInRange(range), + startColumn: range.startColumn, + endColumn: range.endColumn + }; + } + + public static getWordAtPosition(model: ITextModel, _wordSeparators: string, position: Position): IWordAtPosition | null { + const wordSeparators = getMapForWordSeparators(_wordSeparators); + const prevWord = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); + if (prevWord && prevWord.wordType === WordType.Regular && prevWord.start <= position.column - 1 && position.column - 1 <= prevWord.end) { + return WordOperations._createWordAtPosition(model, position.lineNumber, prevWord); + } + const nextWord = WordOperations._findNextWordOnLine(wordSeparators, model, position); + if (nextWord && nextWord.wordType === WordType.Regular && nextWord.start <= position.column - 1 && position.column - 1 <= nextWord.end) { + return WordOperations._createWordAtPosition(model, position.lineNumber, nextWord); + } + return null; + } + public static word(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean, position: Position): SingleCursorState { const wordSeparators = getMapForWordSeparators(config.wordSeparators); let prevWord = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); @@ -611,21 +649,21 @@ export class WordOperations { } export class WordPartOperations extends WordOperations { - public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean): Range { + public static deleteWordPartLeft(ctx: DeleteWordContext): Range { const candidates = enforceDefined([ - WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordStart), - WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordEnd), - WordOperations._deleteWordPartLeft(model, selection) + WordOperations.deleteWordLeft(ctx, WordNavigationType.WordStart), + WordOperations.deleteWordLeft(ctx, WordNavigationType.WordEnd), + WordOperations._deleteWordPartLeft(ctx.model, ctx.selection) ]); candidates.sort(Range.compareRangesUsingEnds); return candidates[2]; } - public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean): Range { + public static deleteWordPartRight(ctx: DeleteWordContext): Range { const candidates = enforceDefined([ - WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordStart), - WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordEnd), - WordOperations._deleteWordPartRight(model, selection) + WordOperations.deleteWordRight(ctx, WordNavigationType.WordStart), + WordOperations.deleteWordRight(ctx, WordNavigationType.WordEnd), + WordOperations._deleteWordPartRight(ctx.model, ctx.selection) ]); candidates.sort(Range.compareRangesUsingStarts); return candidates[0]; diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 2bb09583133..f79fafe21ae 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -81,11 +81,11 @@ export class OneCursor { } // We only have the view state => compute the model state const selectionStart = context.model.validateRange( - context.convertViewRangeToModelRange(viewState.selectionStart) + context.coordinatesConverter.convertViewRangeToModelRange(viewState.selectionStart) ); const position = context.model.validatePosition( - context.convertViewPositionToModelPosition(viewState.position.lineNumber, viewState.position.column) + context.coordinatesConverter.convertViewPositionToModelPosition(viewState.position) ); modelState = new SingleCursorState(selectionStart, viewState.selectionStartLeftoverVisibleColumns, position, viewState.leftoverVisibleColumns); @@ -104,15 +104,15 @@ export class OneCursor { if (!viewState) { // We only have the model state => compute the view state - const viewSelectionStart1 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn)); - const viewSelectionStart2 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn)); + const viewSelectionStart1 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn)); + const viewSelectionStart2 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn)); const viewSelectionStart = new Range(viewSelectionStart1.lineNumber, viewSelectionStart1.column, viewSelectionStart2.lineNumber, viewSelectionStart2.column); - const viewPosition = context.convertModelPositionToViewPosition(modelState.position); + const viewPosition = context.coordinatesConverter.convertModelPositionToViewPosition(modelState.position); viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns); } else { // Validate new view state - const viewSelectionStart = context.validateViewRange(viewState.selectionStart, modelState.selectionStart); - const viewPosition = context.validateViewPosition(viewState.position, modelState.position); + const viewSelectionStart = context.coordinatesConverter.validateViewRange(viewState.selectionStart, modelState.selectionStart); + const viewPosition = context.coordinatesConverter.validateViewPosition(viewState.position, modelState.position); viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns); } diff --git a/src/vs/editor/common/core/range.ts b/src/vs/editor/common/core/range.ts index e212e757dce..d684e3b9520 100644 --- a/src/vs/editor/common/core/range.ts +++ b/src/vs/editor/common/core/range.ts @@ -264,14 +264,28 @@ export class Range { * Return the end position (which will be after or equal to the start position) */ public getEndPosition(): Position { - return new Position(this.endLineNumber, this.endColumn); + return Range.getEndPosition(this); + } + + /** + * Return the end position (which will be after or equal to the start position) + */ + public static getEndPosition(range: IRange): Position { + return new Position(range.endLineNumber, range.endColumn); } /** * Return the start position (which will be before or equal to the end position) */ public getStartPosition(): Position { - return new Position(this.startLineNumber, this.startColumn); + return Range.getStartPosition(this); + } + + /** + * Return the start position (which will be before or equal to the end position) + */ + public static getStartPosition(range: IRange): Position { + return new Position(range.startLineNumber, range.startColumn); } /** diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/rgba.ts index 3a19425bc4d..3cfbf4e0488 100644 --- a/src/vs/editor/common/core/rgba.ts +++ b/src/vs/editor/common/core/rgba.ts @@ -36,6 +36,15 @@ export class RGBA8 { this.a = RGBA8._clamp(a); } + public equals(other: RGBA8): boolean { + return ( + this.r === other.r + && this.g === other.g + && this.b === other.b + && this.a === other.a + ); + } + private static _clamp(c: number): number { if (c < 0) { return 0; diff --git a/src/vs/editor/common/core/stringBuilder.ts b/src/vs/editor/common/core/stringBuilder.ts index 661fd6642e2..8a577ab8de2 100644 --- a/src/vs/editor/common/core/stringBuilder.ts +++ b/src/vs/editor/common/core/stringBuilder.ts @@ -4,8 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from 'vs/base/common/strings'; +import * as platform from 'vs/base/common/platform'; +import * as buffer from 'vs/base/common/buffer'; -declare const TextDecoder: any; // TODO@TypeScript +declare const TextDecoder: { + prototype: TextDecoder; + new(label?: string): TextDecoder; +}; interface TextDecoder { decode(view: Uint16Array): string; } @@ -18,17 +23,43 @@ export interface IStringBuilder { appendASCIIString(str: string): void; } -export let createStringBuilder: (capacity: number) => IStringBuilder; +let _platformTextDecoder: TextDecoder | null; +export function getPlatformTextDecoder(): TextDecoder { + if (!_platformTextDecoder) { + _platformTextDecoder = new TextDecoder(platform.isLittleEndian() ? 'UTF-16LE' : 'UTF-16BE'); + } + return _platformTextDecoder; +} -if (typeof TextDecoder !== 'undefined') { +export const hasTextDecoder = (typeof TextDecoder !== 'undefined'); +export let createStringBuilder: (capacity: number) => IStringBuilder; +export let decodeUTF16LE: (source: Uint8Array, offset: number, len: number) => string; + +if (hasTextDecoder) { createStringBuilder = (capacity) => new StringBuilder(capacity); + decodeUTF16LE = standardDecodeUTF16LE; } else { createStringBuilder = (capacity) => new CompatStringBuilder(); + decodeUTF16LE = compatDecodeUTF16LE; +} + +function standardDecodeUTF16LE(source: Uint8Array, offset: number, len: number): string { + const view = new Uint16Array(source.buffer, offset, len); + return getPlatformTextDecoder().decode(view); +} + +function compatDecodeUTF16LE(source: Uint8Array, offset: number, len: number): string { + let result: string[] = []; + let resultLen = 0; + for (let i = 0; i < len; i++) { + const charCode = buffer.readUInt16LE(source, offset); offset += 2; + result[resultLen++] = String.fromCharCode(charCode); + } + return result.join(''); } class StringBuilder implements IStringBuilder { - private readonly _decoder: TextDecoder; private readonly _capacity: number; private readonly _buffer: Uint16Array; @@ -36,7 +67,6 @@ class StringBuilder implements IStringBuilder { private _bufferLength: number; constructor(capacity: number) { - this._decoder = new TextDecoder('UTF-16LE'); this._capacity = capacity | 0; this._buffer = new Uint16Array(this._capacity); @@ -63,7 +93,7 @@ class StringBuilder implements IStringBuilder { } const view = new Uint16Array(this._buffer.buffer, 0, this._bufferLength); - return this._decoder.decode(view); + return getPlatformTextDecoder().decode(view); } private _flushBuffer(): void { diff --git a/src/vs/editor/common/editorAction.ts b/src/vs/editor/common/editorAction.ts index 7c4ee116356..055a67fbd15 100644 --- a/src/vs/editor/common/editorAction.ts +++ b/src/vs/editor/common/editorAction.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEditorAction } from 'vs/editor/common/editorCommon'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; export class InternalEditorAction implements IEditorAction { @@ -12,7 +12,7 @@ export class InternalEditorAction implements IEditorAction { public readonly label: string; public readonly alias: string; - private readonly _precondition: ContextKeyExpr | undefined; + private readonly _precondition: ContextKeyExpression | undefined; private readonly _run: () => Promise; private readonly _contextKeyService: IContextKeyService; @@ -20,7 +20,7 @@ export class InternalEditorAction implements IEditorAction { id: string, label: string, alias: string, - precondition: ContextKeyExpr | undefined, + precondition: ContextKeyExpression | undefined, run: () => Promise, contextKeyService: IContextKeyService ) { @@ -41,7 +41,6 @@ export class InternalEditorAction implements IEditorAction { return Promise.resolve(undefined); } - const r = this._run(); - return r ? r : Promise.resolve(undefined); + return this._run(); } } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index ee22f3ce88c..94c4a3c48df 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -5,12 +5,13 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ConfigurationChangedEvent, IComputedEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IIdentifiedSingleEditOperation, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IValidEditOperation } from 'vs/editor/common/model'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; /** @@ -22,7 +23,7 @@ export interface IEditOperationBuilder { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; + addEditOperation(range: IRange, text: string | null, forceMoveMarkers?: boolean): void; /** * Add a new edit operation (a replace operation). @@ -30,7 +31,7 @@ export interface IEditOperationBuilder { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addTrackedEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; + addTrackedEditOperation(range: IRange, text: string | null, forceMoveMarkers?: boolean): void; /** * Track `selection` when applying edit operations. @@ -51,7 +52,7 @@ export interface ICursorStateComputerData { /** * Get the inverse edit operations of the added edit operations. */ - getInverseEditOperations(): IIdentifiedSingleEditOperation[]; + getInverseEditOperations(): IValidEditOperation[]; /** * Get a previously tracked selection. * @param id The unique identifier returned by `trackSelection`. @@ -149,11 +150,13 @@ export interface ILineChange extends IChange { * @internal */ export interface IConfiguration extends IDisposable { + onDidChangeFast(listener: (e: ConfigurationChangedEvent) => void): IDisposable; onDidChange(listener: (e: ConfigurationChangedEvent) => void): IDisposable; readonly options: IComputedEditorOptions; setMaxLineNumber(maxLineNumber: number): void; + setViewLineCount(viewLineCount: number): void; updateOptions(newOptions: IEditorOptions): void; getRawOptions(): IEditorOptions; observeReferenceElement(dimension?: IDimension): void; @@ -355,6 +358,12 @@ export interface IEditor { */ revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType?: ScrollType): void; + /** + * Scroll vertically as necessary and reveal a line close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealLineNearTop(lineNumber: number, scrollType?: ScrollType): void; + /** * Scroll vertically or horizontally as necessary and reveal a position. */ @@ -370,6 +379,12 @@ export interface IEditor { */ revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType?: ScrollType): void; + /** + * Scroll vertically or horizontally as necessary and reveal a position close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealPositionNearTop(position: IPosition, scrollType?: ScrollType): void; + /** * Returns the primary selection of the editor. */ @@ -422,6 +437,12 @@ export interface IEditor { */ revealLinesInCenterIfOutsideViewport(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; + /** + * Scroll vertically as necessary and reveal lines close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealLinesNearTop(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; + /** * Scroll vertically or horizontally as necessary and reveal a range. */ @@ -442,13 +463,25 @@ export interface IEditor { */ revealRangeInCenterIfOutsideViewport(range: IRange, scrollType?: ScrollType): void; + /** + * Scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealRangeNearTop(range: IRange, scrollType?: ScrollType): void; + + /** + * Scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport, + * optimized for viewing a code definition. Only if it lies outside the viewport. + */ + revealRangeNearTopIfOutsideViewport(range: IRange, scrollType?: ScrollType): void; + /** * Directly trigger a handler or an editor action. * @param source The source of the call. * @param handlerId The id of the handler or the id of a contribution. * @param payload Extra data to be sent to the handler. */ - trigger(source: string, handlerId: string, payload: any): void; + trigger(source: string | null | undefined, handlerId: string, payload: any): void; /** * Gets the current model attached to this editor. @@ -498,6 +531,24 @@ export interface IDiffEditor extends IEditor { getModifiedEditor(): IEditor; } +/** + * @internal + */ +export interface ICompositeCodeEditor { + + /** + * An event that signals that the active editor has changed + */ + readonly onDidChangeActiveEditor: Event; + + /** + * The active code editor iff any + */ + readonly activeCodeEditor: IEditor | undefined; + // readonly editors: readonly ICodeEditor[] maybe supported with uris +} + + /** * An editor contribution that gets created every time a new editor gets created and gets disposed when the editor gets disposed. */ @@ -638,18 +689,36 @@ export const EditorType = { * Built-in commands. * @internal */ -export const Handler = { - ExecuteCommand: 'executeCommand', - ExecuteCommands: 'executeCommands', +export const enum Handler { + CompositionStart = 'compositionStart', + CompositionEnd = 'compositionEnd', + Type = 'type', + ReplacePreviousChar = 'replacePreviousChar', + Paste = 'paste', + Cut = 'cut', +} - Type: 'type', - ReplacePreviousChar: 'replacePreviousChar', - CompositionStart: 'compositionStart', - CompositionEnd: 'compositionEnd', - Paste: 'paste', +/** + * @internal + */ +export interface TypePayload { + text: string; +} - Cut: 'cut', +/** + * @internal + */ +export interface ReplacePreviousCharPayload { + text: string; + replaceCharCnt: number; +} - Undo: 'undo', - Redo: 'redo', -}; +/** + * @internal + */ +export interface PastePayload { + text: string; + pasteOnNewLine: boolean; + multicursorText: string[] | null; + mode: string | null; +} diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index 6d401cb5805..487e6d4f0fd 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -3,11 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export namespace EditorContextKeys { + + export const editorSimpleInput = new RawContextKey('editorSimpleInput', false); /** * A context key that is set when the editor's text has focus (cursor is blinking). + * Is false when focus is in simple editor widgets (repl input, scm commit input). */ export const editorTextFocus = new RawContextKey('editorTextFocus', false); /** @@ -21,17 +24,27 @@ export namespace EditorContextKeys { export const textInputFocus = new RawContextKey('textInputFocus', false); export const readOnly = new RawContextKey('editorReadonly', false); - export const writable: ContextKeyExpr = readOnly.toNegated(); + export const columnSelection = new RawContextKey('editorColumnSelection', false); + export const writable = readOnly.toNegated(); export const hasNonEmptySelection = new RawContextKey('editorHasSelection', false); - export const hasOnlyEmptySelection: ContextKeyExpr = hasNonEmptySelection.toNegated(); + export const hasOnlyEmptySelection = hasNonEmptySelection.toNegated(); export const hasMultipleSelections = new RawContextKey('editorHasMultipleSelections', false); - export const hasSingleSelection: ContextKeyExpr = hasMultipleSelections.toNegated(); + export const hasSingleSelection = hasMultipleSelections.toNegated(); export const tabMovesFocus = new RawContextKey('editorTabMovesFocus', false); - export const tabDoesNotMoveFocus: ContextKeyExpr = tabMovesFocus.toNegated(); - export const isInEmbeddedEditor = new RawContextKey('isInEmbeddedEditor', false); + export const tabDoesNotMoveFocus = tabMovesFocus.toNegated(); + export const isInWalkThroughSnippet = new RawContextKey('isInEmbeddedEditor', false); export const canUndo = new RawContextKey('canUndo', false); export const canRedo = new RawContextKey('canRedo', false); + export const hoverVisible = new RawContextKey('editorHoverVisible', false); + + /** + * A context key that is set when an editor is part of a larger editor, like notebooks or + * (future) a diff editor + */ + export const inCompositeEditor = new RawContextKey('inCompositeEditor', undefined); + export const notInCompositeEditor = inCompositeEditor.toNegated(); + // -- mode context keys export const languageId = new RawContextKey('editorLangId', ''); export const hasCompletionItemProvider = new RawContextKey('editorHasCompletionItemProvider', false); diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 8c7edd2e6f8..fd5c9ccbb1c 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -15,6 +16,7 @@ import { SearchData } from 'vs/editor/common/model/textModelSearch'; import { LanguageId, LanguageIdentifier, FormattingOptions } from 'vs/editor/common/modes'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { MultilineTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore'; +import { TextChange } from 'vs/editor/common/model/textChange'; /** * Vertical Lane in the overview ruler of the editor. @@ -123,6 +125,10 @@ export interface IModelDecorationOptions { * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ linesDecorationsClassName?: string | null; + /** + * If set, the decoration will be rendered in the lines decorations with this CSS class name, but only for the first line in case of line wrapping. + */ + firstLineDecorationClassName?: string | null; /** * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. */ @@ -290,6 +296,7 @@ export const enum EndOfLineSequence { /** * An identifier for a single edit operation. + * @internal */ export interface ISingleEditOperationIdentifier { /** @@ -334,7 +341,7 @@ export interface IIdentifiedSingleEditOperation { /** * The range to replace. This can be empty to emulate a simple insert. */ - range: Range; + range: IRange; /** * The text to replace with. This can be null to emulate a simple delete. */ @@ -357,6 +364,26 @@ export interface IIdentifiedSingleEditOperation { _isTracked?: boolean; } +export interface IValidEditOperation { + /** + * An identifier associated with this single edit operation. + * @internal + */ + identifier: ISingleEditOperationIdentifier | null; + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: Range; + /** + * The text to replace with. This can be empty to emulate a simple delete. + */ + text: string; + /** + * @internal + */ + textChange: TextChange; +} + /** * A callback that can compute the cursor state after applying a series of edit operations. */ @@ -364,7 +391,7 @@ export interface ICursorStateComputer { /** * A callback that can compute the resulting cursors state after some edit operations have been executed. */ - (inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null; + (inverseEditOperations: IValidEditOperation[]): Selection[] | null; } export class TextModelResolvedOptions { @@ -524,6 +551,18 @@ export interface ITextModel { */ mightContainRTL(): boolean; + /** + * If true, the text model might contain LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS). + * If false, the text model definitely does not contain these. + * @internal + */ + mightContainUnusualLineTerminators(): boolean; + + /** + * @internal + */ + removeUnusualLineTerminators(selections?: Selection[]): void; + /** * If true, the text model might contain non basic ASCII. * If false, the text model **contains only** basic ASCII. @@ -594,6 +633,12 @@ export interface ITextModel { */ equalsTextBuffer(other: ITextBuffer): boolean; + /** + * Get the underling text buffer. + * @internal + */ + getTextBuffer(): ITextBuffer; + /** * Get the text in a certain range. * @param range The range describing what text to get. @@ -755,7 +800,7 @@ export interface ITextModel { /** * Search the model. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchScope Limit the searching to only search inside this range. + * @param searchScope Limit the searching to only search inside these ranges. * @param isRegex Used to indicate that `searchString` is a regular expression. * @param matchCase Force the matching to match lower/upper case exactly. * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. @@ -763,7 +808,7 @@ export interface ITextModel { * @param limitResultCount Limit the number of results * @return The ranges where the matches are. It is empty if no matches have been found. */ - findMatches(searchString: string, searchScope: IRange, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + findMatches(searchString: string, searchScope: IRange | IRange[], isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; /** * Search the model for the next match. Loops to the beginning of the model if needed. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. @@ -795,7 +840,17 @@ export interface ITextModel { /** * @internal */ - setSemanticTokens(tokens: MultilineTokens2[] | null): void; + setSemanticTokens(tokens: MultilineTokens2[] | null, isComplete: boolean): void; + + /** + * @internal + */ + setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[] | null): void; + + /** + * @internal + */ + hasSemanticTokens(): boolean; /** * Flush all tokenization state. @@ -1048,7 +1103,7 @@ export interface ITextModel { * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. */ - pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; + pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; /** * Change the end of line sequence. This is the preferred way of @@ -1060,9 +1115,11 @@ export interface ITextModel { * Edit the model without adding the edits to the undo stack. * This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way. * @param operations The edit operations. - * @return The inverse edit operations, that, when applied, will bring the model back to the previous state. + * @return If desired, the inverse edit operations, that, when applied, will bring the model back to the previous state. */ - applyEdits(operations: IIdentifiedSingleEditOperation[]): IIdentifiedSingleEditOperation[]; + applyEdits(operations: IIdentifiedSingleEditOperation[]): void; + applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: false): void; + applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: true): IValidEditOperation[]; /** * Change the end of line sequence without recording in the undo stack. @@ -1070,12 +1127,22 @@ export interface ITextModel { */ setEOL(eol: EndOfLineSequence): void; + /** + * @internal + */ + _applyUndo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; + + /** + * @internal + */ + _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; + /** * Undo edit operations until the first previous stop point created by `pushStackElement`. * The inverse edit operations will be pushed on the redo stack. * @internal */ - undo(): Selection[] | null; + undo(): void | Promise; /** * Is there anything in the undo stack? @@ -1088,7 +1155,7 @@ export interface ITextModel { * The inverse edit operations will be pushed on the undo stack. * @internal */ - redo(): Selection[] | null; + redo(): void | Promise; /** * Is there anything in the redo stack? @@ -1208,9 +1275,26 @@ export const enum ModelConstants { /** * @internal */ -export interface ITextBuffer { +export class ValidAnnotatedEditOperation implements IIdentifiedSingleEditOperation { + constructor( + public readonly identifier: ISingleEditOperationIdentifier | null, + public readonly range: Range, + public readonly text: string | null, + public readonly forceMoveMarkers: boolean, + public readonly isAutoWhitespaceEdit: boolean, + public readonly _isTracked: boolean, + ) { } +} + +/** + * @internal + */ +export interface IReadonlyTextBuffer { + onDidChangeContent: Event; equals(other: ITextBuffer): boolean; mightContainRTL(): boolean; + mightContainUnusualLineTerminators(): boolean; + resetMightContainUnusualLineTerminators(): void; mightContainNonBasicASCII(): boolean; getBOM(): string; getEOL(): string; @@ -1228,22 +1312,28 @@ export interface ITextBuffer { getLinesContent(): string[]; getLineContent(lineNumber: number): string; getLineCharCode(lineNumber: number, index: number): number; + getCharCode(offset: number): number; getLineLength(lineNumber: number): number; getLineFirstNonWhitespaceColumn(lineNumber: number): number; getLineLastNonWhitespaceColumn(lineNumber: number): number; - - setEOL(newEOL: '\r\n' | '\n'): void; - applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult; findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; } +/** + * @internal + */ +export interface ITextBuffer extends IReadonlyTextBuffer { + setEOL(newEOL: '\r\n' | '\n'): void; + applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult; +} + /** * @internal */ export class ApplyEditsResult { constructor( - public readonly reverseEdits: IIdentifiedSingleEditOperation[], + public readonly reverseEdits: IValidEditOperation[] | null, public readonly changes: IInternalModelContentChange[], public readonly trimAutoWhitespaceLineNumbers: number[] | null ) { } diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index add6b310ea5..0928d7d0e9b 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -3,61 +3,357 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Selection } from 'vs/editor/common/core/selection'; -import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; +import { URI } from 'vs/base/common/uri'; +import { TextChange, compressConsecutiveTextChanges } from 'vs/editor/common/model/textChange'; +import * as buffer from 'vs/base/common/buffer'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { basename } from 'vs/base/common/resources'; -interface IEditOperation { - operations: IIdentifiedSingleEditOperation[]; +function uriGetComparisonKey(resource: URI): string { + return resource.toString(); } -interface IStackElement { - readonly beforeVersionId: number; - readonly beforeCursorState: Selection[] | null; - readonly afterCursorState: Selection[] | null; - readonly afterVersionId: number; +class SingleModelEditStackData { - undo(model: TextModel): void; - redo(model: TextModel): void; -} - -class EditStackElement implements IStackElement { - public readonly beforeVersionId: number; - public readonly beforeCursorState: Selection[]; - public afterCursorState: Selection[] | null; - public afterVersionId: number; - - public editOperations: IEditOperation[]; - - constructor(beforeVersionId: number, beforeCursorState: Selection[]) { - this.beforeVersionId = beforeVersionId; - this.beforeCursorState = beforeCursorState; - this.afterCursorState = null; - this.afterVersionId = -1; - this.editOperations = []; + public static create(model: ITextModel, beforeCursorState: Selection[] | null): SingleModelEditStackData { + const alternativeVersionId = model.getAlternativeVersionId(); + const eol = getModelEOL(model); + return new SingleModelEditStackData( + alternativeVersionId, + alternativeVersionId, + eol, + eol, + beforeCursorState, + beforeCursorState, + [] + ); } - public undo(model: TextModel): void { - // Apply all operations in reverse order - for (let i = this.editOperations.length - 1; i >= 0; i--) { - this.editOperations[i] = { - operations: model.applyEdits(this.editOperations[i].operations) - }; + constructor( + public readonly beforeVersionId: number, + public afterVersionId: number, + public readonly beforeEOL: EndOfLineSequence, + public afterEOL: EndOfLineSequence, + public readonly beforeCursorState: Selection[] | null, + public afterCursorState: Selection[] | null, + public changes: TextChange[] + ) { } + + public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void { + if (textChanges.length > 0) { + this.changes = compressConsecutiveTextChanges(this.changes, textChanges); + } + this.afterEOL = afterEOL; + this.afterVersionId = afterVersionId; + this.afterCursorState = afterCursorState; + } + + private static _writeSelectionsSize(selections: Selection[] | null): number { + return 4 + 4 * 4 * (selections ? selections.length : 0); + } + + private static _writeSelections(b: Uint8Array, selections: Selection[] | null, offset: number): number { + buffer.writeUInt32BE(b, (selections ? selections.length : 0), offset); offset += 4; + if (selections) { + for (const selection of selections) { + buffer.writeUInt32BE(b, selection.selectionStartLineNumber, offset); offset += 4; + buffer.writeUInt32BE(b, selection.selectionStartColumn, offset); offset += 4; + buffer.writeUInt32BE(b, selection.positionLineNumber, offset); offset += 4; + buffer.writeUInt32BE(b, selection.positionColumn, offset); offset += 4; + } + } + return offset; + } + + private static _readSelections(b: Uint8Array, offset: number, dest: Selection[]): number { + const count = buffer.readUInt32BE(b, offset); offset += 4; + for (let i = 0; i < count; i++) { + const selectionStartLineNumber = buffer.readUInt32BE(b, offset); offset += 4; + const selectionStartColumn = buffer.readUInt32BE(b, offset); offset += 4; + const positionLineNumber = buffer.readUInt32BE(b, offset); offset += 4; + const positionColumn = buffer.readUInt32BE(b, offset); offset += 4; + dest.push(new Selection(selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn)); + } + return offset; + } + + public serialize(): ArrayBuffer { + let necessarySize = ( + + 4 // beforeVersionId + + 4 // afterVersionId + + 1 // beforeEOL + + 1 // afterEOL + + SingleModelEditStackData._writeSelectionsSize(this.beforeCursorState) + + SingleModelEditStackData._writeSelectionsSize(this.afterCursorState) + + 4 // change count + ); + for (const change of this.changes) { + necessarySize += change.writeSize(); + } + + const b = new Uint8Array(necessarySize); + let offset = 0; + buffer.writeUInt32BE(b, this.beforeVersionId, offset); offset += 4; + buffer.writeUInt32BE(b, this.afterVersionId, offset); offset += 4; + buffer.writeUInt8(b, this.beforeEOL, offset); offset += 1; + buffer.writeUInt8(b, this.afterEOL, offset); offset += 1; + offset = SingleModelEditStackData._writeSelections(b, this.beforeCursorState, offset); + offset = SingleModelEditStackData._writeSelections(b, this.afterCursorState, offset); + buffer.writeUInt32BE(b, this.changes.length, offset); offset += 4; + for (const change of this.changes) { + offset = change.write(b, offset); + } + return b.buffer; + } + + public static deserialize(source: ArrayBuffer): SingleModelEditStackData { + const b = new Uint8Array(source); + let offset = 0; + const beforeVersionId = buffer.readUInt32BE(b, offset); offset += 4; + const afterVersionId = buffer.readUInt32BE(b, offset); offset += 4; + const beforeEOL = buffer.readUInt8(b, offset); offset += 1; + const afterEOL = buffer.readUInt8(b, offset); offset += 1; + const beforeCursorState: Selection[] = []; + offset = SingleModelEditStackData._readSelections(b, offset, beforeCursorState); + const afterCursorState: Selection[] = []; + offset = SingleModelEditStackData._readSelections(b, offset, afterCursorState); + const changeCount = buffer.readUInt32BE(b, offset); offset += 4; + const changes: TextChange[] = []; + for (let i = 0; i < changeCount; i++) { + offset = TextChange.read(b, offset, changes); + } + return new SingleModelEditStackData( + beforeVersionId, + afterVersionId, + beforeEOL, + afterEOL, + beforeCursorState, + afterCursorState, + changes + ); + } +} + +export interface IUndoRedoDelegate { + prepareUndoRedo(element: MultiModelEditStackElement): Promise | IDisposable | void; +} + +export class SingleModelEditStackElement implements IResourceUndoRedoElement { + + public model: ITextModel | URI; + private _data: SingleModelEditStackData | ArrayBuffer; + + public get type(): UndoRedoElementType.Resource { + return UndoRedoElementType.Resource; + } + + public get resource(): URI { + if (URI.isUri(this.model)) { + return this.model; + } + return this.model.uri; + } + + public get label(): string { + return nls.localize('edit', "Typing"); + } + + constructor(model: ITextModel, beforeCursorState: Selection[] | null) { + this.model = model; + this._data = SingleModelEditStackData.create(model, beforeCursorState); + } + + public toString(): string { + const data = (this._data instanceof SingleModelEditStackData ? this._data : SingleModelEditStackData.deserialize(this._data)); + return data.changes.map(change => change.toString()).join(', '); + } + + public matchesResource(resource: URI): boolean { + const uri = (URI.isUri(this.model) ? this.model : this.model.uri); + return (uri.toString() === resource.toString()); + } + + public setModel(model: ITextModel | URI): void { + this.model = model; + } + + public canAppend(model: ITextModel): boolean { + return (this.model === model && this._data instanceof SingleModelEditStackData); + } + + public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void { + if (this._data instanceof SingleModelEditStackData) { + this._data.append(model, textChanges, afterEOL, afterVersionId, afterCursorState); } } - public redo(model: TextModel): void { - // Apply all operations - for (let i = 0; i < this.editOperations.length; i++) { - this.editOperations[i] = { - operations: model.applyEdits(this.editOperations[i].operations) - }; + public close(): void { + if (this._data instanceof SingleModelEditStackData) { + this._data = this._data.serialize(); } } + + public undo(): void { + if (URI.isUri(this.model)) { + // don't have a model + throw new Error(`Invalid SingleModelEditStackElement`); + } + if (this._data instanceof SingleModelEditStackData) { + this._data = this._data.serialize(); + } + const data = SingleModelEditStackData.deserialize(this._data); + this.model._applyUndo(data.changes, data.beforeEOL, data.beforeVersionId, data.beforeCursorState); + } + + public redo(): void { + if (URI.isUri(this.model)) { + // don't have a model + throw new Error(`Invalid SingleModelEditStackElement`); + } + if (this._data instanceof SingleModelEditStackData) { + this._data = this._data.serialize(); + } + const data = SingleModelEditStackData.deserialize(this._data); + this.model._applyRedo(data.changes, data.afterEOL, data.afterVersionId, data.afterCursorState); + } + + public heapSize(): number { + if (this._data instanceof SingleModelEditStackData) { + this._data = this._data.serialize(); + } + return this._data.byteLength + 168/*heap overhead*/; + } } -function getModelEOL(model: TextModel): EndOfLineSequence { +export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { + + public readonly type = UndoRedoElementType.Workspace; + public readonly label: string; + private _isOpen: boolean; + + private readonly _editStackElementsArr: SingleModelEditStackElement[]; + private readonly _editStackElementsMap: Map; + + private _delegate: IUndoRedoDelegate | null; + + public get resources(): readonly URI[] { + return this._editStackElementsArr.map(editStackElement => editStackElement.resource); + } + + constructor( + label: string, + editStackElements: SingleModelEditStackElement[] + ) { + this.label = label; + this._isOpen = true; + this._editStackElementsArr = editStackElements.slice(0); + this._editStackElementsMap = new Map(); + for (const editStackElement of this._editStackElementsArr) { + const key = uriGetComparisonKey(editStackElement.resource); + this._editStackElementsMap.set(key, editStackElement); + } + this._delegate = null; + } + + public setDelegate(delegate: IUndoRedoDelegate): void { + this._delegate = delegate; + } + + public prepareUndoRedo(): Promise | IDisposable | void { + if (this._delegate) { + return this._delegate.prepareUndoRedo(this); + } + } + + public getMissingModels(): URI[] { + const result: URI[] = []; + for (const editStackElement of this._editStackElementsArr) { + if (URI.isUri(editStackElement.model)) { + result.push(editStackElement.model); + } + } + return result; + } + + public matchesResource(resource: URI): boolean { + const key = uriGetComparisonKey(resource); + return (this._editStackElementsMap.has(key)); + } + + public setModel(model: ITextModel | URI): void { + const key = uriGetComparisonKey(URI.isUri(model) ? model : model.uri); + if (this._editStackElementsMap.has(key)) { + this._editStackElementsMap.get(key)!.setModel(model); + } + } + + public canAppend(model: ITextModel): boolean { + if (!this._isOpen) { + return false; + } + const key = uriGetComparisonKey(model.uri); + if (this._editStackElementsMap.has(key)) { + const editStackElement = this._editStackElementsMap.get(key)!; + return editStackElement.canAppend(model); + } + return false; + } + + public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void { + const key = uriGetComparisonKey(model.uri); + const editStackElement = this._editStackElementsMap.get(key)!; + editStackElement.append(model, textChanges, afterEOL, afterVersionId, afterCursorState); + } + + public close(): void { + this._isOpen = false; + } + + public undo(): void { + this._isOpen = false; + + for (const editStackElement of this._editStackElementsArr) { + editStackElement.undo(); + } + } + + public redo(): void { + for (const editStackElement of this._editStackElementsArr) { + editStackElement.redo(); + } + } + + public heapSize(resource: URI): number { + const key = uriGetComparisonKey(resource); + if (this._editStackElementsMap.has(key)) { + const editStackElement = this._editStackElementsMap.get(key)!; + return editStackElement.heapSize(); + } + return 0; + } + + public split(): IResourceUndoRedoElement[] { + return this._editStackElementsArr; + } + + public toString(): string { + let result: string[] = []; + for (const editStackElement of this._editStackElementsArr) { + result.push(`${basename(editStackElement.resource)}: ${editStackElement}`); + } + return `{${result.join(', ')}}`; + } +} + +export type EditStackElement = SingleModelEditStackElement | MultiModelEditStackElement; + +function getModelEOL(model: ITextModel): EndOfLineSequence { const eol = model.getEOL(); if (eol === '\n') { return EndOfLineSequence.LF; @@ -66,115 +362,66 @@ function getModelEOL(model: TextModel): EndOfLineSequence { } } -class EOLStackElement implements IStackElement { - public readonly beforeVersionId: number; - public readonly beforeCursorState: Selection[] | null; - public readonly afterCursorState: Selection[] | null; - public afterVersionId: number; - - public eol: EndOfLineSequence; - - constructor(beforeVersionId: number, setEOL: EndOfLineSequence) { - this.beforeVersionId = beforeVersionId; - this.beforeCursorState = null; - this.afterCursorState = null; - this.afterVersionId = -1; - this.eol = setEOL; +export function isEditStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement { + if (!element) { + return false; } - - public undo(model: TextModel): void { - let redoEOL = getModelEOL(model); - model.setEOL(this.eol); - this.eol = redoEOL; - } - - public redo(model: TextModel): void { - let undoEOL = getModelEOL(model); - model.setEOL(this.eol); - this.eol = undoEOL; - } -} - -export interface IUndoRedoResult { - selections: Selection[] | null; - recordedVersionId: number; + return ((element instanceof SingleModelEditStackElement) || (element instanceof MultiModelEditStackElement)); } export class EditStack { - private readonly model: TextModel; - private currentOpenStackElement: IStackElement | null; - private past: IStackElement[]; - private future: IStackElement[]; + private readonly _model: TextModel; + private readonly _undoRedoService: IUndoRedoService; - constructor(model: TextModel) { - this.model = model; - this.currentOpenStackElement = null; - this.past = []; - this.future = []; + constructor(model: TextModel, undoRedoService: IUndoRedoService) { + this._model = model; + this._undoRedoService = undoRedoService; } public pushStackElement(): void { - if (this.currentOpenStackElement !== null) { - this.past.push(this.currentOpenStackElement); - this.currentOpenStackElement = null; + const lastElement = this._undoRedoService.getLastElement(this._model.uri); + if (isEditStackElement(lastElement)) { + lastElement.close(); } } public clear(): void { - this.currentOpenStackElement = null; - this.past = []; - this.future = []; + this._undoRedoService.removeElements(this._model.uri); + } + + private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement { + const lastElement = this._undoRedoService.getLastElement(this._model.uri); + if (isEditStackElement(lastElement) && lastElement.canAppend(this._model)) { + return lastElement; + } + const newElement = new SingleModelEditStackElement(this._model, beforeCursorState); + this._undoRedoService.pushElement(newElement); + return newElement; } public pushEOL(eol: EndOfLineSequence): void { - // No support for parallel universes :( - this.future = []; - - if (this.currentOpenStackElement) { - this.pushStackElement(); - } - - const prevEOL = getModelEOL(this.model); - let stackElement = new EOLStackElement(this.model.getAlternativeVersionId(), prevEOL); - - this.model.setEOL(eol); - - stackElement.afterVersionId = this.model.getVersionId(); - this.currentOpenStackElement = stackElement; - this.pushStackElement(); + const editStackElement = this._getOrCreateEditStackElement(null); + this._model.setEOL(eol); + editStackElement.append(this._model, [], getModelEOL(this._model), this._model.getAlternativeVersionId(), null); } - public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { - // No support for parallel universes :( - this.future = []; - - let stackElement: EditStackElement | null = null; - - if (this.currentOpenStackElement) { - if (this.currentOpenStackElement instanceof EditStackElement) { - stackElement = this.currentOpenStackElement; - } else { - this.pushStackElement(); + public pushEditOperation(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { + const editStackElement = this._getOrCreateEditStackElement(beforeCursorState); + const inverseEditOperations = this._model.applyEdits(editOperations, true); + const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations); + const textChanges = inverseEditOperations.map((op, index) => ({ index: index, textChange: op.textChange })); + textChanges.sort((a, b) => { + if (a.textChange.oldPosition === b.textChange.oldPosition) { + return a.index - b.index; } - } - - if (!this.currentOpenStackElement) { - stackElement = new EditStackElement(this.model.getAlternativeVersionId(), beforeCursorState); - this.currentOpenStackElement = stackElement; - } - - const inverseEditOperation: IEditOperation = { - operations: this.model.applyEdits(editOperations) - }; - - stackElement!.editOperations.push(inverseEditOperation); - stackElement!.afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperation.operations); - stackElement!.afterVersionId = this.model.getVersionId(); - return stackElement!.afterCursorState; + return a.textChange.oldPosition - b.textChange.oldPosition; + }); + editStackElement.append(this._model, textChanges.map(op => op.textChange), getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState); + return afterCursorState; } - private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null { + private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IValidEditOperation[]): Selection[] | null { try { return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null; } catch (e) { @@ -182,62 +429,4 @@ export class EditStack { return null; } } - - public undo(): IUndoRedoResult | null { - - this.pushStackElement(); - - if (this.past.length > 0) { - const pastStackElement = this.past.pop()!; - - try { - pastStackElement.undo(this.model); - } catch (e) { - onUnexpectedError(e); - this.clear(); - return null; - } - - this.future.push(pastStackElement); - - return { - selections: pastStackElement.beforeCursorState, - recordedVersionId: pastStackElement.beforeVersionId - }; - } - - return null; - } - - public canUndo(): boolean { - return (this.past.length > 0) || this.currentOpenStackElement !== null; - } - - public redo(): IUndoRedoResult | null { - - if (this.future.length > 0) { - const futureStackElement = this.future.pop()!; - - try { - futureStackElement.redo(this.model); - } catch (e) { - onUnexpectedError(e); - this.clear(); - return null; - } - - this.past.push(futureStackElement); - - return { - selections: futureStackElement.afterCursorState, - recordedVersionId: futureStackElement.afterVersionId - }; - } - - return null; - } - - public canRedo(): boolean { - return (this.future.length > 0); - } } diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index 25b3d150ce0..6a1272552b0 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/model/mirrorTextModel.ts @@ -31,6 +31,7 @@ export class MirrorTextModel { protected _eol: string; protected _versionId: number; protected _lineStarts: PrefixSumComputer | null; + private _cachedTextValue: string | null; constructor(uri: URI, lines: string[], eol: string, versionId: number) { this._uri = uri; @@ -38,6 +39,7 @@ export class MirrorTextModel { this._eol = eol; this._versionId = versionId; this._lineStarts = null; + this._cachedTextValue = null; } dispose(): void { @@ -49,7 +51,10 @@ export class MirrorTextModel { } getText(): string { - return this._lines.join(this._eol); + if (this._cachedTextValue === null) { + this._cachedTextValue = this._lines.join(this._eol); + } + return this._cachedTextValue; } onEvents(e: IModelChangedEvent): void { @@ -66,6 +71,7 @@ export class MirrorTextModel { } this._versionId = e.versionId; + this._cachedTextValue = null; } protected _ensureLineStarts(): void { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index ac23e98889c..03a518eb523 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -239,7 +239,7 @@ class PieceTreeSearchCache { this._cache.push(nodePosition); } - public valdiate(offset: number) { + public validate(offset: number) { let hasInvalidVal = false; let tmp: Array = this._cache; for (let i = 0; i < tmp.length; i++) { @@ -269,7 +269,7 @@ export class PieceTreeBase { protected _buffers!: StringBuffer[]; // 0 is change buffer, others are readonly original buffer. protected _lineCnt!: number; protected _length!: number; - protected _EOL!: string; + protected _EOL!: '\r\n' | '\n'; protected _EOLLength!: number; protected _EOLNormalized!: boolean; private _lastChangeBufferPos!: BufferCursor; @@ -351,7 +351,7 @@ export class PieceTreeBase { } // #region Buffer API - public getEOL(): string { + public getEOL(): '\r\n' | '\n' { return this._EOL; } @@ -626,8 +626,7 @@ export class PieceTreeBase { return this._lastVisitedLine.value; } - public getLineCharCode(lineNumber: number, index: number): number { - let nodePos = this.nodeAt2(lineNumber, index + 1); + private _getCharCode(nodePos: NodePosition): number { if (nodePos.remainder === nodePos.node.piece.length) { // the char we want to fetch is at the head of next node. let matchingNode = nodePos.node.next(); @@ -647,6 +646,11 @@ export class PieceTreeBase { } } + public getLineCharCode(lineNumber: number, index: number): number { + let nodePos = this.nodeAt2(lineNumber, index + 1); + return this._getCharCode(nodePos); + } + public getLineLength(lineNumber: number): number { if (lineNumber === this.getLineCount()) { let startOffset = this.getOffsetAt(lineNumber, 1); @@ -655,6 +659,11 @@ export class PieceTreeBase { return this.getOffsetAt(lineNumber + 1, 1) - this.getOffsetAt(lineNumber, 1) - this._EOLLength; } + public getCharCode(offset: number): number { + let nodePos = this.nodeAt(offset); + return this._getCharCode(nodePos); + } + public findMatchesInNode(node: TreeNode, searcher: Searcher, startLineNumber: number, startColumn: number, startCursor: BufferCursor, endCursor: BufferCursor, searchData: SearchData, captureMatches: boolean, limitResultCount: number, resultLen: number, result: FindMatch[]) { let buffer = this._buffers[node.piece.bufferIndex]; let startOffsetInBuffer = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start); @@ -670,7 +679,7 @@ export class PieceTreeBase { if (searcher._wordSeparators) { searchText = buffer.buffer.substring(start, end); offsetInBuffer = (offset: number) => offset + start; - searcher.reset(-1); + searcher.reset(0); } else { searchText = buffer.buffer; offsetInBuffer = (offset: number) => offset; @@ -838,7 +847,7 @@ export class PieceTreeBase { if (nodeStartOffset === offset) { this.insertContentToNodeLeft(value, node); - this._searchCache.valdiate(offset); + this._searchCache.validate(offset); } else if (nodeStartOffset + node.piece.length > offset) { // we are inserting into the middle of a node. let nodesToDel: TreeNode[] = []; @@ -938,7 +947,7 @@ export class PieceTreeBase { return; } this.deleteNodeHead(startNode, endSplitPosInBuffer); - this._searchCache.valdiate(offset); + this._searchCache.validate(offset); this.validateCRLFWithPrevNode(startNode); this.computeBufferMetadata(); return; @@ -961,7 +970,7 @@ export class PieceTreeBase { let startSplitPosInBuffer = this.positionInBuffer(startNode, startPosition.remainder); this.deleteNodeTail(startNode, startSplitPosInBuffer); - this._searchCache.valdiate(offset); + this._searchCache.validate(offset); if (startNode.piece.length === 0) { nodesToDel.push(startNode); } @@ -1225,14 +1234,14 @@ export class PieceTreeBase { let cache = this._searchCache.get2(lineNumber); if (cache) { x = cache.node; - let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber - 1); + let prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber - 1); let buffer = this._buffers[x.piece.bufferIndex].buffer; let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); if (cache.nodeStartLineNumber + x.piece.lineFeedCnt === lineNumber) { - ret = buffer.substring(startOffset + prevAccumualtedValue, startOffset + x.piece.length); + ret = buffer.substring(startOffset + prevAccumulatedValue, startOffset + x.piece.length); } else { - let accumualtedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber); - return buffer.substring(startOffset + prevAccumualtedValue, startOffset + accumualtedValue - endOffset); + let accumulatedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber); + return buffer.substring(startOffset + prevAccumulatedValue, startOffset + accumulatedValue - endOffset); } } else { let nodeStartOffset = 0; @@ -1241,8 +1250,8 @@ export class PieceTreeBase { if (x.left !== SENTINEL && x.lf_left >= lineNumber - 1) { x = x.left; } else if (x.lf_left + x.piece.lineFeedCnt > lineNumber - 1) { - let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); - let accumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); + let prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); + let accumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); let buffer = this._buffers[x.piece.bufferIndex].buffer; let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); nodeStartOffset += x.size_left; @@ -1252,13 +1261,13 @@ export class PieceTreeBase { nodeStartLineNumber: originalLineNumber - (lineNumber - 1 - x.lf_left) }); - return buffer.substring(startOffset + prevAccumualtedValue, startOffset + accumualtedValue - endOffset); + return buffer.substring(startOffset + prevAccumulatedValue, startOffset + accumulatedValue - endOffset); } else if (x.lf_left + x.piece.lineFeedCnt === lineNumber - 1) { - let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); + let prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); let buffer = this._buffers[x.piece.bufferIndex].buffer; let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); - ret = buffer.substring(startOffset + prevAccumualtedValue, startOffset + x.piece.length); + ret = buffer.substring(startOffset + prevAccumulatedValue, startOffset + x.piece.length); break; } else { lineNumber -= x.lf_left + x.piece.lineFeedCnt; @@ -1274,10 +1283,10 @@ export class PieceTreeBase { let buffer = this._buffers[x.piece.bufferIndex].buffer; if (x.piece.lineFeedCnt > 0) { - let accumualtedValue = this.getAccumulatedValue(x, 0); + let accumulatedValue = this.getAccumulatedValue(x, 0); let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); - ret += buffer.substring(startOffset, startOffset + accumualtedValue - endOffset); + ret += buffer.substring(startOffset, startOffset + accumulatedValue - endOffset); return ret; } else { let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); @@ -1304,7 +1313,7 @@ export class PieceTreeBase { this._lineCnt = lfCnt; this._length = len; - this._searchCache.valdiate(this._length); + this._searchCache.validate(this._length); } // #region node operations @@ -1504,12 +1513,12 @@ export class PieceTreeBase { x = x.left; } else if (x.lf_left + x.piece.lineFeedCnt > lineNumber - 1) { let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); - let accumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); + let accumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); nodeStartOffset += x.size_left; return { node: x, - remainder: Math.min(prevAccumualtedValue + column - 1, accumualtedValue), + remainder: Math.min(prevAccumualtedValue + column - 1, accumulatedValue), nodeStartOffset }; } else if (x.lf_left + x.piece.lineFeedCnt === lineNumber - 1) { @@ -1536,11 +1545,11 @@ export class PieceTreeBase { while (x !== SENTINEL) { if (x.piece.lineFeedCnt > 0) { - let accumualtedValue = this.getAccumulatedValue(x, 0); + let accumulatedValue = this.getAccumulatedValue(x, 0); let nodeStartOffset = this.offsetOfNode(x); return { node: x, - remainder: Math.min(column - 1, accumualtedValue), + remainder: Math.min(column - 1, accumulatedValue), nodeStartOffset }; } else { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index a987231c3ab..62ab2929910 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -3,12 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event } from 'vs/base/common/event'; import * as strings from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { ApplyEditsResult, EndOfLinePreference, FindMatch, IIdentifiedSingleEditOperation, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot } from 'vs/editor/common/model'; +import { ApplyEditsResult, EndOfLinePreference, FindMatch, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot, ValidAnnotatedEditOperation, IValidEditOperation } from 'vs/editor/common/model'; import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; +import { countEOL, StringEOL } from 'vs/editor/common/model/tokensStore'; +import { TextChange } from 'vs/editor/common/model/textChange'; +import { IDisposable } from 'vs/base/common/lifecycle'; export interface IValidatedEditOperation { sortIndex: number; @@ -16,27 +20,38 @@ export interface IValidatedEditOperation { range: Range; rangeOffset: number; rangeLength: number; - lines: string[] | null; + text: string; + eolCount: number; + firstLineLength: number; + lastLineLength: number; forceMoveMarkers: boolean; isAutoWhitespaceEdit: boolean; } -export interface IReverseSingleEditOperation extends IIdentifiedSingleEditOperation { +export interface IReverseSingleEditOperation extends IValidEditOperation { sortIndex: number; } -export class PieceTreeTextBuffer implements ITextBuffer { +export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { private readonly _pieceTree: PieceTreeBase; private readonly _BOM: string; private _mightContainRTL: boolean; + private _mightContainUnusualLineTerminators: boolean; private _mightContainNonBasicASCII: boolean; - constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, isBasicASCII: boolean, eolNormalized: boolean) { + private readonly _onDidChangeContent: Emitter = new Emitter(); + public readonly onDidChangeContent: Event = this._onDidChangeContent.event; + + constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, containsUnusualLineTerminators: boolean, isBasicASCII: boolean, eolNormalized: boolean) { this._BOM = BOM; this._mightContainNonBasicASCII = !isBasicASCII; this._mightContainRTL = containsRTL; + this._mightContainUnusualLineTerminators = containsUnusualLineTerminators; this._pieceTree = new PieceTreeBase(chunks, eol, eolNormalized); } + dispose(): void { + this._onDidChangeContent.dispose(); + } // #region TextBuffer public equals(other: ITextBuffer): boolean { @@ -54,13 +69,19 @@ export class PieceTreeTextBuffer implements ITextBuffer { public mightContainRTL(): boolean { return this._mightContainRTL; } + public mightContainUnusualLineTerminators(): boolean { + return this._mightContainUnusualLineTerminators; + } + public resetMightContainUnusualLineTerminators(): void { + this._mightContainUnusualLineTerminators = false; + } public mightContainNonBasicASCII(): boolean { return this._mightContainNonBasicASCII; } public getBOM(): string { return this._BOM; } - public getEOL(): string { + public getEOL(): '\r\n' | '\n' { return this._pieceTree.getEOL(); } @@ -157,6 +178,10 @@ export class PieceTreeTextBuffer implements ITextBuffer { return this._pieceTree.getLineCharCode(lineNumber, index); } + public getCharCode(offset: number): number { + return this._pieceTree.getCharCode(offset); + } + public getLineLength(lineNumber: number): number { return this._pieceTree.getLineLength(lineNumber); } @@ -193,16 +218,18 @@ export class PieceTreeTextBuffer implements ITextBuffer { return '\r\n'; case EndOfLinePreference.TextDefined: return this.getEOL(); + default: + throw new Error('Unknown EOL preference'); } - throw new Error('Unknown EOL preference'); } public setEOL(newEOL: '\r\n' | '\n'): void { this._pieceTree.setEOL(newEOL); } - public applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult { + public applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult { let mightContainRTL = this._mightContainRTL; + let mightContainUnusualLineTerminators = this._mightContainUnusualLineTerminators; let mightContainNonBasicASCII = this._mightContainNonBasicASCII; let canReduceOperations = true; @@ -213,20 +240,49 @@ export class PieceTreeTextBuffer implements ITextBuffer { canReduceOperations = false; } let validatedRange = op.range; - if (!mightContainRTL && op.text) { - // check if the new inserted text contains RTL - mightContainRTL = strings.containsRTL(op.text); + if (op.text) { + let textMightContainNonBasicASCII = true; + if (!mightContainNonBasicASCII) { + textMightContainNonBasicASCII = !strings.isBasicASCII(op.text); + mightContainNonBasicASCII = textMightContainNonBasicASCII; + } + if (!mightContainRTL && textMightContainNonBasicASCII) { + // check if the new inserted text contains RTL + mightContainRTL = strings.containsRTL(op.text); + } + if (!mightContainUnusualLineTerminators && textMightContainNonBasicASCII) { + // check if the new inserted text contains unusual line terminators + mightContainUnusualLineTerminators = strings.containsUnusualLineTerminators(op.text); + } } - if (!mightContainNonBasicASCII && op.text) { - mightContainNonBasicASCII = !strings.isBasicASCII(op.text); + + let validText = ''; + let eolCount = 0; + let firstLineLength = 0; + let lastLineLength = 0; + if (op.text) { + let strEOL: StringEOL; + [eolCount, firstLineLength, lastLineLength, strEOL] = countEOL(op.text); + + const bufferEOL = this.getEOL(); + const expectedStrEOL = (bufferEOL === '\r\n' ? StringEOL.CRLF : StringEOL.LF); + if (strEOL === StringEOL.Unknown || strEOL === expectedStrEOL) { + validText = op.text; + } else { + validText = op.text.replace(/\r\n|\r|\n/g, bufferEOL); + } } + operations[i] = { sortIndex: i, identifier: op.identifier || null, range: validatedRange, rangeOffset: this.getOffsetAt(validatedRange.startLineNumber, validatedRange.startColumn), rangeLength: this.getValueLengthInRange(validatedRange), - lines: op.text ? op.text.split(/\r\n|\r|\n/) : null, + text: validText, + eolCount: eolCount, + firstLineLength: firstLineLength, + lastLineLength: lastLineLength, forceMoveMarkers: Boolean(op.forceMoveMarkers), isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false }; @@ -254,48 +310,59 @@ export class PieceTreeTextBuffer implements ITextBuffer { } // Delta encode operations - let reverseRanges = PieceTreeTextBuffer._getInverseEditRanges(operations); + let reverseRanges = (computeUndoEdits || recordTrimAutoWhitespace ? PieceTreeTextBuffer._getInverseEditRanges(operations) : []); let newTrimAutoWhitespaceCandidates: { lineNumber: number, oldContent: string }[] = []; + if (recordTrimAutoWhitespace) { + for (let i = 0; i < operations.length; i++) { + let op = operations[i]; + let reverseRange = reverseRanges[i]; - for (let i = 0; i < operations.length; i++) { - let op = operations[i]; - let reverseRange = reverseRanges[i]; - - if (recordTrimAutoWhitespace && op.isAutoWhitespaceEdit && op.range.isEmpty()) { - // Record already the future line numbers that might be auto whitespace removal candidates on next edit - for (let lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) { - let currentLineContent = ''; - if (lineNumber === reverseRange.startLineNumber) { - currentLineContent = this.getLineContent(op.range.startLineNumber); - if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) { - continue; + if (op.isAutoWhitespaceEdit && op.range.isEmpty()) { + // Record already the future line numbers that might be auto whitespace removal candidates on next edit + for (let lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) { + let currentLineContent = ''; + if (lineNumber === reverseRange.startLineNumber) { + currentLineContent = this.getLineContent(op.range.startLineNumber); + if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) { + continue; + } } + newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent }); } - newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent }); } } } - let reverseOperations: IReverseSingleEditOperation[] = []; - for (let i = 0; i < operations.length; i++) { - let op = operations[i]; - let reverseRange = reverseRanges[i]; + let reverseOperations: IReverseSingleEditOperation[] | null = null; + if (computeUndoEdits) { - reverseOperations[i] = { - sortIndex: op.sortIndex, - identifier: op.identifier, - range: reverseRange, - text: this.getValueInRange(op.range), - forceMoveMarkers: op.forceMoveMarkers - }; + let reverseRangeDeltaOffset = 0; + reverseOperations = []; + for (let i = 0; i < operations.length; i++) { + const op = operations[i]; + const reverseRange = reverseRanges[i]; + const bufferText = this.getValueInRange(op.range); + const reverseRangeOffset = op.rangeOffset + reverseRangeDeltaOffset; + reverseRangeDeltaOffset += (op.text.length - bufferText.length); + + reverseOperations[i] = { + sortIndex: op.sortIndex, + identifier: op.identifier, + range: reverseRange, + text: bufferText, + textChange: new TextChange(op.rangeOffset, bufferText, reverseRangeOffset, op.text) + }; + } + + // Can only sort reverse operations when the order is not significant + if (!hasTouchingRanges) { + reverseOperations.sort((a, b) => a.sortIndex - b.sortIndex); + } } - // Can only sort reverse operations when the order is not significant - if (!hasTouchingRanges) { - reverseOperations.sort((a, b) => a.sortIndex - b.sortIndex); - } this._mightContainRTL = mightContainRTL; + this._mightContainUnusualLineTerminators = mightContainUnusualLineTerminators; this._mightContainNonBasicASCII = mightContainNonBasicASCII; const contentChanges = this._doApplyEdits(operations); @@ -324,6 +391,8 @@ export class PieceTreeTextBuffer implements ITextBuffer { } } + this._onDidChangeContent.fire(); + return new ApplyEditsResult( reverseOperations, contentChanges, @@ -350,58 +419,45 @@ export class PieceTreeTextBuffer implements ITextBuffer { } _toSingleEditOperation(operations: IValidatedEditOperation[]): IValidatedEditOperation { - let forceMoveMarkers = false, - firstEditRange = operations[0].range, - lastEditRange = operations[operations.length - 1].range, - entireEditRange = new Range(firstEditRange.startLineNumber, firstEditRange.startColumn, lastEditRange.endLineNumber, lastEditRange.endColumn), - lastEndLineNumber = firstEditRange.startLineNumber, - lastEndColumn = firstEditRange.startColumn, - result: string[] = []; + let forceMoveMarkers = false; + const firstEditRange = operations[0].range; + const lastEditRange = operations[operations.length - 1].range; + const entireEditRange = new Range(firstEditRange.startLineNumber, firstEditRange.startColumn, lastEditRange.endLineNumber, lastEditRange.endColumn); + let lastEndLineNumber = firstEditRange.startLineNumber; + let lastEndColumn = firstEditRange.startColumn; + const result: string[] = []; for (let i = 0, len = operations.length; i < len; i++) { - let operation = operations[i], - range = operation.range; + const operation = operations[i]; + const range = operation.range; forceMoveMarkers = forceMoveMarkers || operation.forceMoveMarkers; // (1) -- Push old text - for (let lineNumber = lastEndLineNumber; lineNumber < range.startLineNumber; lineNumber++) { - if (lineNumber === lastEndLineNumber) { - result.push(this.getLineContent(lineNumber).substring(lastEndColumn - 1)); - } else { - result.push('\n'); - result.push(this.getLineContent(lineNumber)); - } - } - - if (range.startLineNumber === lastEndLineNumber) { - result.push(this.getLineContent(range.startLineNumber).substring(lastEndColumn - 1, range.startColumn - 1)); - } else { - result.push('\n'); - result.push(this.getLineContent(range.startLineNumber).substring(0, range.startColumn - 1)); - } + result.push(this.getValueInRange(new Range(lastEndLineNumber, lastEndColumn, range.startLineNumber, range.startColumn))); // (2) -- Push new text - if (operation.lines) { - for (let j = 0, lenJ = operation.lines.length; j < lenJ; j++) { - if (j !== 0) { - result.push('\n'); - } - result.push(operation.lines[j]); - } + if (operation.text.length > 0) { + result.push(operation.text); } - lastEndLineNumber = operation.range.endLineNumber; - lastEndColumn = operation.range.endColumn; + lastEndLineNumber = range.endLineNumber; + lastEndColumn = range.endColumn; } + const text = result.join(''); + const [eolCount, firstLineLength, lastLineLength] = countEOL(text); + return { sortIndex: 0, identifier: operations[0].identifier, range: entireEditRange, rangeOffset: this.getOffsetAt(entireEditRange.startLineNumber, entireEditRange.startColumn), rangeLength: this.getValueLengthInRange(entireEditRange, EndOfLinePreference.TextDefined), - lines: result.join('').split('\n'), + text: text, + eolCount: eolCount, + firstLineLength: firstLineLength, + lastLineLength: lastLineLength, forceMoveMarkers: forceMoveMarkers, isAutoWhitespaceEdit: false }; @@ -421,41 +477,26 @@ export class PieceTreeTextBuffer implements ITextBuffer { const endLineNumber = op.range.endLineNumber; const endColumn = op.range.endColumn; - if (startLineNumber === endLineNumber && startColumn === endColumn && (!op.lines || op.lines.length === 0)) { + if (startLineNumber === endLineNumber && startColumn === endColumn && op.text.length === 0) { // no-op continue; } - const deletingLinesCnt = endLineNumber - startLineNumber; - const insertingLinesCnt = (op.lines ? op.lines.length - 1 : 0); - const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt); - - const text = (op.lines ? op.lines.join(this.getEOL()) : ''); - - if (text) { + if (op.text) { // replacement this._pieceTree.delete(op.rangeOffset, op.rangeLength); - this._pieceTree.insert(op.rangeOffset, text, true); + this._pieceTree.insert(op.rangeOffset, op.text, true); } else { // deletion this._pieceTree.delete(op.rangeOffset, op.rangeLength); } - if (editingLinesCnt < insertingLinesCnt) { - let newLinesContent: string[] = []; - for (let j = editingLinesCnt + 1; j <= insertingLinesCnt; j++) { - newLinesContent.push(op.lines![j]); - } - - newLinesContent[newLinesContent.length - 1] = this.getLineContent(startLineNumber + insertingLinesCnt - 1); - } - const contentChangeRange = new Range(startLineNumber, startColumn, endLineNumber, endColumn); contentChanges.push({ range: contentChangeRange, rangeLength: op.rangeLength, - text: text, + text: op.text, rangeOffset: op.rangeOffset, forceMoveMarkers: op.forceMoveMarkers }); @@ -474,6 +515,32 @@ export class PieceTreeTextBuffer implements ITextBuffer { public getPieceTree(): PieceTreeBase { return this._pieceTree; } + + public static _getInverseEditRange(range: Range, text: string) { + let startLineNumber = range.startLineNumber; + let startColumn = range.startColumn; + const [eolCount, firstLineLength, lastLineLength] = countEOL(text); + let resultRange: Range; + + if (text.length > 0) { + // the operation inserts something + const lineCount = eolCount + 1; + + if (lineCount === 1) { + // single line insert + resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + firstLineLength); + } else { + // multi line insert + resultRange = new Range(startLineNumber, startColumn, startLineNumber + lineCount - 1, lastLineLength + 1); + } + } else { + // There is nothing to insert + resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn); + } + + return resultRange; + } + /** * Assumes `operations` are validated and sorted ascending */ @@ -504,18 +571,16 @@ export class PieceTreeTextBuffer implements ITextBuffer { let resultRange: Range; - if (op.lines && op.lines.length > 0) { + if (op.text.length > 0) { // the operation inserts something - let lineCount = op.lines.length; - let firstLine = op.lines[0]; - let lastLine = op.lines[lineCount - 1]; + const lineCount = op.eolCount + 1; if (lineCount === 1) { // single line insert - resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + firstLine.length); + resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + op.firstLineLength); } else { // multi line insert - resultRange = new Range(startLineNumber, startColumn, startLineNumber + lineCount - 1, lastLine.length + 1); + resultRange = new Range(startLineNumber, startColumn, startLineNumber + lineCount - 1, op.lastLineLength + 1); } } else { // There is nothing to insert diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts index fa8d98e94e6..d134517ba15 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts @@ -18,6 +18,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory { private readonly _lf: number, private readonly _crlf: number, private readonly _containsRTL: boolean, + private readonly _containsUnusualLineTerminators: boolean, private readonly _isBasicASCII: boolean, private readonly _normalizeEOL: boolean ) { } @@ -53,7 +54,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory { } } - return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._isBasicASCII, this._normalizeEOL); + return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._containsUnusualLineTerminators, this._isBasicASCII, this._normalizeEOL); } public getFirstLineText(lengthLimit: number): string { @@ -73,6 +74,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { private lf: number; private crlf: number; private containsRTL: boolean; + private containsUnusualLineTerminators: boolean; private isBasicASCII: boolean; constructor() { @@ -87,6 +89,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { this.lf = 0; this.crlf = 0; this.containsRTL = false; + this.containsUnusualLineTerminators = false; this.isBasicASCII = true; } @@ -140,9 +143,13 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { this.isBasicASCII = lineStarts.isBasicASCII; } if (!this.isBasicASCII && !this.containsRTL) { - // No need to check if is basic ASCII + // No need to check if it is basic ASCII this.containsRTL = strings.containsRTL(chunk); } + if (!this.isBasicASCII && !this.containsUnusualLineTerminators) { + // No need to check if it is basic ASCII + this.containsUnusualLineTerminators = strings.containsUnusualLineTerminators(chunk); + } } public finish(normalizeEOL: boolean = true): PieceTreeTextBufferFactory { @@ -154,6 +161,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { this.lf, this.crlf, this.containsRTL, + this.containsUnusualLineTerminators, this.isBasicASCII, normalizeEOL ); diff --git a/src/vs/editor/common/model/textChange.ts b/src/vs/editor/common/model/textChange.ts new file mode 100644 index 00000000000..a213f13b494 --- /dev/null +++ b/src/vs/editor/common/model/textChange.ts @@ -0,0 +1,344 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as buffer from 'vs/base/common/buffer'; +import { decodeUTF16LE } from 'vs/editor/common/core/stringBuilder'; + +function escapeNewLine(str: string): string { + return ( + str + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + ); +} + +export class TextChange { + + public get oldLength(): number { + return this.oldText.length; + } + + public get oldEnd(): number { + return this.oldPosition + this.oldText.length; + } + + public get newLength(): number { + return this.newText.length; + } + + public get newEnd(): number { + return this.newPosition + this.newText.length; + } + + constructor( + public readonly oldPosition: number, + public readonly oldText: string, + public readonly newPosition: number, + public readonly newText: string + ) { } + + public toString(): string { + if (this.oldText.length === 0) { + return `(insert@${this.oldPosition} "${escapeNewLine(this.newText)}")`; + } + if (this.newText.length === 0) { + return `(delete@${this.oldPosition} "${escapeNewLine(this.oldText)}")`; + } + return `(replace@${this.oldPosition} "${escapeNewLine(this.oldText)}" with "${escapeNewLine(this.newText)}")`; + } + + private static _writeStringSize(str: string): number { + return ( + 4 + 2 * str.length + ); + } + + private static _writeString(b: Uint8Array, str: string, offset: number): number { + const len = str.length; + buffer.writeUInt32BE(b, len, offset); offset += 4; + for (let i = 0; i < len; i++) { + buffer.writeUInt16LE(b, str.charCodeAt(i), offset); offset += 2; + } + return offset; + } + + private static _readString(b: Uint8Array, offset: number): string { + const len = buffer.readUInt32BE(b, offset); offset += 4; + return decodeUTF16LE(b, offset, len); + } + + public writeSize(): number { + return ( + + 4 // oldPosition + + 4 // newPosition + + TextChange._writeStringSize(this.oldText) + + TextChange._writeStringSize(this.newText) + ); + } + + public write(b: Uint8Array, offset: number): number { + buffer.writeUInt32BE(b, this.oldPosition, offset); offset += 4; + buffer.writeUInt32BE(b, this.newPosition, offset); offset += 4; + offset = TextChange._writeString(b, this.oldText, offset); + offset = TextChange._writeString(b, this.newText, offset); + return offset; + } + + public static read(b: Uint8Array, offset: number, dest: TextChange[]): number { + const oldPosition = buffer.readUInt32BE(b, offset); offset += 4; + const newPosition = buffer.readUInt32BE(b, offset); offset += 4; + const oldText = TextChange._readString(b, offset); offset += TextChange._writeStringSize(oldText); + const newText = TextChange._readString(b, offset); offset += TextChange._writeStringSize(newText); + dest.push(new TextChange(oldPosition, oldText, newPosition, newText)); + return offset; + } +} + +export function compressConsecutiveTextChanges(prevEdits: TextChange[] | null, currEdits: TextChange[]): TextChange[] { + if (prevEdits === null || prevEdits.length === 0) { + return currEdits; + } + const compressor = new TextChangeCompressor(prevEdits, currEdits); + return compressor.compress(); +} + +class TextChangeCompressor { + + private _prevEdits: TextChange[]; + private _currEdits: TextChange[]; + + private _result: TextChange[]; + private _resultLen: number; + + private _prevLen: number; + private _prevDeltaOffset: number; + + private _currLen: number; + private _currDeltaOffset: number; + + constructor(prevEdits: TextChange[], currEdits: TextChange[]) { + this._prevEdits = prevEdits; + this._currEdits = currEdits; + + this._result = []; + this._resultLen = 0; + + this._prevLen = this._prevEdits.length; + this._prevDeltaOffset = 0; + + this._currLen = this._currEdits.length; + this._currDeltaOffset = 0; + } + + public compress(): TextChange[] { + let prevIndex = 0; + let currIndex = 0; + + let prevEdit = this._getPrev(prevIndex); + let currEdit = this._getCurr(currIndex); + + while (prevIndex < this._prevLen || currIndex < this._currLen) { + + if (prevEdit === null) { + this._acceptCurr(currEdit!); + currEdit = this._getCurr(++currIndex); + continue; + } + + if (currEdit === null) { + this._acceptPrev(prevEdit); + prevEdit = this._getPrev(++prevIndex); + continue; + } + + if (currEdit.oldEnd <= prevEdit.newPosition) { + this._acceptCurr(currEdit); + currEdit = this._getCurr(++currIndex); + continue; + } + + if (prevEdit.newEnd <= currEdit.oldPosition) { + this._acceptPrev(prevEdit); + prevEdit = this._getPrev(++prevIndex); + continue; + } + + if (currEdit.oldPosition < prevEdit.newPosition) { + const [e1, e2] = TextChangeCompressor._splitCurr(currEdit, prevEdit.newPosition - currEdit.oldPosition); + this._acceptCurr(e1); + currEdit = e2; + continue; + } + + if (prevEdit.newPosition < currEdit.oldPosition) { + const [e1, e2] = TextChangeCompressor._splitPrev(prevEdit, currEdit.oldPosition - prevEdit.newPosition); + this._acceptPrev(e1); + prevEdit = e2; + continue; + } + + // At this point, currEdit.oldPosition === prevEdit.newPosition + + let mergePrev: TextChange; + let mergeCurr: TextChange; + + if (currEdit.oldEnd === prevEdit.newEnd) { + mergePrev = prevEdit; + mergeCurr = currEdit; + prevEdit = this._getPrev(++prevIndex); + currEdit = this._getCurr(++currIndex); + } else if (currEdit.oldEnd < prevEdit.newEnd) { + const [e1, e2] = TextChangeCompressor._splitPrev(prevEdit, currEdit.oldLength); + mergePrev = e1; + mergeCurr = currEdit; + prevEdit = e2; + currEdit = this._getCurr(++currIndex); + } else { + const [e1, e2] = TextChangeCompressor._splitCurr(currEdit, prevEdit.newLength); + mergePrev = prevEdit; + mergeCurr = e1; + prevEdit = this._getPrev(++prevIndex); + currEdit = e2; + } + + this._result[this._resultLen++] = new TextChange( + mergePrev.oldPosition, + mergePrev.oldText, + mergeCurr.newPosition, + mergeCurr.newText + ); + this._prevDeltaOffset += mergePrev.newLength - mergePrev.oldLength; + this._currDeltaOffset += mergeCurr.newLength - mergeCurr.oldLength; + } + + const merged = TextChangeCompressor._merge(this._result); + const cleaned = TextChangeCompressor._removeNoOps(merged); + return cleaned; + } + + private _acceptCurr(currEdit: TextChange): void { + this._result[this._resultLen++] = TextChangeCompressor._rebaseCurr(this._prevDeltaOffset, currEdit); + this._currDeltaOffset += currEdit.newLength - currEdit.oldLength; + } + + private _getCurr(currIndex: number): TextChange | null { + return (currIndex < this._currLen ? this._currEdits[currIndex] : null); + } + + private _acceptPrev(prevEdit: TextChange): void { + this._result[this._resultLen++] = TextChangeCompressor._rebasePrev(this._currDeltaOffset, prevEdit); + this._prevDeltaOffset += prevEdit.newLength - prevEdit.oldLength; + } + + private _getPrev(prevIndex: number): TextChange | null { + return (prevIndex < this._prevLen ? this._prevEdits[prevIndex] : null); + } + + private static _rebaseCurr(prevDeltaOffset: number, currEdit: TextChange): TextChange { + return new TextChange( + currEdit.oldPosition - prevDeltaOffset, + currEdit.oldText, + currEdit.newPosition, + currEdit.newText + ); + } + + private static _rebasePrev(currDeltaOffset: number, prevEdit: TextChange): TextChange { + return new TextChange( + prevEdit.oldPosition, + prevEdit.oldText, + prevEdit.newPosition + currDeltaOffset, + prevEdit.newText + ); + } + + private static _splitPrev(edit: TextChange, offset: number): [TextChange, TextChange] { + const preText = edit.newText.substr(0, offset); + const postText = edit.newText.substr(offset); + + return [ + new TextChange( + edit.oldPosition, + edit.oldText, + edit.newPosition, + preText + ), + new TextChange( + edit.oldEnd, + '', + edit.newPosition + offset, + postText + ) + ]; + } + + private static _splitCurr(edit: TextChange, offset: number): [TextChange, TextChange] { + const preText = edit.oldText.substr(0, offset); + const postText = edit.oldText.substr(offset); + + return [ + new TextChange( + edit.oldPosition, + preText, + edit.newPosition, + edit.newText + ), + new TextChange( + edit.oldPosition + offset, + postText, + edit.newEnd, + '' + ) + ]; + } + + private static _merge(edits: TextChange[]): TextChange[] { + if (edits.length === 0) { + return edits; + } + + let result: TextChange[] = [], resultLen = 0; + + let prev = edits[0]; + for (let i = 1; i < edits.length; i++) { + const curr = edits[i]; + + if (prev.oldEnd === curr.oldPosition) { + // Merge into `prev` + prev = new TextChange( + prev.oldPosition, + prev.oldText + curr.oldText, + prev.newPosition, + prev.newText + curr.newText + ); + } else { + result[resultLen++] = prev; + prev = curr; + } + } + result[resultLen++] = prev; + + return result; + } + + private static _removeNoOps(edits: TextChange[]): TextChange[] { + if (edits.length === 0) { + return edits; + } + + let result: TextChange[] = [], resultLen = 0; + + for (let i = 0; i < edits.length; i++) { + const edit = edits[i]; + + if (edit.oldText === edit.newText) { + continue; + } + result[resultLen++] = edit; + } + + return result; + } +} diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index f154d2bc0f8..24f25a4e52b 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -29,11 +29,13 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; -import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; -import { withUndefinedAsNull } from 'vs/base/common/types'; +import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer'; import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore'; import { Color } from 'vs/base/common/color'; +import { EditorTheme } from 'vs/editor/common/view/viewContext'; +import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; +import { TextChange } from 'vs/editor/common/model/textChange'; import { Constants } from 'vs/base/common/uint'; function createTextBufferBuilder() { @@ -169,6 +171,21 @@ const enum StringOffsetValidationType { SurrogatePairs = 1, } +type ContinueBracketSearchPredicate = null | (() => boolean); + +class BracketSearchCanceled { + public static INSTANCE = new BracketSearchCanceled(); + _searchCanceledBrand = undefined; + private constructor() { } +} + +function stripBracketSearchCanceled(result: T | null | BracketSearchCanceled): T | null { + if (result instanceof BracketSearchCanceled) { + return null; + } + return result; +} + export class TextModel extends Disposable implements model.ITextModel { private static readonly MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB @@ -186,10 +203,6 @@ export class TextModel extends Disposable implements model.ITextModel { largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations, }; - public static createFromString(text: string, options: model.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier | null = null, uri: URI | null = null): TextModel { - return new TextModel(text, options, languageIdentifier, uri); - } - public static resolveOptions(textBuffer: model.ITextBuffer, options: model.ITextModelCreationOptions): model.TextModelResolvedOptions { if (options.detectIndentation) { const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces); @@ -252,6 +265,7 @@ export class TextModel extends Disposable implements model.ITextModel { public readonly id: string; public readonly isForSimpleWidget: boolean; private readonly _associatedResource: URI; + private readonly _undoRedoService: IUndoRedoService; private _attachedEditorCount: number; private _buffer: model.ITextBuffer; private _options: model.TextModelResolvedOptions; @@ -263,11 +277,12 @@ export class TextModel extends Disposable implements model.ITextModel { * Unlike, versionId, this can go down (via undo) or go to previous values (via redo) */ private _alternativeVersionId: number; + private _initialUndoRedoSnapshot: ResourceEditStackSnapshot | null; private readonly _isTooLargeForSyncing: boolean; private readonly _isTooLargeForTokenization: boolean; //#region Editing - private _commandManager: EditStack; + private readonly _commandManager: EditStack; private _isUndoing: boolean; private _isRedoing: boolean; private _trimAutoWhitespaceLines: number[] | null; @@ -292,7 +307,13 @@ export class TextModel extends Disposable implements model.ITextModel { private readonly _tokenization: TextModelTokenization; //#endregion - constructor(source: string | model.ITextBufferFactory, creationOptions: model.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier | null, associatedResource: URI | null = null) { + constructor( + source: string | model.ITextBufferFactory, + creationOptions: model.ITextModelCreationOptions, + languageIdentifier: LanguageIdentifier | null, + associatedResource: URI | null = null, + undoRedoService: IUndoRedoService + ) { super(); // Generate a new unique model id @@ -304,6 +325,7 @@ export class TextModel extends Disposable implements model.ITextModel { } else { this._associatedResource = associatedResource; } + this._undoRedoService = undoRedoService; this._attachedEditorCount = 0; this._buffer = createTextBuffer(source, creationOptions.defaultEOL); @@ -329,6 +351,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._versionId = 1; this._alternativeVersionId = 1; + this._initialUndoRedoSnapshot = null; this._isDisposed = false; this._isDisposing = false; @@ -346,7 +369,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._decorations = Object.create(null); this._decorationsTree = new DecorationsTrees(); - this._commandManager = new EditStack(this); + this._commandManager = new EditStack(this, undoRedoService); this._isUndoing = false; this._isRedoing = false; this._trimAutoWhitespaceLines = null; @@ -377,6 +400,11 @@ export class TextModel extends Disposable implements model.ITextModel { return this._buffer.equals(other); } + public getTextBuffer(): model.ITextBuffer { + this._assertNotDisposed(); + return this._buffer; + } + private _emitContentChangedEvent(rawChange: ModelRawContentChangedEvent, change: IModelContentChangedEvent): void { if (this._isDisposing) { // Do not confuse listeners by emitting any event after disposing @@ -435,7 +463,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._decorationsTree = new DecorationsTrees(); // Destroy my edit history and settings - this._commandManager = new EditStack(this); + this._commandManager.clear(); this._trimAutoWhitespaceLines = null; this._emitContentChangedEvent( @@ -673,6 +701,16 @@ export class TextModel extends Disposable implements model.ITextModel { return this._buffer.mightContainRTL(); } + public mightContainUnusualLineTerminators(): boolean { + return this._buffer.mightContainUnusualLineTerminators(); + } + + public removeUnusualLineTerminators(selections: Selection[] | null = null): void { + const matches = this.findMatches(strings.UNUSUAL_LINE_TERMINATORS.source, false, true, false, null, false, Constants.MAX_SAFE_SMALL_INTEGER); + this._buffer.resetMightContainUnusualLineTerminators(); + this.pushEditOperations(selections, matches.map(m => ({ range: m.range, text: null })), () => null); + } + public mightContainNonBasicASCII(): boolean { return this._buffer.mightContainNonBasicASCII(); } @@ -682,6 +720,11 @@ export class TextModel extends Disposable implements model.ITextModel { return this._alternativeVersionId; } + public getInitialUndoRedoSnapshot(): ResourceEditStackSnapshot | null { + this._assertNotDisposed(); + return this._initialUndoRedoSnapshot; + } + public getOffsetAt(rawPosition: IPosition): number { this._assertNotDisposed(); let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, StringOffsetValidationType.Relaxed); @@ -699,10 +742,18 @@ export class TextModel extends Disposable implements model.ITextModel { this._alternativeVersionId = this._versionId; } - private _overwriteAlternativeVersionId(newAlternativeVersionId: number): void { + public _overwriteVersionId(versionId: number): void { + this._versionId = versionId; + } + + public _overwriteAlternativeVersionId(newAlternativeVersionId: number): void { this._alternativeVersionId = newAlternativeVersionId; } + public _overwriteInitialUndoRedoSnapshot(newInitialUndoRedoSnapshot: ResourceEditStackSnapshot | null): void { + this._initialUndoRedoSnapshot = newInitialUndoRedoSnapshot; + } + public getValue(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): string { this._assertNotDisposed(); const fullModelRange = this.getFullModelRange(); @@ -1067,16 +1118,38 @@ export class TextModel extends Disposable implements model.ITextModel { return this._buffer.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount); } - public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] { + public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] { this._assertNotDisposed(); - let searchRange: Range; - if (Range.isIRange(rawSearchScope)) { - searchRange = this.validateRange(rawSearchScope); - } else { - searchRange = this.getFullModelRange(); + let searchRanges: Range[] | null = null; + + if (rawSearchScope !== null) { + if (!Array.isArray(rawSearchScope)) { + rawSearchScope = [rawSearchScope]; + } + + if (rawSearchScope.every((searchScope: Range) => Range.isIRange(searchScope))) { + searchRanges = rawSearchScope.map((searchScope: Range) => this.validateRange(searchScope)); + } } + if (searchRanges === null) { + searchRanges = [this.getFullModelRange()]; + } + + searchRanges = searchRanges.sort((d1, d2) => d1.startLineNumber - d2.startLineNumber || d1.startColumn - d2.startColumn); + + const uniqueSearchRanges: Range[] = []; + uniqueSearchRanges.push(searchRanges.reduce((prev, curr) => { + if (Range.areIntersecting(prev, curr)) { + return prev.plusRange(curr); + } + + uniqueSearchRanges.push(prev); + return curr; + })); + + let matchMapper: (value: Range, index: number, array: Range[]) => model.FindMatch[]; if (!isRegex && searchString.indexOf('\n') < 0) { // not regex, not multi line const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators); @@ -1086,10 +1159,12 @@ export class TextModel extends Disposable implements model.ITextModel { return []; } - return this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount); + matchMapper = (searchRange: Range) => this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount); + } else { + matchMapper = (searchRange: Range) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount); } - return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount); + return uniqueSearchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []); } public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null { @@ -1146,6 +1221,9 @@ export class TextModel extends Disposable implements model.ITextModel { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); + if (this._initialUndoRedoSnapshot === null) { + this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri); + } this._commandManager.pushEOL(eol); } finally { this._eventEmitter.endDeferredEmit(); @@ -1153,18 +1231,40 @@ export class TextModel extends Disposable implements model.ITextModel { } } - public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { + private _validateEditOperation(rawOperation: model.IIdentifiedSingleEditOperation): model.ValidAnnotatedEditOperation { + if (rawOperation instanceof model.ValidAnnotatedEditOperation) { + return rawOperation; + } + return new model.ValidAnnotatedEditOperation( + rawOperation.identifier || null, + this.validateRange(rawOperation.range), + rawOperation.text, + rawOperation.forceMoveMarkers || false, + rawOperation.isAutoWhitespaceEdit || false, + rawOperation._isTracked || false + ); + } + + private _validateEditOperations(rawOperations: model.IIdentifiedSingleEditOperation[]): model.ValidAnnotatedEditOperation[] { + const result: model.ValidAnnotatedEditOperation[] = []; + for (let i = 0, len = rawOperations.length; i < len; i++) { + result[i] = this._validateEditOperation(rawOperations[i]); + } + return result; + } + + public pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); - return this._pushEditOperations(beforeCursorState, editOperations, cursorStateComputer); + return this._pushEditOperations(beforeCursorState, this._validateEditOperations(editOperations), cursorStateComputer); } finally { this._eventEmitter.endDeferredEmit(); this._onDidChangeDecorations.endDeferredEmit(); } } - private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { + private _pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) { // Go through each saved line number and insert a trim whitespace edit // if it is safe to do so (no conflicts with other edits). @@ -1179,22 +1279,24 @@ export class TextModel extends Disposable implements model.ITextModel { // Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor // We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace let editsAreNearCursors = true; - for (let i = 0, len = beforeCursorState.length; i < len; i++) { - let sel = beforeCursorState[i]; - let foundEditNearSel = false; - for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) { - let editRange = incomingEdits[j].range; - let selIsAbove = editRange.startLineNumber > sel.endLineNumber; - let selIsBelow = sel.startLineNumber > editRange.endLineNumber; - if (!selIsAbove && !selIsBelow) { - foundEditNearSel = true; + if (beforeCursorState) { + for (let i = 0, len = beforeCursorState.length; i < len; i++) { + let sel = beforeCursorState[i]; + let foundEditNearSel = false; + for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) { + let editRange = incomingEdits[j].range; + let selIsAbove = editRange.startLineNumber > sel.endLineNumber; + let selIsBelow = sel.startLineNumber > editRange.endLineNumber; + if (!selIsAbove && !selIsBelow) { + foundEditNearSel = true; + break; + } + } + if (!foundEditNearSel) { + editsAreNearCursors = false; break; } } - if (!foundEditNearSel) { - editsAreNearCursors = false; - break; - } } if (editsAreNearCursors) { @@ -1237,10 +1339,8 @@ export class TextModel extends Disposable implements model.ITextModel { } if (allowTrimLine) { - editOperations.push({ - range: new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn), - text: null - }); + const trimRange = new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn); + editOperations.push(new model.ValidAnnotatedEditOperation(null, trimRange, null, false, false, false)); } } @@ -1248,27 +1348,72 @@ export class TextModel extends Disposable implements model.ITextModel { this._trimAutoWhitespaceLines = null; } + if (this._initialUndoRedoSnapshot === null) { + this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri); + } return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer); } - public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] { + _applyUndo(changes: TextChange[], eol: model.EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void { + const edits = changes.map((change) => { + const rangeStart = this.getPositionAt(change.newPosition); + const rangeEnd = this.getPositionAt(change.newEnd); + return { + range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column), + text: change.oldText + }; + }); + this._applyUndoRedoEdits(edits, eol, true, false, resultingAlternativeVersionId, resultingSelection); + } + + _applyRedo(changes: TextChange[], eol: model.EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void { + const edits = changes.map((change) => { + const rangeStart = this.getPositionAt(change.oldPosition); + const rangeEnd = this.getPositionAt(change.oldEnd); + return { + range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column), + text: change.newText + }; + }); + this._applyUndoRedoEdits(edits, eol, false, true, resultingAlternativeVersionId, resultingSelection); + } + + private _applyUndoRedoEdits(edits: model.IIdentifiedSingleEditOperation[], eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); - return this._applyEdits(rawOperations); + this._isUndoing = isUndoing; + this._isRedoing = isRedoing; + this.applyEdits(edits, false); + this.setEOL(eol); + this._overwriteAlternativeVersionId(resultingAlternativeVersionId); + } finally { + this._isUndoing = false; + this._isRedoing = false; + this._eventEmitter.endDeferredEmit(resultingSelection); + this._onDidChangeDecorations.endDeferredEmit(); + } + } + + public applyEdits(operations: model.IIdentifiedSingleEditOperation[]): void; + public applyEdits(operations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: false): void; + public applyEdits(operations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: true): model.IValidEditOperation[]; + public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: boolean = false): void | model.IValidEditOperation[] { + try { + this._onDidChangeDecorations.beginDeferredEmit(); + this._eventEmitter.beginDeferredEmit(); + const operations = this._validateEditOperations(rawOperations); + return this._doApplyEdits(operations, computeUndoEdits); } finally { this._eventEmitter.endDeferredEmit(); this._onDidChangeDecorations.endDeferredEmit(); } } - private _applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] { - for (let i = 0, len = rawOperations.length; i < len; i++) { - rawOperations[i].range = this.validateRange(rawOperations[i].range); - } + private _doApplyEdits(rawOperations: model.ValidAnnotatedEditOperation[], computeUndoEdits: boolean): void | model.IValidEditOperation[] { const oldLineCount = this._buffer.getLineCount(); - const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace); + const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace, computeUndoEdits); const newLineCount = this._buffer.getLineCount(); const contentChanges = result.changes; @@ -1343,65 +1488,23 @@ export class TextModel extends Disposable implements model.ITextModel { ); } - return result.reverseEdits; + return (result.reverseEdits === null ? undefined : result.reverseEdits); } - private _undo(): Selection[] | null { - this._isUndoing = true; - let r = this._commandManager.undo(); - this._isUndoing = false; - - if (!r) { - return null; - } - - this._overwriteAlternativeVersionId(r.recordedVersionId); - - return r.selections; - } - - public undo(): Selection[] | null { - try { - this._onDidChangeDecorations.beginDeferredEmit(); - this._eventEmitter.beginDeferredEmit(); - return this._undo(); - } finally { - this._eventEmitter.endDeferredEmit(); - this._onDidChangeDecorations.endDeferredEmit(); - } + public undo(): void | Promise { + return this._undoRedoService.undo(this.uri); } public canUndo(): boolean { - return this._commandManager.canUndo(); + return this._undoRedoService.canUndo(this.uri); } - private _redo(): Selection[] | null { - this._isRedoing = true; - let r = this._commandManager.redo(); - this._isRedoing = false; - - if (!r) { - return null; - } - - this._overwriteAlternativeVersionId(r.recordedVersionId); - - return r.selections; - } - - public redo(): Selection[] | null { - try { - this._onDidChangeDecorations.beginDeferredEmit(); - this._eventEmitter.beginDeferredEmit(); - return this._redo(); - } finally { - this._eventEmitter.endDeferredEmit(); - this._onDidChangeDecorations.endDeferredEmit(); - } + public redo(): void | Promise { + return this._undoRedoService.redo(this.uri); } public canRedo(): boolean { - return this._commandManager.canRedo(); + return this._undoRedoService.canRedo(this.uri); } //#endregion @@ -1422,19 +1525,15 @@ export class TextModel extends Disposable implements model.ITextModel { private _changeDecorations(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null { let changeAccessor: model.IModelDecorationsChangeAccessor = { addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => { - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0]; }, changeDecoration: (id: string, newRange: IRange): void => { - this._onDidChangeDecorations.fire(); this._changeDecorationImpl(id, newRange); }, changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => { - this._onDidChangeDecorations.fire(); this._changeDecorationOptionsImpl(id, _normalizeOptions(options)); }, removeDecoration: (id: string): void => { - this._onDidChangeDecorations.fire(); this._deltaDecorationsImpl(ownerId, [id], []); }, deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => { @@ -1442,7 +1541,6 @@ export class TextModel extends Disposable implements model.ITextModel { // nothing to do return []; } - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); } }; @@ -1473,7 +1571,6 @@ export class TextModel extends Disposable implements model.ITextModel { try { this._onDidChangeDecorations.beginDeferredEmit(); - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); } finally { this._onDidChangeDecorations.endDeferredEmit(); @@ -1621,6 +1718,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._decorationsTree.delete(node); node.reset(this.getVersionId(), startOffset, endOffset, range); this._decorationsTree.insert(node); + this._onDidChangeDecorations.checkAffectedAndFire(node.options); } private _changeDecorationOptionsImpl(decorationId: string, options: ModelDecorationOptions): void { @@ -1632,6 +1730,9 @@ export class TextModel extends Disposable implements model.ITextModel { const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false); const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false); + this._onDidChangeDecorations.checkAffectedAndFire(node.options); + this._onDidChangeDecorations.checkAffectedAndFire(options); + if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) { // Delete + Insert due to an overview ruler status change this._decorationsTree.delete(node); @@ -1665,6 +1766,7 @@ export class TextModel extends Disposable implements model.ITextModel { // (2) remove the node from the tree (if it exists) if (node) { this._decorationsTree.delete(node); + this._onDidChangeDecorations.checkAffectedAndFire(node.options); } } @@ -1687,6 +1789,7 @@ export class TextModel extends Disposable implements model.ITextModel { node.ownerId = ownerId; node.reset(versionId, startOffset, endOffset, range); node.setOptions(options); + this._onDidChangeDecorations.checkAffectedAndFire(options); this._decorationsTree.insert(node); @@ -1712,7 +1815,7 @@ export class TextModel extends Disposable implements model.ITextModel { throw new Error('Illegal value for lineNumber'); } - this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens); + this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens, false); } public setTokens(tokens: MultilineTokens[]): void { @@ -1724,24 +1827,61 @@ export class TextModel extends Disposable implements model.ITextModel { for (let i = 0, len = tokens.length; i < len; i++) { const element = tokens[i]; - ranges.push({ fromLineNumber: element.startLineNumber, toLineNumber: element.startLineNumber + element.tokens.length - 1 }); + let minChangedLineNumber = 0; + let maxChangedLineNumber = 0; + let hasChange = false; for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) { - this.setLineTokens(element.startLineNumber + j, element.tokens[j]); + const lineNumber = element.startLineNumber + j; + if (hasChange) { + this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], false); + maxChangedLineNumber = lineNumber; + } else { + const lineHasChange = this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], true); + if (lineHasChange) { + hasChange = true; + minChangedLineNumber = lineNumber; + maxChangedLineNumber = lineNumber; + } + } + } + if (hasChange) { + ranges.push({ fromLineNumber: minChangedLineNumber, toLineNumber: maxChangedLineNumber }); } } + if (ranges.length > 0) { + this._emitModelTokensChangedEvent({ + tokenizationSupportChanged: false, + semanticTokensApplied: false, + ranges: ranges + }); + } + } + + public setSemanticTokens(tokens: MultilineTokens2[] | null, isComplete: boolean): void { + this._tokens2.set(tokens, isComplete); + this._emitModelTokensChangedEvent({ tokenizationSupportChanged: false, - ranges: ranges + semanticTokensApplied: tokens !== null, + ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }] }); } - public setSemanticTokens(tokens: MultilineTokens2[] | null): void { - this._tokens2.set(tokens); + public hasSemanticTokens(): boolean { + return this._tokens2.isComplete(); + } + + public setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[]): void { + if (this.hasSemanticTokens()) { + return; + } + const changedRange = this._tokens2.setPartial(range, tokens); this._emitModelTokensChangedEvent({ tokenizationSupportChanged: false, - ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }] + semanticTokensApplied: true, + ranges: [{ fromLineNumber: changedRange.startLineNumber, toLineNumber: changedRange.endLineNumber }] }); } @@ -1755,6 +1895,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._tokens.flush(); this._emitModelTokensChangedEvent({ tokenizationSupportChanged: true, + semanticTokensApplied: false, ranges: [{ fromLineNumber: 1, toLineNumber: this._buffer.getLineCount() @@ -1767,6 +1908,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._emitModelTokensChangedEvent({ tokenizationSupportChanged: false, + semanticTokensApplied: false, ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }] }); } @@ -1937,7 +2079,7 @@ export class TextModel extends Disposable implements model.ITextModel { return null; } - return this._findMatchingBracketUp(data, position); + return stripBracketSearchCanceled(this._findMatchingBracketUp(data, position, null)); } public matchBracket(position: IPosition): [Range, Range] | null { @@ -1985,8 +2127,11 @@ export class TextModel extends Disposable implements model.ITextModel { // check that we didn't hit a bracket too far away from position if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase(); - const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]); + const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText], null); if (r) { + if (r instanceof BracketSearchCanceled) { + return null; + } bestResult = r; } } @@ -2023,8 +2168,11 @@ export class TextModel extends Disposable implements model.ITextModel { // check that we didn't hit a bracket too far away from position if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) { const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase(); - const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]); + const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText], null); if (r) { + if (r instanceof BracketSearchCanceled) { + return null; + } return r; } } @@ -2034,35 +2182,41 @@ export class TextModel extends Disposable implements model.ITextModel { return null; } - private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] | null { + private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean, continueSearchPredicate: ContinueBracketSearchPredicate): [Range, Range] | null | BracketSearchCanceled { if (!data) { return null; } - if (isOpen) { - let matched = this._findMatchingBracketDown(data, foundBracket.getEndPosition()); - if (matched) { - return [foundBracket, matched]; - } - } else { - let matched = this._findMatchingBracketUp(data, foundBracket.getStartPosition()); - if (matched) { - return [foundBracket, matched]; - } + const matched = ( + isOpen + ? this._findMatchingBracketDown(data, foundBracket.getEndPosition(), continueSearchPredicate) + : this._findMatchingBracketUp(data, foundBracket.getStartPosition(), continueSearchPredicate) + ); + + if (!matched) { + return null; } - return null; + if (matched instanceof BracketSearchCanceled) { + return matched; + } + + return [foundBracket, matched]; } - private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range | null { + private _findMatchingBracketUp(bracket: RichEditBracket, position: Position, continueSearchPredicate: ContinueBracketSearchPredicate): Range | null | BracketSearchCanceled { // console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); const languageId = bracket.languageIdentifier.id; const reversedBracketRegex = bracket.reversedRegex; let count = -1; - const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => { + let totalCallCount = 0; + const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null | BracketSearchCanceled => { while (true) { + if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) { + return BracketSearchCanceled.INSTANCE; + } const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); if (!r) { break; @@ -2137,15 +2291,19 @@ export class TextModel extends Disposable implements model.ITextModel { return null; } - private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range | null { + private _findMatchingBracketDown(bracket: RichEditBracket, position: Position, continueSearchPredicate: ContinueBracketSearchPredicate): Range | null | BracketSearchCanceled { // console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position)); const languageId = bracket.languageIdentifier.id; const bracketRegex = bracket.forwardRegex; let count = 1; - const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => { + let totalCallCount = 0; + const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null | BracketSearchCanceled => { while (true) { + if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) { + return BracketSearchCanceled.INSTANCE; + } const r = BracketsUtils.findNextBracketInRange(bracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); if (!r) { break; @@ -2375,7 +2533,16 @@ export class TextModel extends Disposable implements model.ITextModel { return null; } - public findEnclosingBrackets(_position: IPosition, maxDuration = Constants.MAX_SAFE_SMALL_INTEGER): [Range, Range] | null { + public findEnclosingBrackets(_position: IPosition, maxDuration?: number): [Range, Range] | null { + let continueSearchPredicate: ContinueBracketSearchPredicate; + if (typeof maxDuration === 'undefined') { + continueSearchPredicate = null; + } else { + const startTime = Date.now(); + continueSearchPredicate = () => { + return (Date.now() - startTime <= maxDuration); + }; + } const position = this.validatePosition(_position); const lineCount = this.getLineCount(); const savedCounts = new Map(); @@ -2391,8 +2558,13 @@ export class TextModel extends Disposable implements model.ITextModel { } counts = savedCounts.get(languageId)!; }; - const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null => { + + let totalCallCount = 0; + const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null | BracketSearchCanceled => { while (true) { + if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) { + return BracketSearchCanceled.INSTANCE; + } const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); if (!r) { break; @@ -2408,7 +2580,7 @@ export class TextModel extends Disposable implements model.ITextModel { } if (counts[bracket.index] === -1) { - return this._matchFoundBracket(r, bracket, false); + return this._matchFoundBracket(r, bracket, false, continueSearchPredicate); } } @@ -2419,12 +2591,7 @@ export class TextModel extends Disposable implements model.ITextModel { let languageId: LanguageId = -1; let modeBrackets: RichEditBrackets | null = null; - const startTime = Date.now(); for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) { - const elapsedTime = Date.now() - startTime; - if (elapsedTime > maxDuration) { - return null; - } const lineTokens = this._getLineTokens(lineNumber); const tokenCount = lineTokens.getCount(); const lineText = this._buffer.getLineContent(lineNumber); @@ -2453,7 +2620,7 @@ export class TextModel extends Disposable implements model.ITextModel { if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) { const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset); if (r) { - return r; + return stripBracketSearchCanceled(r); } prevSearchInToken = false; } @@ -2478,7 +2645,7 @@ export class TextModel extends Disposable implements model.ITextModel { if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) { const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset); if (r) { - return r; + return stripBracketSearchCanceled(r); } } } @@ -2489,7 +2656,7 @@ export class TextModel extends Disposable implements model.ITextModel { if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) { const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset); if (r) { - return r; + return stripBracketSearchCanceled(r); } } } @@ -2640,14 +2807,16 @@ export class TextModel extends Disposable implements model.ITextModel { let goDown = true; let indent = 0; + let initialIndent = 0; + for (let distance = 0; goUp || goDown; distance++) { const upLineNumber = lineNumber - distance; const downLineNumber = lineNumber + distance; - if (distance !== 0 && (upLineNumber < 1 || upLineNumber < minLineNumber)) { + if (distance > 1 && (upLineNumber < 1 || upLineNumber < minLineNumber)) { goUp = false; } - if (distance !== 0 && (downLineNumber > lineCount || downLineNumber > maxLineNumber)) { + if (distance > 1 && (downLineNumber > lineCount || downLineNumber > maxLineNumber)) { goDown = false; } if (distance > 50000) { @@ -2656,10 +2825,9 @@ export class TextModel extends Disposable implements model.ITextModel { goDown = false; } + let upLineIndentLevel: number = -1; if (goUp) { // compute indent level going up - let upLineIndentLevel: number; - const currentIndent = this._computeIndentLevel(upLineNumber - 1); if (currentIndent >= 0) { // This line has content (besides whitespace) @@ -2671,30 +2839,11 @@ export class TextModel extends Disposable implements model.ITextModel { up_resolveIndents(upLineNumber); upLineIndentLevel = this._getIndentLevelForWhitespaceLine(offSide, up_aboveContentLineIndent, up_belowContentLineIndent); } - - if (distance === 0) { - // This is the initial line number - startLineNumber = upLineNumber; - endLineNumber = downLineNumber; - indent = upLineIndentLevel; - if (indent === 0) { - // No need to continue - return { startLineNumber, endLineNumber, indent }; - } - continue; - } - - if (upLineIndentLevel >= indent) { - startLineNumber = upLineNumber; - } else { - goUp = false; - } } + let downLineIndentLevel = -1; if (goDown) { // compute indent level going down - let downLineIndentLevel: number; - const currentIndent = this._computeIndentLevel(downLineNumber - 1); if (currentIndent >= 0) { // This line has content (besides whitespace) @@ -2706,7 +2855,50 @@ export class TextModel extends Disposable implements model.ITextModel { down_resolveIndents(downLineNumber); downLineIndentLevel = this._getIndentLevelForWhitespaceLine(offSide, down_aboveContentLineIndent, down_belowContentLineIndent); } + } + if (distance === 0) { + initialIndent = upLineIndentLevel; + continue; + } + + if (distance === 1) { + if (downLineNumber <= lineCount && downLineIndentLevel >= 0 && initialIndent + 1 === downLineIndentLevel) { + // This is the beginning of a scope, we have special handling here, since we want the + // child scope indent to be active, not the parent scope + goUp = false; + startLineNumber = downLineNumber; + endLineNumber = downLineNumber; + indent = downLineIndentLevel; + continue; + } + + if (upLineNumber >= 1 && upLineIndentLevel >= 0 && upLineIndentLevel - 1 === initialIndent) { + // This is the end of a scope, just like above + goDown = false; + startLineNumber = upLineNumber; + endLineNumber = upLineNumber; + indent = upLineIndentLevel; + continue; + } + + startLineNumber = lineNumber; + endLineNumber = lineNumber; + indent = initialIndent; + if (indent === 0) { + // No need to continue + return { startLineNumber, endLineNumber, indent }; + } + } + + if (goUp) { + if (upLineIndentLevel >= indent) { + startLineNumber = upLineNumber; + } else { + goUp = false; + } + } + if (goDown) { if (downLineIndentLevel >= indent) { endLineNumber = downLineNumber; } else { @@ -2920,7 +3112,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions { this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center); } - public getColor(theme: ITheme): string { + public getColor(theme: EditorTheme): string { if (!this._resolvedColor) { if (theme.type !== 'light' && this.darkColor) { this._resolvedColor = this._resolveColor(this.darkColor, theme); @@ -2935,7 +3127,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions { this._resolvedColor = null; } - private _resolveColor(color: string | ThemeColor, theme: ITheme): string { + private _resolveColor(color: string | ThemeColor, theme: EditorTheme): string { if (typeof color === 'string') { return color; } @@ -2957,7 +3149,7 @@ export class ModelDecorationMinimapOptions extends DecorationOptions { this.position = options.position; } - public getColor(theme: ITheme): Color | undefined { + public getColor(theme: EditorTheme): Color | undefined { if (!this._resolvedColor) { if (theme.type !== 'light' && this.darkColor) { this._resolvedColor = this._resolveColor(this.darkColor, theme); @@ -2973,7 +3165,7 @@ export class ModelDecorationMinimapOptions extends DecorationOptions { this._resolvedColor = undefined; } - private _resolveColor(color: string | ThemeColor, theme: ITheme): Color | undefined { + private _resolveColor(color: string | ThemeColor, theme: EditorTheme): Color | undefined { if (typeof color === 'string') { return Color.fromHex(color); } @@ -3005,6 +3197,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { readonly minimap: ModelDecorationMinimapOptions | null; readonly glyphMarginClassName: string | null; readonly linesDecorationsClassName: string | null; + readonly firstLineDecorationClassName: string | null; readonly marginClassName: string | null; readonly inlineClassName: string | null; readonly inlineClassNameAffectsLetterSpacing: boolean; @@ -3015,8 +3208,8 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; this.zIndex = options.zIndex || 0; this.className = options.className ? cleanClassName(options.className) : null; - this.hoverMessage = withUndefinedAsNull(options.hoverMessage); - this.glyphMarginHoverMessage = withUndefinedAsNull(options.glyphMarginHoverMessage); + this.hoverMessage = options.hoverMessage || null; + this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null; this.isWholeLine = options.isWholeLine || false; this.showIfCollapsed = options.showIfCollapsed || false; this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false; @@ -3024,6 +3217,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { this.minimap = options.minimap ? new ModelDecorationMinimapOptions(options.minimap) : null; this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null; this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null; + this.firstLineDecorationClassName = options.firstLineDecorationClassName ? cleanClassName(options.firstLineDecorationClassName) : null; this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : null; this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : null; this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false; @@ -3057,11 +3251,15 @@ export class DidChangeDecorationsEmitter extends Disposable { private _deferredCnt: number; private _shouldFire: boolean; + private _affectsMinimap: boolean; + private _affectsOverviewRuler: boolean; constructor() { super(); this._deferredCnt = 0; this._shouldFire = false; + this._affectsMinimap = false; + this._affectsOverviewRuler = false; } public beginDeferredEmit(): void { @@ -3072,13 +3270,31 @@ export class DidChangeDecorationsEmitter extends Disposable { this._deferredCnt--; if (this._deferredCnt === 0) { if (this._shouldFire) { + const event: IModelDecorationsChangedEvent = { + affectsMinimap: this._affectsMinimap, + affectsOverviewRuler: this._affectsOverviewRuler, + }; this._shouldFire = false; - this._actual.fire({}); + this._affectsMinimap = false; + this._affectsOverviewRuler = false; + this._actual.fire(event); } } } + public checkAffectedAndFire(options: ModelDecorationOptions): void { + if (!this._affectsMinimap) { + this._affectsMinimap = options.minimap && options.minimap.position ? true : false; + } + if (!this._affectsOverviewRuler) { + this._affectsOverviewRuler = options.overviewRuler && options.overviewRuler.color ? true : false; + } + this._shouldFire = true; + } + public fire(): void { + this._affectsMinimap = true; + this._affectsOverviewRuler = true; this._shouldFire = true; } } @@ -3108,10 +3324,11 @@ export class DidChangeContentEmitter extends Disposable { this._deferredCnt++; } - public endDeferredEmit(): void { + public endDeferredEmit(resultingSelection: Selection[] | null = null): void { this._deferredCnt--; if (this._deferredCnt === 0) { if (this._deferredEvent !== null) { + this._deferredEvent.rawContentChangedEvent.resultingSelection = resultingSelection; const e = this._deferredEvent; this._deferredEvent = null; this._fastEmitter.fire(e); diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts index fc84cb84f93..6511d02173f 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IRange } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; /** * An event describing that the current mode associated with a model has changed. @@ -76,13 +77,17 @@ export interface IModelContentChangedEvent { * An event describing that model decorations have changed. */ export interface IModelDecorationsChangedEvent { + readonly affectsMinimap: boolean; + readonly affectsOverviewRuler: boolean; } /** * An event describing that some ranges of lines have been tokenized (their tokens have changed). + * @internal */ export interface IModelTokensChangedEvent { readonly tokenizationSupportChanged: boolean; + readonly semanticTokensApplied: boolean; readonly ranges: { /** * The start of the range (inclusive) @@ -222,11 +227,14 @@ export class ModelRawContentChangedEvent { */ public readonly isRedoing: boolean; + public resultingSelection: Selection[] | null; + constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) { this.changes = changes; this.versionId = versionId; this.isUndoing = isUndoing; this.isRedoing = isRedoing; + this.resultingSelection = null; } public containsEvent(type: RawContentChangedType): boolean { diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index d41d1a2a1ba..8ebfdd22216 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -515,7 +515,7 @@ export class Searcher { private _prevMatchStartIndex: number; private _prevMatchLength: number; - constructor(wordSeparators: WordCharacterClassifier | null, searchRegex: RegExp, ) { + constructor(wordSeparators: WordCharacterClassifier | null, searchRegex: RegExp,) { this._wordSeparators = wordSeparators; this._searchRegex = searchRegex; this._prevMatchStartIndex = -1; @@ -548,8 +548,12 @@ export class Searcher { if (matchStartIndex === this._prevMatchStartIndex && matchLength === this._prevMatchLength) { if (matchLength === 0) { // the search result is an empty string and won't advance `regex.lastIndex`, so `regex.exec` will stuck here - // we attempt to recover from that by advancing by one - this._searchRegex.lastIndex += 1; + // we attempt to recover from that by advancing by two if surrogate pair found and by one otherwise + if (strings.getNextCodePoint(text, textLength, this._searchRegex.lastIndex) > 0xFFFF) { + this._searchRegex.lastIndex += 2; + } else { + this._searchRegex.lastIndex += 1; + } continue; } // Exit early if the regex matches the same range twice diff --git a/src/vs/editor/common/model/tokensStore.ts b/src/vs/editor/common/model/tokensStore.ts index d77bb8b7de6..a49ef27a71e 100644 --- a/src/vs/editor/common/model/tokensStore.ts +++ b/src/vs/editor/common/model/tokensStore.ts @@ -6,15 +6,23 @@ import * as arrays from 'vs/base/common/arrays'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; -import { IRange } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes'; import { writeUInt32BE, readUInt32BE } from 'vs/base/common/buffer'; import { CharCode } from 'vs/base/common/charCode'; -export function countEOL(text: string): [number, number, number] { +export const enum StringEOL { + Unknown = 0, + Invalid = 3, + LF = 1, + CRLF = 2 +} + +export function countEOL(text: string): [number, number, number, StringEOL] { let eolCount = 0; let firstLineLength = 0; let lastLineStart = 0; + let eol: StringEOL = StringEOL.Unknown; for (let i = 0, len = text.length; i < len; i++) { const chr = text.charCodeAt(i); @@ -25,12 +33,16 @@ export function countEOL(text: string): [number, number, number] { eolCount++; if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) { // \r\n... case + eol |= StringEOL.CRLF; i++; // skip \n } else { // \r... case + eol |= StringEOL.Invalid; } lastLineStart = i + 1; } else if (chr === CharCode.LineFeed) { + // \n... case + eol |= StringEOL.LF; if (eolCount === 0) { firstLineLength = i; } @@ -41,7 +53,7 @@ export function countEOL(text: string): [number, number, number] { if (eolCount === 0) { firstLineLength = text.length; } - return [eolCount, firstLineLength, text.length - lastLineStart]; + return [eolCount, firstLineLength, text.length - lastLineStart, eol]; } function getDefaultMetadata(topLevelLanguageId: LanguageId): number { @@ -112,20 +124,7 @@ export class MultilineTokensBuilder { } } -export interface IEncodedTokens { - getTokenCount(): number; - getDeltaLine(tokenIndex: number): number; - getMaxDeltaLine(): number; - getStartCharacter(tokenIndex: number): number; - getEndCharacter(tokenIndex: number): number; - getMetadata(tokenIndex: number): number; - - clear(): void; - acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void; - acceptInsertText(deltaLine: number, character: number, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void; -} - -export class SparseEncodedTokens implements IEncodedTokens { +export class SparseEncodedTokens { /** * The encoding of tokens is: * 4*i deltaLine (from `startLineNumber`) @@ -133,7 +132,7 @@ export class SparseEncodedTokens implements IEncodedTokens { * 4*i+2 endCharacter (from the line start) * 4*i+3 metadata */ - private _tokens: Uint32Array; + private readonly _tokens: Uint32Array; private _tokenCount: number; constructor(tokens: Uint32Array) { @@ -141,38 +140,167 @@ export class SparseEncodedTokens implements IEncodedTokens { this._tokenCount = tokens.length / 4; } + public toString(startLineNumber: number): string { + let pieces: string[] = []; + for (let i = 0; i < this._tokenCount; i++) { + pieces.push(`(${this._getDeltaLine(i) + startLineNumber},${this._getStartCharacter(i)}-${this._getEndCharacter(i)})`); + } + return `[${pieces.join(',')}]`; + } + public getMaxDeltaLine(): number { - const tokenCount = this.getTokenCount(); + const tokenCount = this._getTokenCount(); if (tokenCount === 0) { return -1; } - return this.getDeltaLine(tokenCount - 1); + return this._getDeltaLine(tokenCount - 1); } - public getTokenCount(): number { + public getRange(): Range | null { + const tokenCount = this._getTokenCount(); + if (tokenCount === 0) { + return null; + } + const startChar = this._getStartCharacter(0); + const maxDeltaLine = this._getDeltaLine(tokenCount - 1); + const endChar = this._getEndCharacter(tokenCount - 1); + return new Range(0, startChar + 1, maxDeltaLine, endChar + 1); + } + + private _getTokenCount(): number { return this._tokenCount; } - public getDeltaLine(tokenIndex: number): number { + private _getDeltaLine(tokenIndex: number): number { return this._tokens[4 * tokenIndex]; } - public getStartCharacter(tokenIndex: number): number { + private _getStartCharacter(tokenIndex: number): number { return this._tokens[4 * tokenIndex + 1]; } - public getEndCharacter(tokenIndex: number): number { + private _getEndCharacter(tokenIndex: number): number { return this._tokens[4 * tokenIndex + 2]; } - public getMetadata(tokenIndex: number): number { - return this._tokens[4 * tokenIndex + 3]; + public isEmpty(): boolean { + return (this._getTokenCount() === 0); + } + + public getLineTokens(deltaLine: number): LineTokens2 | null { + let low = 0; + let high = this._getTokenCount() - 1; + + while (low < high) { + const mid = low + Math.floor((high - low) / 2); + const midDeltaLine = this._getDeltaLine(mid); + + if (midDeltaLine < deltaLine) { + low = mid + 1; + } else if (midDeltaLine > deltaLine) { + high = mid - 1; + } else { + let min = mid; + while (min > low && this._getDeltaLine(min - 1) === deltaLine) { + min--; + } + let max = mid; + while (max < high && this._getDeltaLine(max + 1) === deltaLine) { + max++; + } + return new LineTokens2(this._tokens.subarray(4 * min, 4 * max + 4)); + } + } + + if (this._getDeltaLine(low) === deltaLine) { + return new LineTokens2(this._tokens.subarray(4 * low, 4 * low + 4)); + } + + return null; } public clear(): void { this._tokenCount = 0; } + public removeTokens(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): number { + const tokens = this._tokens; + const tokenCount = this._tokenCount; + let newTokenCount = 0; + let hasDeletedTokens = false; + let firstDeltaLine = 0; + for (let i = 0; i < tokenCount; i++) { + const srcOffset = 4 * i; + const tokenDeltaLine = tokens[srcOffset]; + const tokenStartCharacter = tokens[srcOffset + 1]; + const tokenEndCharacter = tokens[srcOffset + 2]; + const tokenMetadata = tokens[srcOffset + 3]; + + if ( + (tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar)) + && (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar)) + ) { + hasDeletedTokens = true; + } else { + if (newTokenCount === 0) { + firstDeltaLine = tokenDeltaLine; + } + if (hasDeletedTokens) { + // must move the token to the left + const destOffset = 4 * newTokenCount; + tokens[destOffset] = tokenDeltaLine - firstDeltaLine; + tokens[destOffset + 1] = tokenStartCharacter; + tokens[destOffset + 2] = tokenEndCharacter; + tokens[destOffset + 3] = tokenMetadata; + } + newTokenCount++; + } + } + + this._tokenCount = newTokenCount; + + return firstDeltaLine; + } + + public split(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): [SparseEncodedTokens, SparseEncodedTokens, number] { + const tokens = this._tokens; + const tokenCount = this._tokenCount; + let aTokens: number[] = []; + let bTokens: number[] = []; + let destTokens: number[] = aTokens; + let destOffset = 0; + let destFirstDeltaLine: number = 0; + for (let i = 0; i < tokenCount; i++) { + const srcOffset = 4 * i; + const tokenDeltaLine = tokens[srcOffset]; + const tokenStartCharacter = tokens[srcOffset + 1]; + const tokenEndCharacter = tokens[srcOffset + 2]; + const tokenMetadata = tokens[srcOffset + 3]; + + if ((tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar))) { + if ((tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar))) { + // this token is touching the range + continue; + } else { + // this token is after the range + if (destTokens !== bTokens) { + // this token is the first token after the range + destTokens = bTokens; + destOffset = 0; + destFirstDeltaLine = tokenDeltaLine; + } + } + } + + destTokens[destOffset++] = tokenDeltaLine - destFirstDeltaLine; + destTokens[destOffset++] = tokenStartCharacter; + destTokens[destOffset++] = tokenEndCharacter; + destTokens[destOffset++] = tokenMetadata; + } + + return [new SparseEncodedTokens(new Uint32Array(aTokens)), new SparseEncodedTokens(new Uint32Array(bTokens)), destFirstDeltaLine]; + } + public acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void { // This is a bit complex, here are the cases I used to think about this: // @@ -402,30 +530,26 @@ export class SparseEncodedTokens implements IEncodedTokens { export class LineTokens2 { - private readonly _actual: IEncodedTokens; - private readonly _startTokenIndex: number; - private readonly _endTokenIndex: number; + private readonly _tokens: Uint32Array; - constructor(actual: IEncodedTokens, startTokenIndex: number, endTokenIndex: number) { - this._actual = actual; - this._startTokenIndex = startTokenIndex; - this._endTokenIndex = endTokenIndex; + constructor(tokens: Uint32Array) { + this._tokens = tokens; } public getCount(): number { - return this._endTokenIndex - this._startTokenIndex + 1; + return this._tokens.length / 4; } public getStartCharacter(tokenIndex: number): number { - return this._actual.getStartCharacter(this._startTokenIndex + tokenIndex); + return this._tokens[4 * tokenIndex + 1]; } public getEndCharacter(tokenIndex: number): number { - return this._actual.getEndCharacter(this._startTokenIndex + tokenIndex); + return this._tokens[4 * tokenIndex + 2]; } public getMetadata(tokenIndex: number): number { - return this._actual.getMetadata(this._startTokenIndex + tokenIndex); + return this._tokens[4 * tokenIndex + 3]; } } @@ -433,59 +557,58 @@ export class MultilineTokens2 { public startLineNumber: number; public endLineNumber: number; - public tokens: IEncodedTokens; + public tokens: SparseEncodedTokens; - constructor(startLineNumber: number, tokens: IEncodedTokens) { + constructor(startLineNumber: number, tokens: SparseEncodedTokens) { this.startLineNumber = startLineNumber; this.tokens = tokens; this.endLineNumber = this.startLineNumber + this.tokens.getMaxDeltaLine(); } + public toString(): string { + return this.tokens.toString(this.startLineNumber); + } + private _updateEndLineNumber(): void { this.endLineNumber = this.startLineNumber + this.tokens.getMaxDeltaLine(); } + public isEmpty(): boolean { + return this.tokens.isEmpty(); + } + public getLineTokens(lineNumber: number): LineTokens2 | null { if (this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber) { - const findResult = MultilineTokens2._findTokensWithLine(this.tokens, lineNumber - this.startLineNumber); - if (findResult) { - const [startTokenIndex, endTokenIndex] = findResult; - return new LineTokens2(this.tokens, startTokenIndex, endTokenIndex); - } + return this.tokens.getLineTokens(lineNumber - this.startLineNumber); } return null; } - private static _findTokensWithLine(tokens: IEncodedTokens, deltaLine: number): [number, number] | null { - let low = 0; - let high = tokens.getTokenCount() - 1; - - while (low < high) { - const mid = low + Math.floor((high - low) / 2); - const midDeltaLine = tokens.getDeltaLine(mid); - - if (midDeltaLine < deltaLine) { - low = mid + 1; - } else if (midDeltaLine > deltaLine) { - high = mid - 1; - } else { - let min = mid; - while (min > low && tokens.getDeltaLine(min - 1) === deltaLine) { - min--; - } - let max = mid; - while (max < high && tokens.getDeltaLine(max + 1) === deltaLine) { - max++; - } - return [min, max]; - } + public getRange(): Range | null { + const deltaRange = this.tokens.getRange(); + if (!deltaRange) { + return deltaRange; } + return new Range(this.startLineNumber + deltaRange.startLineNumber, deltaRange.startColumn, this.startLineNumber + deltaRange.endLineNumber, deltaRange.endColumn); + } - if (tokens.getDeltaLine(low) === deltaLine) { - return [low, low]; - } + public removeTokens(range: Range): void { + const startLineIndex = range.startLineNumber - this.startLineNumber; + const endLineIndex = range.endLineNumber - this.startLineNumber; - return null; + this.startLineNumber += this.tokens.removeTokens(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1); + this._updateEndLineNumber(); + } + + public split(range: Range): [MultilineTokens2, MultilineTokens2] { + // split tokens to two: + // a) all the tokens before `range` + // b) all the tokens after `range` + const startLineIndex = range.startLineNumber - this.startLineNumber; + const endLineIndex = range.endLineNumber - this.startLineNumber; + + const [a, b, bDeltaLine] = this.tokens.split(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1); + return [new MultilineTokens2(this.startLineNumber, a), new MultilineTokens2(this.startLineNumber + bDeltaLine, b)]; } public applyEdit(range: IRange, text: string): void { @@ -749,17 +872,105 @@ function toUint32Array(arr: Uint32Array | ArrayBuffer): Uint32Array { export class TokensStore2 { private _pieces: MultilineTokens2[]; + private _isComplete: boolean; constructor() { this._pieces = []; + this._isComplete = false; } public flush(): void { this._pieces = []; + this._isComplete = false; } - public set(pieces: MultilineTokens2[] | null) { + public set(pieces: MultilineTokens2[] | null, isComplete: boolean): void { this._pieces = pieces || []; + this._isComplete = isComplete; + } + + public setPartial(_range: Range, pieces: MultilineTokens2[]): Range { + // console.log(`setPartial ${_range} ${pieces.map(p => p.toString()).join(', ')}`); + + let range = _range; + if (pieces.length > 0) { + const _firstRange = pieces[0].getRange(); + const _lastRange = pieces[pieces.length - 1].getRange(); + if (!_firstRange || !_lastRange) { + return _range; + } + range = _range.plusRange(_firstRange).plusRange(_lastRange); + } + + let insertPosition: { index: number; } | null = null; + for (let i = 0, len = this._pieces.length; i < len; i++) { + const piece = this._pieces[i]; + if (piece.endLineNumber < range.startLineNumber) { + // this piece is before the range + continue; + } + + if (piece.startLineNumber > range.endLineNumber) { + // this piece is after the range, so mark the spot before this piece + // as a good insertion position and stop looping + insertPosition = insertPosition || { index: i }; + break; + } + + // this piece might intersect with the range + piece.removeTokens(range); + + if (piece.isEmpty()) { + // remove the piece if it became empty + this._pieces.splice(i, 1); + i--; + len--; + continue; + } + + if (piece.endLineNumber < range.startLineNumber) { + // after removal, this piece is before the range + continue; + } + + if (piece.startLineNumber > range.endLineNumber) { + // after removal, this piece is after the range + insertPosition = insertPosition || { index: i }; + continue; + } + + // after removal, this piece contains the range + const [a, b] = piece.split(range); + if (a.isEmpty()) { + // this piece is actually after the range + insertPosition = insertPosition || { index: i }; + continue; + } + if (b.isEmpty()) { + // this piece is actually before the range + continue; + } + this._pieces.splice(i, 1, a, b); + i++; + len++; + + insertPosition = insertPosition || { index: i }; + } + + insertPosition = insertPosition || { index: this._pieces.length }; + + if (pieces.length > 0) { + this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces); + } + + // console.log(`I HAVE ${this._pieces.length} pieces`); + // console.log(`${this._pieces.map(p => p.toString()).join('\n')}`); + + return range; + } + + public isComplete(): boolean { + return this._isComplete; } public addSemanticTokens(lineNumber: number, aTokens: LineTokens): LineTokens { @@ -770,7 +981,7 @@ export class TokensStore2 { } const pieceIndex = TokensStore2._findFirstPieceWithLine(pieces, lineNumber); - const bTokens = this._pieces[pieceIndex].getLineTokens(lineNumber); + const bTokens = pieces[pieceIndex].getLineTokens(lineNumber); if (!bTokens) { return aTokens; @@ -781,46 +992,65 @@ export class TokensStore2 { let aIndex = 0; let result: number[] = [], resultLen = 0; + let lastEndOffset = 0; + + const emitToken = (endOffset: number, metadata: number) => { + if (endOffset === lastEndOffset) { + return; + } + lastEndOffset = endOffset; + result[resultLen++] = endOffset; + result[resultLen++] = metadata; + }; + for (let bIndex = 0; bIndex < bLen; bIndex++) { const bStartCharacter = bTokens.getStartCharacter(bIndex); const bEndCharacter = bTokens.getEndCharacter(bIndex); const bMetadata = bTokens.getMetadata(bIndex); + const bMask = ( + ((bMetadata & MetadataConsts.SEMANTIC_USE_ITALIC) ? MetadataConsts.ITALIC_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_BOLD) ? MetadataConsts.BOLD_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_UNDERLINE) ? MetadataConsts.UNDERLINE_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_FOREGROUND) ? MetadataConsts.FOREGROUND_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_BACKGROUND) ? MetadataConsts.BACKGROUND_MASK : 0) + ) >>> 0; + const aMask = (~bMask) >>> 0; + // push any token from `a` that is before `b` while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bStartCharacter) { - result[resultLen++] = aTokens.getEndOffset(aIndex); - result[resultLen++] = aTokens.getMetadata(aIndex); + emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); aIndex++; } // push the token from `a` if it intersects the token from `b` if (aIndex < aLen && aTokens.getStartOffset(aIndex) < bStartCharacter) { - result[resultLen++] = bStartCharacter; - result[resultLen++] = aTokens.getMetadata(aIndex); + emitToken(bStartCharacter, aTokens.getMetadata(aIndex)); } // skip any tokens from `a` that are contained inside `b` - while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bEndCharacter) { + while (aIndex < aLen && aTokens.getEndOffset(aIndex) < bEndCharacter) { + emitToken(aTokens.getEndOffset(aIndex), (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); aIndex++; } - const aMetadata = aTokens.getMetadata(Math.min(Math.max(0, aIndex - 1), aLen - 1)); - const languageId = TokenMetadata.getLanguageId(aMetadata); - const tokenType = TokenMetadata.getTokenType(aMetadata); + if (aIndex < aLen) { + emitToken(bEndCharacter, (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); + if (aTokens.getEndOffset(aIndex) === bEndCharacter) { + // `a` ends exactly at the same spot as `b`! + aIndex++; + } + } else { + const aMergeIndex = Math.min(Math.max(0, aIndex - 1), aLen - 1); - // push the token from `b` - result[resultLen++] = bEndCharacter; - result[resultLen++] = ( - (bMetadata & MetadataConsts.LANG_TTYPE_CMPL) - | ((languageId << MetadataConsts.LANGUAGEID_OFFSET) >>> 0) - | ((tokenType << MetadataConsts.TOKEN_TYPE_OFFSET) >>> 0) - ); + // push the token from `b` + emitToken(bEndCharacter, (aTokens.getMetadata(aMergeIndex) & aMask) | (bMetadata & bMask)); + } } // push the remaining tokens from `a` while (aIndex < aLen) { - result[resultLen++] = aTokens.getEndOffset(aIndex); - result[resultLen++] = aTokens.getMetadata(aIndex); + emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); aIndex++; } @@ -952,10 +1182,35 @@ export class TokensStore { this._len += insertCount; } - public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null): void { + public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null, checkEquality: boolean): boolean { const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens); this._ensureLine(lineIndex); + const oldTokens = this._lineTokens[lineIndex]; this._lineTokens[lineIndex] = tokens; + + if (checkEquality) { + return !TokensStore._equals(oldTokens, tokens); + } + return false; + } + + private static _equals(_a: Uint32Array | ArrayBuffer | null, _b: Uint32Array | ArrayBuffer | null) { + if (!_a || !_b) { + return !_a && !_b; + } + + const a = toUint32Array(_a); + const b = toUint32Array(_b); + + if (a.length !== b.length) { + return false; + } + for (let i = 0, len = a.length; i < len; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; } //#region Editing diff --git a/src/vs/editor/common/model/wordHelper.ts b/src/vs/editor/common/model/wordHelper.ts index 3e849b0dddf..a1cae550a88 100644 --- a/src/vs/editor/common/model/wordHelper.ts +++ b/src/vs/editor/common/model/wordHelper.ts @@ -55,77 +55,81 @@ export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegEx return result; } -function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null { - // find whitespace enclosed text around column and match from there +const _defaultConfig = { + maxLen: 1000, + windowSize: 15, + timeBudget: 150 +}; - let pos = column - 1 - textOffset; - let start = text.lastIndexOf(' ', pos - 1) + 1; +export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config = _defaultConfig): IWordAtPosition | null { - wordDefinition.lastIndex = start; + if (text.length > config.maxLen) { + // don't throw strings that long at the regexp + // but use a sub-string in which a word must occur + let start = column - config.maxLen / 2; + if (start < 0) { + start = 0; + } else { + textOffset += start; + } + text = text.substring(start, column + config.maxLen / 2); + return getWordAtText(column, wordDefinition, text, textOffset, config); + } + + const t1 = Date.now(); + const pos = column - 1 - textOffset; + + let prevRegexIndex = -1; + let match: RegExpMatchArray | null = null; + + for (let i = 1; ; i++) { + // check time budget + if (Date.now() - t1 >= config.timeBudget) { + break; + } + + // reset the index at which the regexp should start matching, also know where it + // should stop so that subsequent search don't repeat previous searches + const regexIndex = pos - config.windowSize * i; + wordDefinition.lastIndex = Math.max(0, regexIndex); + const thisMatch = _findRegexMatchEnclosingPosition(wordDefinition, text, pos, prevRegexIndex); + + if (!thisMatch && match) { + // stop: we have something + break; + } + + match = thisMatch; + + // stop: searched at start + if (regexIndex <= 0) { + break; + } + prevRegexIndex = regexIndex; + } + + if (match) { + let result = { + word: match[0], + startColumn: textOffset + 1 + match.index!, + endColumn: textOffset + 1 + match.index! + match[0].length + }; + wordDefinition.lastIndex = 0; + return result; + } + + return null; +} + +function _findRegexMatchEnclosingPosition(wordDefinition: RegExp, text: string, pos: number, stopPos: number): RegExpMatchArray | null { let match: RegExpMatchArray | null; while (match = wordDefinition.exec(text)) { const matchIndex = match.index || 0; if (matchIndex <= pos && wordDefinition.lastIndex >= pos) { - return { - word: match[0], - startColumn: textOffset + 1 + matchIndex, - endColumn: textOffset + 1 + wordDefinition.lastIndex - }; - } - } - - return null; -} - - -function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null { - // matches all words starting at the beginning - // of the input until it finds a match that encloses - // the desired column. slow but correct - - let pos = column - 1 - textOffset; - wordDefinition.lastIndex = 0; - - let match: RegExpMatchArray | null; - while (match = wordDefinition.exec(text)) { - const matchIndex = match.index || 0; - if (matchIndex > pos) { - // |nW -> matched only after the pos + return match; + } else if (stopPos > 0 && matchIndex > stopPos) { return null; - - } else if (wordDefinition.lastIndex >= pos) { - // W|W -> match encloses pos - return { - word: match[0], - startColumn: textOffset + 1 + matchIndex, - endColumn: textOffset + 1 + wordDefinition.lastIndex - }; } } - return null; } - -export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null { - - // if `words` can contain whitespace character we have to use the slow variant - // otherwise we use the fast variant of finding a word - wordDefinition.lastIndex = 0; - let match = wordDefinition.exec(text); - if (!match) { - return null; - } - // todo@joh the `match` could already be the (first) word - const ret = match[0].indexOf(' ') >= 0 - // did match a word which contains a space character -> use slow word find - ? getWordAtPosSlow(column, wordDefinition, text, textOffset) - // sane word definition -> use fast word find - : getWordAtPosFast(column, wordDefinition, text, textOffset); - - // both (getWordAtPosFast and getWordAtPosSlow) leave the wordDefinition-RegExp - // in an undefined state and to not confuse other users of the wordDefinition - // we reset the lastIndex - wordDefinition.lastIndex = 0; - - return ret; -} diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 0f22fc63b9c..4cdbc83339e 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -8,7 +8,6 @@ import { Color } from 'vs/base/common/color'; import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { isObject } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -19,7 +18,8 @@ import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureR import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; - +import { iconRegistry, Codicon } from 'vs/base/common/codicons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; /** * Open ended enum at runtime * @internal @@ -125,7 +125,15 @@ export const enum MetadataConsts { FOREGROUND_MASK = 0b00000000011111111100000000000000, BACKGROUND_MASK = 0b11111111100000000000000000000000, - LANG_TTYPE_CMPL = 0b11111111111111111111100000000000, + ITALIC_MASK = 0b00000000000000000000100000000000, + BOLD_MASK = 0b00000000000000000001000000000000, + UNDERLINE_MASK = 0b00000000000000000010000000000000, + + SEMANTIC_USE_ITALIC = 0b00000000000000000000000000000001, + SEMANTIC_USE_BOLD = 0b00000000000000000000000000000010, + SEMANTIC_USE_UNDERLINE = 0b00000000000000000000000000000100, + SEMANTIC_USE_FOREGROUND = 0b00000000000000000000000000001000, + SEMANTIC_USE_BACKGROUND = 0b00000000000000000000000000010000, LANGUAGEID_OFFSET = 0, TOKEN_TYPE_OFFSET = 8, @@ -257,6 +265,36 @@ export interface HoverProvider { provideHover(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are + * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. + * @internal + */ +export interface EvaluatableExpression { + /** + * The range to which this expression applies. + */ + range: IRange; + /* + * This expression overrides the expression extracted from the range. + */ + expression?: string; +} + +/** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + * @internal + */ +export interface EvaluatableExpressionProvider { + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + export const enum CompletionItemKind { Method, Function, @@ -283,6 +321,8 @@ export const enum CompletionItemKind { Customcolor, Folder, TypeParameter, + User, + Issue, Snippet, // <- highest value (used for compare!) } @@ -291,35 +331,43 @@ export const enum CompletionItemKind { */ export const completionKindToCssClass = (function () { let data = Object.create(null); - data[CompletionItemKind.Method] = 'method'; - data[CompletionItemKind.Function] = 'function'; - data[CompletionItemKind.Constructor] = 'constructor'; - data[CompletionItemKind.Field] = 'field'; - data[CompletionItemKind.Variable] = 'variable'; - data[CompletionItemKind.Class] = 'class'; - data[CompletionItemKind.Struct] = 'struct'; - data[CompletionItemKind.Interface] = 'interface'; - data[CompletionItemKind.Module] = 'module'; - data[CompletionItemKind.Property] = 'property'; - data[CompletionItemKind.Event] = 'event'; - data[CompletionItemKind.Operator] = 'operator'; - data[CompletionItemKind.Unit] = 'unit'; - data[CompletionItemKind.Value] = 'value'; - data[CompletionItemKind.Constant] = 'constant'; - data[CompletionItemKind.Enum] = 'enum'; - data[CompletionItemKind.EnumMember] = 'enum-member'; - data[CompletionItemKind.Keyword] = 'keyword'; - data[CompletionItemKind.Snippet] = 'snippet'; - data[CompletionItemKind.Text] = 'text'; - data[CompletionItemKind.Color] = 'color'; - data[CompletionItemKind.File] = 'file'; - data[CompletionItemKind.Reference] = 'reference'; - data[CompletionItemKind.Customcolor] = 'customcolor'; - data[CompletionItemKind.Folder] = 'folder'; - data[CompletionItemKind.TypeParameter] = 'type-parameter'; + data[CompletionItemKind.Method] = 'symbol-method'; + data[CompletionItemKind.Function] = 'symbol-function'; + data[CompletionItemKind.Constructor] = 'symbol-constructor'; + data[CompletionItemKind.Field] = 'symbol-field'; + data[CompletionItemKind.Variable] = 'symbol-variable'; + data[CompletionItemKind.Class] = 'symbol-class'; + data[CompletionItemKind.Struct] = 'symbol-struct'; + data[CompletionItemKind.Interface] = 'symbol-interface'; + data[CompletionItemKind.Module] = 'symbol-module'; + data[CompletionItemKind.Property] = 'symbol-property'; + data[CompletionItemKind.Event] = 'symbol-event'; + data[CompletionItemKind.Operator] = 'symbol-operator'; + data[CompletionItemKind.Unit] = 'symbol-unit'; + data[CompletionItemKind.Value] = 'symbol-value'; + data[CompletionItemKind.Constant] = 'symbol-constant'; + data[CompletionItemKind.Enum] = 'symbol-enum'; + data[CompletionItemKind.EnumMember] = 'symbol-enum-member'; + data[CompletionItemKind.Keyword] = 'symbol-keyword'; + data[CompletionItemKind.Snippet] = 'symbol-snippet'; + data[CompletionItemKind.Text] = 'symbol-text'; + data[CompletionItemKind.Color] = 'symbol-color'; + data[CompletionItemKind.File] = 'symbol-file'; + data[CompletionItemKind.Reference] = 'symbol-reference'; + data[CompletionItemKind.Customcolor] = 'symbol-customcolor'; + data[CompletionItemKind.Folder] = 'symbol-folder'; + data[CompletionItemKind.TypeParameter] = 'symbol-type-parameter'; + data[CompletionItemKind.User] = 'account'; + data[CompletionItemKind.Issue] = 'issues'; - return function (kind: CompletionItemKind) { - return data[kind] || 'property'; + return function (kind: CompletionItemKind): string { + const name = data[kind]; + let codicon = name && iconRegistry.get(name); + if (!codicon) { + console.info('No codicon found for CompletionItemKind ' + kind); + codicon = Codicon.symbolProperty; + } + return codicon.classNames; }; })(); @@ -359,7 +407,8 @@ export let completionKindFromString: { data['folder'] = CompletionItemKind.Folder; data['type-parameter'] = CompletionItemKind.TypeParameter; data['typeParameter'] = CompletionItemKind.TypeParameter; - + data['account'] = CompletionItemKind.User; + data['issue'] = CompletionItemKind.Issue; return function (value: string, strict?: true) { let res = data[value]; if (typeof res === 'undefined' && !strict) { @@ -376,9 +425,9 @@ export interface CompletionItemLabel { name: string; /** - * The signature without the return type. Render after `name`. + * The parameters without the return type. Render after `name`. */ - signature?: string; + parameters?: string; /** * The fully qualified name, like package name or file path. Rendered after `signature`. @@ -458,7 +507,7 @@ export interface CompletionItem { preselect?: boolean; /** * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the [label](#CompletionItem.label) + * this completion. * is used. */ insertText: string; @@ -504,6 +553,11 @@ export interface CompletionList { suggestions: CompletionItem[]; incomplete?: boolean; dispose?(): void; + + /** + * @internal + */ + duration?: number; } /** @@ -560,7 +614,7 @@ export interface CompletionItemProvider { * * The editor will only resolve a completion item once. */ - resolveCompletionItem?(model: model.ITextModel, position: Position, item: CompletionItem, token: CancellationToken): ProviderResult; + resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult; } export interface CodeAction { @@ -599,15 +653,25 @@ export interface CodeActionList extends IDisposable { * @internal */ export interface CodeActionProvider { + + displayName?: string + /** * Provide commands for the given document and range. */ provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult; + /** + * Given a code action fill in the edit. Will only invoked when missing. + */ + resolveCodeAction?(codeAction: CodeAction, token: CancellationToken): ProviderResult; + /** * Optional list of CodeActionKinds that this provider returns. */ - providedCodeActionKinds?: ReadonlyArray; + readonly providedCodeActionKinds?: ReadonlyArray; + + readonly documentation?: ReadonlyArray<{ readonly kind: string, readonly command: Command }>; /** * @internal @@ -651,6 +715,12 @@ export interface SignatureInformation { * The parameters of this signature. */ parameters: ParameterInformation[]; + /** + * Index of the active parameter. + * + * If provided, this is used in place of `SignatureHelp.activeSignature`. + */ + activeParameter?: number; } /** * Signature help represents the signature of something @@ -748,6 +818,20 @@ export interface DocumentHighlightProvider { provideDocumentHighlights(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * The rename provider interface defines the contract between extensions and + * the live-rename feature. + */ +export interface OnTypeRenameProvider { + + wordPattern?: RegExp; + + /** + * Provide a list of ranges that can be live-renamed together. + */ + provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>; +} + /** * Value-object that contains additional information when * requesting references. @@ -977,7 +1061,13 @@ export namespace SymbolKinds { * @internal */ export function toCssClassName(kind: SymbolKind, inline?: boolean): string { - return `codicon ${inline ? 'inline' : 'block'} codicon-symbol-${byKind.get(kind) || 'property'}`; + const symbolName = byKind.get(kind); + let codicon = symbolName && iconRegistry.get('symbol-' + symbolName); + if (!codicon) { + console.info('No codicon found for SymbolKind ' + kind); + codicon = Codicon.symbolProperty; + } + return `${inline ? 'inline' : 'block'} ${codicon.classNames}`; } } @@ -1205,11 +1295,17 @@ export interface SelectionRangeProvider { export interface FoldingContext { } /** - * A provider of colors for editor models. + * A provider of folding ranges for editor models. */ export interface FoldingRangeProvider { + /** - * Provides the color ranges for a specific model. + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChange?: Event; + + /** + * Provides the folding ranges for a specific model. */ provideFoldingRanges(model: model.ITextModel, context: FoldingContext, token: CancellationToken): ProviderResult; } @@ -1258,35 +1354,15 @@ export class FoldingRangeKind { } } -/** - * @internal - */ -export namespace WorkspaceFileEdit { - /** - * @internal - */ - export function is(thing: any): thing is WorkspaceFileEdit { - return isObject(thing) && (Boolean((thing).newUri) || Boolean((thing).oldUri)); - } -} - -/** - * @internal - */ -export namespace WorkspaceTextEdit { - /** - * @internal - */ - export function is(thing: any): thing is WorkspaceTextEdit { - return isObject(thing) && URI.isUri((thing).resource) && isObject((thing).edit); - } -} export interface WorkspaceEditMetadata { needsConfirmation: boolean; label: string; description?: string; - iconPath?: { id: string } | { light: URI, dark: URI }; + /** + * @internal + */ + iconPath?: ThemeIcon | URI | { light: URI, dark: URI }; } export interface WorkspaceFileEditOptions { @@ -1330,10 +1406,31 @@ export interface RenameProvider { /** * @internal */ -export interface Session { +export interface AuthenticationSession { id: string; accessToken: string; - displayName: string; + account: { + label: string; + id: string; + } + scopes: ReadonlyArray; +} + +/** + * @internal + */ +export interface AuthenticationSessionsChangeEvent { + added: ReadonlyArray; + removed: ReadonlyArray; + changed: ReadonlyArray; +} + +/** + * @internal + */ +export interface AuthenticationProviderInformation { + id: string; + label: string; } export interface Command { @@ -1412,11 +1509,13 @@ export interface CommentThread { comments: Comment[] | undefined; onDidChangeComments: Event; collapsibleState?: CommentThreadCollapsibleState; + canReply: boolean; input?: CommentInput; onDidChangeInput: Event; onDidChangeRange: Event; onDidChangeLabel: Event; onDidChangeCollasibleState: Event; + onDidChangeCanReply: Event; isDisposed: boolean; } @@ -1440,6 +1539,21 @@ export interface CommentReaction { readonly canEdit?: boolean; } +/** + * @internal + */ +export interface CommentOptions { + /** + * An optional string to show on the comment input box when it's collapsed. + */ + prompt?: string; + + /** + * An optional string to show as placeholder in the comment input box when it's focused. + */ + placeHolder?: string; +} + /** * @internal */ @@ -1496,7 +1610,7 @@ export interface IWebviewPortMapping { export interface IWebviewOptions { readonly enableScripts?: boolean; readonly enableCommandUris?: boolean; - readonly localResourceRoots?: ReadonlyArray; + readonly localResourceRoots?: ReadonlyArray; readonly portMapping?: ReadonlyArray; } @@ -1548,6 +1662,7 @@ export interface SemanticTokensEdits { } export interface DocumentSemanticTokensProvider { + onDidChange?: Event; getLegend(): SemanticTokensLegend; provideDocumentSemanticTokens(model: model.ITextModel, lastResultId: string | null, token: CancellationToken): ProviderResult; releaseDocumentSemanticTokens(resultId: string | undefined): void; @@ -1585,6 +1700,11 @@ export const SignatureHelpProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ @@ -1595,6 +1715,11 @@ export const DocumentSymbolProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const OnTypeRenameProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 98a39849444..295fbdc01df 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -289,3 +289,31 @@ export class StandardAutoClosingPairConditional { return (this._standardTokenMask & standardToken) === 0; } } + +/** + * @internal + */ +export class AutoClosingPairs { + + public readonly autoClosingPairsOpen: Map; + public readonly autoClosingPairsClose: Map; + + constructor(autoClosingPairs: StandardAutoClosingPairConditional[]) { + this.autoClosingPairsOpen = new Map(); + this.autoClosingPairsClose = new Map(); + for (const pair of autoClosingPairs) { + appendEntry(this.autoClosingPairsOpen, pair.open.charAt(pair.open.length - 1), pair); + if (pair.close.length === 1) { + appendEntry(this.autoClosingPairsClose, pair.close, pair); + } + } + } +} + +function appendEntry(target: Map, key: K, value: V): void { + if (target.has(key)) { + target.get(key)!.push(value); + } else { + target.set(key, [value]); + } +} diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index eacb7ed17b9..2d7600d323c 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -11,7 +11,7 @@ import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; -import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction } from 'vs/editor/common/modes/languageConfiguration'; +import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction, AutoClosingPairs } from 'vs/editor/common/modes/languageConfiguration'; import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; @@ -235,12 +235,9 @@ export class LanguageConfigurationRegistryImpl { return value.characterPair || null; } - public getAutoClosingPairs(languageId: LanguageId): StandardAutoClosingPairConditional[] { - let characterPairSupport = this._getCharacterPairSupport(languageId); - if (!characterPairSupport) { - return []; - } - return characterPairSupport.getAutoClosingPairs(); + public getAutoClosingPairs(languageId: LanguageId): AutoClosingPairs { + const characterPairSupport = this._getCharacterPairSupport(languageId); + return new AutoClosingPairs(characterPairSupport ? characterPairSupport.getAutoClosingPairs() : []); } public getAutoCloseBeforeSet(languageId: LanguageId): string { @@ -274,6 +271,16 @@ export class LanguageConfigurationRegistryImpl { return ensureValidWordDefinition(value.wordDefinition || null); } + public getWordDefinitions(): [LanguageId, RegExp][] { + let result: [LanguageId, RegExp][] = []; + this._entries.forEach((value, language) => { + if (value) { + result.push([language, value.wordDefinition]); + } + }); + return result; + } + public getFoldingRules(languageId: LanguageId): FoldingRules { let value = this._getRichEditSupport(languageId); if (!value) { diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 4278ec10e3a..b5cd1ca4a75 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { LRUCache } from 'vs/base/common/map'; +import { MovingAverage } from 'vs/base/common/numbers'; import { ITextModel } from 'vs/editor/common/model'; import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; @@ -174,3 +177,48 @@ export class LanguageFeatureRegistry { } } } + + +/** + * Keeps moving average per model and set of providers so that requests + * can be debounce according to the provider performance + */ +export class LanguageFeatureRequestDelays { + + private readonly _cache = new LRUCache(50, 0.7); + + constructor( + private readonly _registry: LanguageFeatureRegistry, + readonly min: number, + readonly max: number = Number.MAX_SAFE_INTEGER, + ) { } + + private _key(model: ITextModel): string { + return model.id + hash(this._registry.all(model)); + } + + private _clamp(value: number | undefined): number { + if (value === undefined) { + return this.min; + } else { + return Math.min(this.max, Math.max(this.min, Math.floor(value * 1.3))); + } + } + + get(model: ITextModel): number { + const key = this._key(model); + const avg = this._cache.get(key); + return this._clamp(avg?.value); + } + + update(model: ITextModel, value: number): number { + const key = this._key(model); + let avg = this._cache.get(key); + if (!avg) { + avg = new MovingAverage(); + this._cache.set(key, avg); + } + avg.update(value); + return this.get(model); + } +} diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index 4cf93dc4d23..eff5d35ab30 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -5,19 +5,20 @@ import { IRelativePattern, match as matchGlobPattern } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; // TODO@Alex +import { normalize } from 'vs/base/common/path'; export interface LanguageFilter { - language?: string; - scheme?: string; - pattern?: string | IRelativePattern; + readonly language?: string; + readonly scheme?: string; + readonly pattern?: string | IRelativePattern; /** * This provider is implemented in the UI thread. */ - hasAccessToAllModels?: boolean; - exclusive?: boolean; + readonly hasAccessToAllModels?: boolean; + readonly exclusive?: boolean; } -export type LanguageSelector = string | LanguageFilter | Array; +export type LanguageSelector = string | LanguageFilter | ReadonlyArray; export function score(selector: LanguageSelector | undefined, candidateUri: URI, candidateLanguage: string, candidateIsSynchronized: boolean): number { @@ -83,7 +84,19 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI, } if (pattern) { - if (pattern === candidateUri.fsPath || matchGlobPattern(pattern, candidateUri.fsPath)) { + let normalizedPattern: string | IRelativePattern; + if (typeof pattern === 'string') { + normalizedPattern = pattern; + } else { + // Since this pattern has a `base` property, we need + // to normalize this path first before passing it on + // because we will compare it against `Uri.fsPath` + // which uses platform specific separators. + // Refs: https://github.com/microsoft/vscode/issues/99938 + normalizedPattern = { ...pattern, base: normalize(pattern.base) }; + } + + if (normalizedPattern === candidateUri.fsPath || matchGlobPattern(normalizedPattern, candidateUri.fsPath)) { ret = 10; } else { return 0; diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 9d381736536..c0ca979fb9b 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -154,7 +154,7 @@ function getClassifier(): CharacterClassifier { if (_classifier === null) { _classifier = new CharacterClassifier(CharacterClass.None); - const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…'; + const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…'; for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) { _classifier.set(FORCE_TERMINATION_CHARACTERS.charCodeAt(i), CharacterClass.ForceTermination); } @@ -223,6 +223,7 @@ export class LinkComputer { let state = State.Start; let hasOpenParens = false; let hasOpenSquareBracket = false; + let inSquareBrackets = false; let hasOpenCurlyBracket = false; while (j < len) { @@ -241,10 +242,12 @@ export class LinkComputer { chClass = (hasOpenParens ? CharacterClass.None : CharacterClass.ForceTermination); break; case CharCode.OpenSquareBracket: + inSquareBrackets = true; hasOpenSquareBracket = true; chClass = CharacterClass.None; break; case CharCode.CloseSquareBracket: + inSquareBrackets = false; chClass = (hasOpenSquareBracket ? CharacterClass.None : CharacterClass.ForceTermination); break; case CharCode.OpenCurlyBrace: @@ -268,6 +271,14 @@ export class LinkComputer { // `*` terminates a link if the link began with `*` chClass = (linkBeginChCode === CharCode.Asterisk) ? CharacterClass.ForceTermination : CharacterClass.None; break; + case CharCode.Pipe: + // `|` terminates a link if the link began with `|` + chClass = (linkBeginChCode === CharCode.Pipe) ? CharacterClass.ForceTermination : CharacterClass.None; + break; + case CharCode.Space: + // ` ` allow space in between [ and ] + chClass = (inSquareBrackets ? CharacterClass.None : CharacterClass.ForceTermination); + break; default: chClass = classifier.get(chCode); } diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index f91dd75d17c..c2ef63388de 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -9,6 +9,7 @@ import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IDisposable } from 'vs/base/common/lifecycle'; // Define extension point ids export const Extensions = { @@ -30,9 +31,19 @@ export class EditorModesRegistry { // --- languages - public registerLanguage(def: ILanguageExtensionPoint): void { + public registerLanguage(def: ILanguageExtensionPoint): IDisposable { this._languages.push(def); this._onDidChangeLanguages.fire(undefined); + return { + dispose: () => { + for (let i = 0, len = this._languages.length; i < len; i++) { + if (this._languages[i] === def) { + this._languages.splice(i, 1); + return; + } + } + } + }; } public setDynamicLanguages(def: ILanguageExtensionPoint[]): void { this._dynamicLanguages = def; @@ -51,7 +62,7 @@ export const PLAINTEXT_LANGUAGE_IDENTIFIER = new LanguageIdentifier(PLAINTEXT_MO ModesRegistry.registerLanguage({ id: PLAINTEXT_MODE_ID, - extensions: ['.txt', '.gitignore'], + extensions: ['.txt'], aliases: [nls.localize('plainText.alias', "Plain Text"), 'text'], mimetypes: ['text/plain'] }); diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts index ae10537c82e..9325b4814a0 100644 --- a/src/vs/editor/common/modes/supports/richEditBrackets.ts +++ b/src/vs/editor/common/modes/supports/richEditBrackets.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from 'vs/base/common/strings'; +import * as stringBuilder from 'vs/editor/common/core/stringBuilder'; import { Range } from 'vs/editor/common/core/range'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; @@ -264,14 +265,24 @@ function createBracketOrRegExp(pieces: string[]): RegExp { return strings.createRegExp(regexStr, true); } -let toReversedString = (function () { +const toReversedString = (function () { function reverse(str: string): string { - let reversedStr = ''; - for (let i = str.length - 1; i >= 0; i--) { - reversedStr += str.charAt(i); + if (stringBuilder.hasTextDecoder) { + // create a Uint16Array and then use a TextDecoder to create a string + const arr = new Uint16Array(str.length); + let offset = 0; + for (let i = str.length - 1; i >= 0; i--) { + arr[offset++] = str.charCodeAt(i); + } + return stringBuilder.getPlatformTextDecoder().decode(arr); + } else { + let result: string[] = [], resultLen = 0; + for (let i = str.length - 1; i >= 0; i--) { + result[resultLen++] = str.charAt(i); + } + return result.join(''); } - return reversedStr; } let lastInput: string | null = null; diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index 12566c3b00e..e28c23d39b4 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -395,7 +395,7 @@ export class ThemeTrieElement { } } -export function generateTokensCSSForColorMap(colorMap: Color[]): string { +export function generateTokensCSSForColorMap(colorMap: readonly Color[]): string { let rules: string[] = []; for (let i = 1, len = colorMap.length; i < len; i++) { let color = colorMap[i]; diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index c8c4acac430..dd64e79ded6 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -68,7 +68,9 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens break; case CharCode.UTF8_BOM: - case CharCode.LINE_SEPARATOR_2028: + case CharCode.LINE_SEPARATOR: + case CharCode.PARAGRAPH_SEPARATOR: + case CharCode.NEXT_LINE: partContent += '\ufffd'; break; diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index fc3423047fb..c92c1296b19 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -7,8 +7,6 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; -import { withUndefinedAsNull } from 'vs/base/common/types'; -import { keys } from 'vs/base/common/map'; export class TokenizationRegistryImpl implements ITokenizationRegistry { @@ -77,13 +75,13 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { } public get(language: string): ITokenizationSupport | null { - return withUndefinedAsNull(this._map.get(language)); + return (this._map.get(language) || null); } public setColorMap(colorMap: Color[]): void { this._colorMap = colorMap; this._onDidChange.fire({ - changedLanguages: keys(this._map), + changedLanguages: Array.from(this._map.keys()), changedColorMap: true }); } diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 3e5361ce764..69592e735c6 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -5,7 +5,6 @@ import { mergeSort } from 'vs/base/common/arrays'; import { stringDiff } from 'vs/base/common/diff/diff'; -import { FIN, Iterator, IteratorResult } from 'vs/base/common/iterator'; import { IDisposable } from 'vs/base/common/lifecycle'; import { globals } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -24,6 +23,7 @@ import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerSe import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; import * as types from 'vs/base/common/types'; import { EditorWorkerHost } from 'vs/editor/common/services/editorWorkerServiceImpl'; +import { StopWatch } from 'vs/base/common/stopwatch'; export interface IMirrorModel { readonly uri: URI; @@ -65,7 +65,7 @@ export interface ICommonModel extends ILinkComputerTarget, IMirrorModel { getLineCount(): number; getLineContent(lineNumber: number): string; getLineWords(lineNumber: number, wordDefinition: RegExp): IWordAtPosition[]; - createWordIterator(wordDefinition: RegExp): Iterator; + words(wordDefinition: RegExp): Iterable; getWordUntilPosition(position: IPosition, wordDefinition: RegExp): IWordAtPosition; getValueInRange(range: IRange): string; getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range | null; @@ -153,36 +153,37 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel { }; } - public createWordIterator(wordDefinition: RegExp): Iterator { - let obj: { done: false; value: string; }; + + public words(wordDefinition: RegExp): Iterable { + + const lines = this._lines; + const wordenize = this._wordenize.bind(this); + let lineNumber = 0; - let lineText: string; + let lineText = ''; let wordRangesIdx = 0; let wordRanges: IWordRange[] = []; - let next = (): IteratorResult => { - if (wordRangesIdx < wordRanges.length) { - const value = lineText.substring(wordRanges[wordRangesIdx].start, wordRanges[wordRangesIdx].end); - wordRangesIdx += 1; - if (!obj) { - obj = { done: false, value: value }; - } else { - obj.value = value; + return { + *[Symbol.iterator]() { + while (true) { + if (wordRangesIdx < wordRanges.length) { + const value = lineText.substring(wordRanges[wordRangesIdx].start, wordRanges[wordRangesIdx].end); + wordRangesIdx += 1; + yield value; + } else { + if (lineNumber < lines.length) { + lineText = lines[lineNumber]; + wordRanges = wordenize(lineText, wordDefinition); + wordRangesIdx = 0; + lineNumber += 1; + } else { + break; + } + } } - return obj; - - } else if (lineNumber >= this._lines.length) { - return FIN; - - } else { - lineText = this._lines[lineNumber]; - wordRanges = this._wordenize(lineText, wordDefinition); - wordRangesIdx = 0; - lineNumber += 1; - return next(); } }; - return { next }; } public getLineWords(lineNumber: number, wordDefinition: RegExp): IWordAtPosition[] { @@ -529,13 +530,13 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { private static readonly _suggestionsLimit = 10000; - public async textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise { + public async textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> { const model = this._getModel(modelUrl); if (!model) { return null; } - + const sw = new StopWatch(true); const words: string[] = []; const seen = new Set(); const wordDefRegExp = new RegExp(wordDef, wordDefFlags); @@ -545,12 +546,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { seen.add(model.getValueInRange(wordAt)); } - for ( - let iter = model.createWordIterator(wordDefRegExp), e = iter.next(); - !e.done && seen.size <= EditorSimpleWorker._suggestionsLimit; - e = iter.next() - ) { - const word = e.value; + for (let word of model.words(wordDefRegExp)) { if (seen.has(word)) { continue; } @@ -559,8 +555,11 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { continue; } words.push(word); + if (seen.size > EditorSimpleWorker._suggestionsLimit) { + break; + } } - return words; + return { words, duration: sw.elapsed() }; } diff --git a/src/vs/editor/common/services/editorWorkerService.ts b/src/vs/editor/common/services/editorWorkerService.ts index ab850e96a2c..a99c84822d8 100644 --- a/src/vs/editor/common/services/editorWorkerService.ts +++ b/src/vs/editor/common/services/editorWorkerService.ts @@ -19,7 +19,7 @@ export interface IDiffComputationResult { } export interface IEditorWorkerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; canComputeDiff(original: URI, modified: URI): boolean; computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise; diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 1f2df5a96b7..5976df3c226 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -22,6 +22,7 @@ import { regExpFlags } from 'vs/base/common/strings'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; import { StopWatch } from 'vs/base/common/stopwatch'; +import { canceled } from 'vs/base/common/errors'; /** * Stop syncing a model to the worker if it was not needed for 1 min. @@ -45,11 +46,13 @@ function canSyncModel(modelService: IModelService, resource: URI): boolean { } export class EditorWorkerServiceImpl extends Disposable implements IEditorWorkerService { - public _serviceBrand: undefined; + + declare readonly _serviceBrand: undefined; private readonly _modelService: IModelService; private readonly _workerManager: WorkerManager; private readonly _logService: ILogService; + constructor( @IModelService modelService: IModelService, @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @@ -60,7 +63,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker this._workerManager = this._register(new WorkerManager(this._modelService)); this._logService = logService; - // todo@joh make sure this happens only once + // register default link-provider and default completions-provider this._register(modes.LinkProviderRegistry.register('*', { provideLinks: (model, token) => { if (!canSyncModel(this._modelService, model.uri)) { @@ -158,20 +161,21 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider { const insert = replace.setEndPosition(position.lineNumber, position.column); const client = await this._workerManager.withWorker(); - const words = await client.textualSuggest(model.uri, position); - if (!words) { + const data = await client.textualSuggest(model.uri, position); + if (!data) { return undefined; } return { - suggestions: words.map((word): modes.CompletionItem => { + duration: data.duration, + suggestions: data.words.map((word): modes.CompletionItem => { return { kind: modes.CompletionItemKind.Text, label: word, insertText: word, range: { insert, replace } }; - }) + }), }; } } @@ -378,6 +382,7 @@ export class EditorWorkerClient extends Disposable { private _worker: IWorkerClient | null; private readonly _workerFactory: DefaultWorkerFactory; private _modelManager: EditorModelManager | null; + private _disposed = false; constructor(modelService: IModelService, keepIdleModels: boolean, label: string | undefined) { super(); @@ -425,6 +430,9 @@ export class EditorWorkerClient extends Disposable { } protected _withSyncedResources(resources: URI[]): Promise { + if (this._disposed) { + return Promise.reject(canceled()); + } return this._getProxy().then((proxy) => { this._getOrCreateModelManager(proxy).ensureSyncedResources(resources); return proxy; @@ -455,7 +463,7 @@ export class EditorWorkerClient extends Disposable { }); } - public textualSuggest(resource: URI, position: IPosition): Promise { + public textualSuggest(resource: URI, position: IPosition): Promise<{ words: string[], duration: number } | null> { return this._withSyncedResources([resource]).then(proxy => { let model = this._modelService.getModel(resource); if (!model) { @@ -493,4 +501,9 @@ export class EditorWorkerClient extends Disposable { return proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags); }); } + + dispose(): void { + super.dispose(); + this._disposed = true; + } } diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index fbdc31d6d5c..b20a0b7c58b 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -54,6 +54,11 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe return classes; } + +export function getIconClassesForModeId(modeId: string): string[] { + return ['file-icon', `${cssEscape(modeId)}-lang-file-icon`]; +} + export function detectModeId(modelService: IModelService, modeService: IModeService, resource: uri): string | null { if (!resource) { return null; // we need a resource at least diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 8a12a471f95..c734735a733 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -15,7 +15,6 @@ import { NULL_LANGUAGE_IDENTIFIER, NULL_MODE_ID } from 'vs/editor/common/modes/n import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { withUndefinedAsNull } from 'vs/base/common/types'; const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -154,9 +153,14 @@ export class LanguagesRegistry extends Disposable { } if (Array.isArray(lang.extensions)) { + if (lang.configuration) { + // insert first as this appears to be the 'primary' language definition + resolvedLanguage.extensions = lang.extensions.concat(resolvedLanguage.extensions); + } else { + resolvedLanguage.extensions = resolvedLanguage.extensions.concat(lang.extensions); + } for (let extension of lang.extensions) { mime.registerTextMime({ id: langId, mime: primaryMime, extension: extension }, this._warnOnOverwrite); - resolvedLanguage.extensions.push(extension); } } @@ -268,7 +272,7 @@ export class LanguagesRegistry extends Disposable { return null; } const language = this._languages[modeId]; - return withUndefinedAsNull(language.mimetypes[0]); + return (language.mimetypes[0] || null); } public extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): string[] { diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index 78820460000..eb2d6795d48 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -12,11 +12,9 @@ import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeServ import { overviewRulerWarning, overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; -import { keys } from 'vs/base/common/map'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { Schemas } from 'vs/base/common/network'; import { Emitter, Event } from 'vs/base/common/event'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { minimapWarning, minimapError } from 'vs/platform/theme/common/colorRegistry'; function MODEL_ID(resource: URI): string { @@ -32,18 +30,19 @@ class MarkerDecorations extends Disposable { ) { super(); this._register(toDisposable(() => { - this.model.deltaDecorations(keys(this._markersData), []); + this.model.deltaDecorations([...this._markersData.keys()], []); this._markersData.clear(); })); } - public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): void { - const oldIds = keys(this._markersData); + public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): boolean { + const oldIds = [...this._markersData.keys()]; this._markersData.clear(); const ids = this.model.deltaDecorations(oldIds, newDecorations); for (let index = 0; index < ids.length; index++) { this._markersData.set(ids[index], markers[index]); } + return oldIds.length !== 0 || ids.length !== 0; } getMarker(decoration: IModelDecoration): IMarker | undefined { @@ -64,7 +63,7 @@ class MarkerDecorations extends Disposable { export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeMarker = this._register(new Emitter()); readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; @@ -90,7 +89,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null { const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri)); - return markerDecorations ? withUndefinedAsNull(markerDecorations.getMarker(decoration)) : null; + return markerDecorations ? (markerDecorations.getMarker(decoration) || null) : null; } getLiveMarkers(model: ITextModel): [Range, IMarker][] { @@ -139,8 +138,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor options: this._createDecorationOption(marker) }; }); - markerDecorations.update(markers, newModelDecorations); - this._onDidChangeMarker.fire(markerDecorations.model); + if (markerDecorations.update(markers, newModelDecorations)) { + this._onDidChangeMarker.fire(markerDecorations.model); + } } private _createDecorationRange(model: ITextModel, rawMarker: IMarker): Range { diff --git a/src/vs/editor/common/services/markersDecorationService.ts b/src/vs/editor/common/services/markersDecorationService.ts index cd1c541cbc0..745260c8884 100644 --- a/src/vs/editor/common/services/markersDecorationService.ts +++ b/src/vs/editor/common/services/markersDecorationService.ts @@ -12,7 +12,7 @@ import { Range } from 'vs/editor/common/core/range'; export const IMarkerDecorationsService = createDecorator('markerDecorationsService'); export interface IMarkerDecorationsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidChangeMarker: Event; diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index a6d2a6bc9e2..2739d652551 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -28,9 +28,10 @@ export interface ILanguageSelection extends IDisposable { } export interface IModeService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidCreateMode: Event; + onLanguagesMaybeChanged: Event; // --- reading isRegisteredMode(mimetypeOrModeId: string): boolean; diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index 083d387118b..6b2fd6f80f8 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -50,7 +50,7 @@ export class ModeServiceImpl implements IModeService { public readonly onDidCreateMode: Event = this._onDidCreateMode.event; protected readonly _onLanguagesMaybeChanged = new Emitter(); - private readonly onLanguagesMaybeChanged: Event = this._onLanguagesMaybeChanged.event; + public readonly onLanguagesMaybeChanged: Event = this._onLanguagesMaybeChanged.event; constructor(warnOnOverwrite = false) { this._instantiatedModes = {}; diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts index 1c4a4514404..ab583a7390c 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/services/modelService.ts @@ -8,11 +8,15 @@ import { URI } from 'vs/base/common/uri'; import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model'; import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider } from 'vs/editor/common/modes'; +import { SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling'; export const IModelService = createDecorator('modelService'); +export type DocumentTokensProvider = DocumentSemanticTokensProvider | DocumentRangeSemanticTokensProvider; + export interface IModelService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource?: URI, isForSimpleWidget?: boolean): ITextModel; @@ -28,6 +32,8 @@ export interface IModelService { getModel(resource: URI): ITextModel | null; + getSemanticTokensProviderStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling; + onModelAdded: Event; onModelRemoved: Event; diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 26222549279..7836fa5de72 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -4,34 +4,54 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import * as errors from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { EDITOR_MODEL_DEFAULTS, IEditorSemanticHighlightingOptions } from 'vs/editor/common/config/editorOptions'; +import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model'; import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata } from 'vs/editor/common/modes'; +import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits } from 'vs/editor/common/modes'; import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; import { ILanguageSelection } from 'vs/editor/common/services/modeService'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModelService, DocumentTokensProvider } from 'vs/editor/common/services/modelService'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; +import { StringSHA1 } from 'vs/base/common/hash'; +import { EditStackElement, isEditStackElement } from 'vs/editor/common/model/editStack'; +import { Schemas } from 'vs/base/common/network'; +import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling'; + +export interface IEditorSemanticHighlightingOptions { + enabled: true | false | 'configuredByTheme'; +} function MODEL_ID(resource: URI): string { return resource.toString(); } +function computeModelSha1(model: ITextModel): string { + // compute the sha1 + const shaComputer = new StringSHA1(); + const snapshot = model.createSnapshot(); + let text: string | null; + while ((text = snapshot.read())) { + shaComputer.update(text); + } + return shaComputer.digest(); +} + + class ModelData implements IDisposable { - public readonly model: ITextModel; + public readonly model: TextModel; private _languageSelection: ILanguageSelection | null; private _languageSelectionListener: IDisposable | null; @@ -39,7 +59,7 @@ class ModelData implements IDisposable { private readonly _modelEventListeners = new DisposableStore(); constructor( - model: ITextModel, + model: TextModel, onWillDispose: (model: ITextModel) => void, onDidChangeLanguage: (model: ITextModel, e: IModelLanguageChangedEvent) => void ) { @@ -93,12 +113,38 @@ interface IRawConfig { const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF; -export class ModelServiceImpl extends Disposable implements IModelService { - public _serviceBrand: undefined; +export interface EditStackPastFutureElements { + past: EditStackElement[]; + future: EditStackElement[]; +} - private readonly _configurationService: IConfigurationService; - private readonly _configurationServiceSubscription: IDisposable; - private readonly _resourcePropertiesService: ITextResourcePropertiesService; +class DisposedModelInfo { + constructor( + public readonly uri: URI, + public readonly initialUndoRedoSnapshot: ResourceEditStackSnapshot | null, + public readonly time: number, + public readonly sharesUndoRedoStack: boolean, + public readonly heapSize: number, + public readonly sha1: string, + public readonly versionId: number, + public readonly alternativeVersionId: number, + ) { } +} + +function schemaShouldMaintainUndoRedoElements(resource: URI) { + return ( + resource.scheme === Schemas.file + || resource.scheme === Schemas.vscodeRemote + || resource.scheme === Schemas.userData + || resource.scheme === 'fake-fs' // for tests + ); +} + +export class ModelServiceImpl extends Disposable implements IModelService { + + public static MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK = 20 * 1024 * 1024; + + public _serviceBrand: undefined; private readonly _onModelAdded: Emitter = this._register(new Emitter()); public readonly onModelAdded: Event = this._onModelAdded.event; @@ -109,37 +155,40 @@ export class ModelServiceImpl extends Disposable implements IModelService { private readonly _onModelModeChanged: Emitter<{ model: ITextModel; oldModeId: string; }> = this._register(new Emitter<{ model: ITextModel; oldModeId: string; }>()); public readonly onModelModeChanged: Event<{ model: ITextModel; oldModeId: string; }> = this._onModelModeChanged.event; - private _modelCreationOptionsByLanguageAndResource: { - [languageAndResource: string]: ITextModelCreationOptions; - }; + private _modelCreationOptionsByLanguageAndResource: { [languageAndResource: string]: ITextModelCreationOptions; }; /** * All the models known in the system. */ private readonly _models: { [modelId: string]: ModelData; }; + private readonly _disposedModels: Map; + private _disposedModelsHeapSize: number; + private readonly _semanticStyling: SemanticStyling; constructor( - @IConfigurationService configurationService: IConfigurationService, - @ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService, - @IThemeService themeService: IThemeService, - @ILogService logService: ILogService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ITextResourcePropertiesService private readonly _resourcePropertiesService: ITextResourcePropertiesService, + @IThemeService private readonly _themeService: IThemeService, + @ILogService private readonly _logService: ILogService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, ) { super(); - this._configurationService = configurationService; - this._resourcePropertiesService = resourcePropertiesService; - this._models = {}; this._modelCreationOptionsByLanguageAndResource = Object.create(null); + this._models = {}; + this._disposedModels = new Map(); + this._disposedModelsHeapSize = 0; + this._semanticStyling = this._register(new SemanticStyling(this._themeService, this._logService)); - this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions()); + this._register(this._configurationService.onDidChangeConfiguration(() => this._updateModelOptions())); this._updateModelOptions(); - this._register(new SemanticColoringFeature(this, themeService, configurationService, logService)); + this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._semanticStyling)); } private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions { let tabSize = EDITOR_MODEL_DEFAULTS.tabSize; if (config.editor && typeof config.editor.tabSize !== 'undefined') { - let parsedTabSize = parseInt(config.editor.tabSize, 10); + const parsedTabSize = parseInt(config.editor.tabSize, 10); if (!isNaN(parsedTabSize)) { tabSize = parsedTabSize; } @@ -150,7 +199,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { let indentSize = tabSize; if (config.editor && typeof config.editor.indentSize !== 'undefined' && config.editor.indentSize !== 'tabSize') { - let parsedIndentSize = parseInt(config.editor.indentSize, 10); + const parsedIndentSize = parseInt(config.editor.indentSize, 10); if (!isNaN(parsedIndentSize)) { indentSize = parsedIndentSize; } @@ -199,11 +248,30 @@ export class ModelServiceImpl extends Disposable implements IModelService { }; } + private _getEOL(resource: URI | undefined, language: string): string { + if (resource) { + return this._resourcePropertiesService.getEOL(resource, language); + } + const eol = this._configurationService.getValue('files.eol', { overrideIdentifier: language }); + if (eol && eol !== 'auto') { + return eol; + } + return platform.OS === platform.OperatingSystem.Linux || platform.OS === platform.OperatingSystem.Macintosh ? '\n' : '\r\n'; + } + + private _shouldRestoreUndoStack(): boolean { + const result = this._configurationService.getValue('files.restoreUndoStack'); + if (typeof result === 'boolean') { + return result; + } + return true; + } + public getCreationOptions(language: string, resource: URI | undefined, isForSimpleWidget: boolean): ITextModelCreationOptions { let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource]; if (!creationOptions) { const editor = this._configurationService.getValue('editor', { overrideIdentifier: language, resource }); - const eol = this._resourcePropertiesService.getEOL(resource, language); + const eol = this._getEOL(resource, language); creationOptions = ModelServiceImpl._readModelOptions({ editor, eol }, isForSimpleWidget); this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions; } @@ -211,14 +279,14 @@ export class ModelServiceImpl extends Disposable implements IModelService { } private _updateModelOptions(): void { - let oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource; + const oldOptionsByLanguageAndResource = this._modelCreationOptionsByLanguageAndResource; this._modelCreationOptionsByLanguageAndResource = Object.create(null); // Update options on all models - let keys = Object.keys(this._models); + const keys = Object.keys(this._models); for (let i = 0, len = keys.length; i < len; i++) { - let modelId = keys[i]; - let modelData = this._models[modelId]; + const modelId = keys[i]; + const modelData = this._models[modelId]; const language = modelData.model.getLanguageIdentifier().language; const uri = modelData.model.uri; const oldOptions = oldOptionsByLanguageAndResource[language + uri]; @@ -258,17 +326,73 @@ export class ModelServiceImpl extends Disposable implements IModelService { } } - public dispose(): void { - this._configurationServiceSubscription.dispose(); - super.dispose(); + // --- begin IModelService + + private _insertDisposedModel(disposedModelData: DisposedModelInfo): void { + this._disposedModels.set(MODEL_ID(disposedModelData.uri), disposedModelData); + this._disposedModelsHeapSize += disposedModelData.heapSize; } - // --- begin IModelService + private _removeDisposedModel(resource: URI): DisposedModelInfo | undefined { + const disposedModelData = this._disposedModels.get(MODEL_ID(resource)); + if (disposedModelData) { + this._disposedModelsHeapSize -= disposedModelData.heapSize; + } + this._disposedModels.delete(MODEL_ID(resource)); + return disposedModelData; + } + + private _ensureDisposedModelsHeapSize(maxModelsHeapSize: number): void { + if (this._disposedModelsHeapSize > maxModelsHeapSize) { + // we must remove some old undo stack elements to free up some memory + const disposedModels: DisposedModelInfo[] = []; + this._disposedModels.forEach(entry => { + if (!entry.sharesUndoRedoStack) { + disposedModels.push(entry); + } + }); + disposedModels.sort((a, b) => a.time - b.time); + while (disposedModels.length > 0 && this._disposedModelsHeapSize > maxModelsHeapSize) { + const disposedModel = disposedModels.shift()!; + this._removeDisposedModel(disposedModel.uri); + if (disposedModel.initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(disposedModel.initialUndoRedoSnapshot); + } + } + } + } private _createModelData(value: string | ITextBufferFactory, languageIdentifier: LanguageIdentifier, resource: URI | undefined, isForSimpleWidget: boolean): ModelData { // create & save the model const options = this.getCreationOptions(languageIdentifier.language, resource, isForSimpleWidget); - const model: TextModel = new TextModel(value, options, languageIdentifier, resource); + const model: TextModel = new TextModel(value, options, languageIdentifier, resource, this._undoRedoService); + if (resource && this._disposedModels.has(MODEL_ID(resource))) { + const disposedModelData = this._removeDisposedModel(resource)!; + const elements = this._undoRedoService.getElements(resource); + const sha1IsEqual = (computeModelSha1(model) === disposedModelData.sha1); + if (sha1IsEqual || disposedModelData.sharesUndoRedoStack) { + for (const element of elements.past) { + if (isEditStackElement(element) && element.matchesResource(resource)) { + element.setModel(model); + } + } + for (const element of elements.future) { + if (isEditStackElement(element) && element.matchesResource(resource)) { + element.setModel(model); + } + } + this._undoRedoService.setElementsValidFlag(resource, true, (element) => (isEditStackElement(element) && element.matchesResource(resource))); + if (sha1IsEqual) { + model._overwriteVersionId(disposedModelData.versionId); + model._overwriteAlternativeVersionId(disposedModelData.alternativeVersionId); + model._overwriteInitialUndoRedoSnapshot(disposedModelData.initialUndoRedoSnapshot); + } + } else { + if (disposedModelData.initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(disposedModelData.initialUndoRedoSnapshot); + } + } + } const modelId = MODEL_ID(model.uri); if (this._models[modelId]) { @@ -301,7 +425,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { model.pushEditOperations( [], ModelServiceImpl._computeEdits(model, textBuffer), - (inverseEditOperations: IIdentifiedSingleEditOperation[]) => [] + () => [] ); model.pushStackElement(); } @@ -341,7 +465,8 @@ export class ModelServiceImpl extends Disposable implements IModelService { const commonSuffix = this._commonSuffix(model, modelLineCount - commonPrefix, commonPrefix, textBuffer, textBufferLineCount - commonPrefix, commonPrefix); - let oldRange: Range, newRange: Range; + let oldRange: Range; + let newRange: Range; if (commonSuffix > 0) { oldRange = new Range(commonPrefix + 1, 1, modelLineCount - commonSuffix + 1, 1); newRange = new Range(commonPrefix + 1, 1, textBufferLineCount - commonSuffix + 1, 1); @@ -375,7 +500,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { if (!languageSelection) { return; } - let modelData = this._models[MODEL_ID(model.uri)]; + const modelData = this._models[MODEL_ID(model.uri)]; if (!modelData) { return; } @@ -384,19 +509,71 @@ export class ModelServiceImpl extends Disposable implements IModelService { public destroyModel(resource: URI): void { // We need to support that not all models get disposed through this service (i.e. model.dispose() should work!) - let modelData = this._models[MODEL_ID(resource)]; + const modelData = this._models[MODEL_ID(resource)]; if (!modelData) { return; } + const model = modelData.model; + const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString()); + let maintainUndoRedoStack = false; + let heapSize = 0; + if (sharesUndoRedoStack || (this._shouldRestoreUndoStack() && schemaShouldMaintainUndoRedoElements(resource))) { + const elements = this._undoRedoService.getElements(resource); + if (elements.past.length > 0 || elements.future.length > 0) { + for (const element of elements.past) { + if (isEditStackElement(element) && element.matchesResource(resource)) { + maintainUndoRedoStack = true; + heapSize += element.heapSize(resource); + element.setModel(resource); // remove reference from text buffer instance + } + } + for (const element of elements.future) { + if (isEditStackElement(element) && element.matchesResource(resource)) { + maintainUndoRedoStack = true; + heapSize += element.heapSize(resource); + element.setModel(resource); // remove reference from text buffer instance + } + } + } + } + + if (!maintainUndoRedoStack) { + if (!sharesUndoRedoStack) { + const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot(); + if (initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot); + } + } + modelData.model.dispose(); + return; + } + + const maxMemory = ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK; + if (!sharesUndoRedoStack && heapSize > maxMemory) { + // the undo stack for this file would never fit in the configured memory, so don't bother with it. + const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot(); + if (initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot); + } + modelData.model.dispose(); + return; + } + + this._ensureDisposedModelsHeapSize(maxMemory - heapSize); + + // We only invalidate the elements, but they remain in the undo-redo service. + this._undoRedoService.setElementsValidFlag(resource, false, (element) => (isEditStackElement(element) && element.matchesResource(resource))); + this._insertDisposedModel(new DisposedModelInfo(resource, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId())); + modelData.model.dispose(); } public getModels(): ITextModel[] { - let ret: ITextModel[] = []; + const ret: ITextModel[] = []; - let keys = Object.keys(this._models); + const keys = Object.keys(this._models); for (let i = 0, len = keys.length; i < len; i++) { - let modelId = keys[i]; + const modelId = keys[i]; ret.push(this._models[modelId].model); } @@ -404,19 +581,23 @@ export class ModelServiceImpl extends Disposable implements IModelService { } public getModel(resource: URI): ITextModel | null { - let modelId = MODEL_ID(resource); - let modelData = this._models[modelId]; + const modelId = MODEL_ID(resource); + const modelData = this._models[modelId]; if (!modelData) { return null; } return modelData.model; } + public getSemanticTokensProviderStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling { + return this._semanticStyling.get(provider); + } + // --- end IModelService private _onWillDispose(model: ITextModel): void { - let modelId = MODEL_ID(model.uri); - let modelData = this._models[modelId]; + const modelId = MODEL_ID(model.uri); + const modelData = this._models[modelId]; delete this._models[modelId]; modelData.dispose(); @@ -441,24 +622,26 @@ export interface ILineSequence { getLineContent(lineNumber: number): string; } +export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting'; + +export function isSemanticColoringEnabled(model: ITextModel, themeService: IThemeService, configurationService: IConfigurationService): boolean { + const setting = configurationService.getValue(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri })?.enabled; + if (typeof setting === 'boolean') { + return setting; + } + return themeService.getColorTheme().semanticHighlighting; +} + class SemanticColoringFeature extends Disposable { - private static readonly SETTING_ID = 'editor.semanticHighlighting'; + private readonly _watchers: Record; + private readonly _semanticStyling: SemanticStyling; - private _watchers: Record; - private _semanticStyling: SemanticStyling; - private _configurationService: IConfigurationService; - - constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) { + constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, semanticStyling: SemanticStyling) { super(); - this._configurationService = configurationService; this._watchers = Object.create(null); - this._semanticStyling = this._register(new SemanticStyling(themeService, logService)); + this._semanticStyling = semanticStyling; - const isSemanticColoringEnabled = (model: ITextModel) => { - const options = configurationService.getValue(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }); - return options && options.enabled; - }; const register = (model: ITextModel) => { this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); }; @@ -466,8 +649,22 @@ class SemanticColoringFeature extends Disposable { modelSemanticColoring.dispose(); delete this._watchers[model.uri.toString()]; }; + const handleSettingOrThemeChange = () => { + for (let model of modelService.getModels()) { + const curr = this._watchers[model.uri.toString()]; + if (isSemanticColoringEnabled(model, themeService, configurationService)) { + if (!curr) { + register(model); + } + } else { + if (curr) { + deregister(model, curr); + } + } + } + }; this._register(modelService.onModelAdded((model) => { - if (isSemanticColoringEnabled(model)) { + if (isSemanticColoringEnabled(model, themeService, configurationService)) { register(model); } })); @@ -477,203 +674,38 @@ class SemanticColoringFeature extends Disposable { deregister(model, curr); } })); - this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) { - for (let model of modelService.getModels()) { - const curr = this._watchers[model.uri.toString()]; - if (isSemanticColoringEnabled(model)) { - if (!curr) { - register(model); - } - } else { - if (curr) { - deregister(model, curr); - } - } - } + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) { + handleSettingOrThemeChange(); } - }); + })); + this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange)); } } class SemanticStyling extends Disposable { - private _caches: WeakMap; + private _caches: WeakMap; constructor( private readonly _themeService: IThemeService, private readonly _logService: ILogService ) { super(); - this._caches = new WeakMap(); - if (this._themeService) { - // workaround for tests which use undefined... :/ - this._register(this._themeService.onThemeChange(() => { - this._caches = new WeakMap(); - })); - } + this._caches = new WeakMap(); + this._register(this._themeService.onDidColorThemeChange(() => { + this._caches = new WeakMap(); + })); } - public get(provider: DocumentSemanticTokensProvider): SemanticColoringProviderStyling { + public get(provider: DocumentTokensProvider): SemanticTokensProviderStyling { if (!this._caches.has(provider)) { - this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService, this._logService)); + this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._logService)); } return this._caches.get(provider)!; } } -const enum Constants { - NO_STYLING = 0b01111111111111111111111111111111 -} - -class HashTableEntry { - public readonly tokenTypeIndex: number; - public readonly tokenModifierSet: number; - public readonly metadata: number; - public next: HashTableEntry | null; - - constructor(tokenTypeIndex: number, tokenModifierSet: number, metadata: number) { - this.tokenTypeIndex = tokenTypeIndex; - this.tokenModifierSet = tokenModifierSet; - this.metadata = metadata; - this.next = null; - } -} - -class HashTable { - - private static _SIZES = [3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143]; - - private _elementsCount: number; - private _currentLengthIndex: number; - private _currentLength: number; - private _growCount: number; - private _elements: (HashTableEntry | null)[]; - - constructor() { - this._elementsCount = 0; - this._currentLengthIndex = 0; - this._currentLength = HashTable._SIZES[this._currentLengthIndex]; - this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0); - this._elements = []; - HashTable._nullOutEntries(this._elements, this._currentLength); - } - - private static _nullOutEntries(entries: (HashTableEntry | null)[], length: number): void { - for (let i = 0; i < length; i++) { - entries[i] = null; - } - } - - private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number): number { - return ((((tokenTypeIndex << 5) - tokenTypeIndex) + tokenModifierSet) | 0) % this._currentLength; // tokenTypeIndex * 31 + tokenModifierSet, keep as int32 - } - - public get(tokenTypeIndex: number, tokenModifierSet: number): HashTableEntry | null { - const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet); - - let p = this._elements[hash]; - while (p) { - if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet) { - return p; - } - p = p.next; - } - - return null; - } - - public add(tokenTypeIndex: number, tokenModifierSet: number, metadata: number): void { - this._elementsCount++; - if (this._growCount !== 0 && this._elementsCount >= this._growCount) { - // expand! - const oldElements = this._elements; - - this._currentLengthIndex++; - this._currentLength = HashTable._SIZES[this._currentLengthIndex]; - this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0); - this._elements = []; - HashTable._nullOutEntries(this._elements, this._currentLength); - - for (const first of oldElements) { - let p = first; - while (p) { - const oldNext = p.next; - p.next = null; - this._add(p); - p = oldNext; - } - } - } - this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, metadata)); - } - - private _add(element: HashTableEntry): void { - const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet); - element.next = this._elements[hash]; - this._elements[hash] = element; - } -} - -class SemanticColoringProviderStyling { - - private readonly _hashTable: HashTable; - - constructor( - private readonly _legend: SemanticTokensLegend, - private readonly _themeService: IThemeService, - private readonly _logService: ILogService - ) { - this._hashTable = new HashTable(); - } - - public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number { - const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet); - let metadata: number | undefined; - if (entry) { - metadata = entry.metadata; - } else { - const tokenType = this._legend.tokenTypes[tokenTypeIndex]; - const tokenModifiers: string[] = []; - let modifierSet = tokenModifierSet; - for (let modifierIndex = 0; modifierSet > 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) { - if (modifierSet & 1) { - tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]); - } - modifierSet = modifierSet >> 1; - } - - metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); - if (typeof metadata === 'undefined') { - metadata = Constants.NO_STYLING; - } - this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata); - } - if (this._logService.getLevel() === LogLevel.Trace) { - const type = this._legend.tokenTypes[tokenTypeIndex]; - const modifiers = tokenModifierSet ? ' ' + this._legend.tokenModifiers.filter((_, i) => tokenModifierSet & (1 << i)).join(' ') : ''; - this._logService.trace(`tokenStyleMetadata ${entry ? '[CACHED] ' : ''}${type}${modifiers}: foreground ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`); - } - return metadata; - } - - -} - -const enum SemanticColoringConstants { - /** - * Let's aim at having 8KB buffers if possible... - * So that would be 8192 / (5 * 4) = 409.6 tokens per area - */ - DesiredTokensPerArea = 400, - - /** - * Try to keep the total number of areas under 1024 if possible, - * simply compensate by having more tokens per area... - */ - DesiredMaxAreas = 1024, -} - class SemanticTokensResponse { constructor( private readonly _provider: DocumentSemanticTokensProvider, @@ -691,9 +723,10 @@ class ModelSemanticColoring extends Disposable { private _isDisposed: boolean; private readonly _model: ITextModel; private readonly _semanticStyling: SemanticStyling; - private readonly _fetchSemanticTokens: RunOnceScheduler; - private _currentResponse: SemanticTokensResponse | null; - private _currentRequestCancellationTokenSource: CancellationTokenSource | null; + private readonly _fetchDocumentSemanticTokens: RunOnceScheduler; + private _currentDocumentResponse: SemanticTokensResponse | null; + private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null; + private _documentProvidersChangeListeners: IDisposable[]; constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) { super(); @@ -701,44 +734,57 @@ class ModelSemanticColoring extends Disposable { this._isDisposed = false; this._model = model; this._semanticStyling = stylingProvider; - this._fetchSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchSemanticTokensNow(), 300)); - this._currentResponse = null; - this._currentRequestCancellationTokenSource = null; + this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), 300)); + this._currentDocumentResponse = null; + this._currentDocumentRequestCancellationTokenSource = null; + this._documentProvidersChangeListeners = []; - this._register(this._model.onDidChangeContent(e => { - if (!this._fetchSemanticTokens.isScheduled()) { - this._fetchSemanticTokens.schedule(); + this._register(this._model.onDidChangeContent(() => { + if (!this._fetchDocumentSemanticTokens.isScheduled()) { + this._fetchDocumentSemanticTokens.schedule(); } })); - this._register(DocumentSemanticTokensProviderRegistry.onDidChange(e => this._fetchSemanticTokens.schedule())); - if (themeService) { - // workaround for tests which use undefined... :/ - this._register(themeService.onThemeChange(_ => { - // clear out existing tokens - this._setSemanticTokens(null, null, null, []); - this._fetchSemanticTokens.schedule(); - })); - } - this._fetchSemanticTokens.schedule(0); + const bindDocumentChangeListeners = () => { + dispose(this._documentProvidersChangeListeners); + this._documentProvidersChangeListeners = []; + for (const provider of DocumentSemanticTokensProviderRegistry.all(model)) { + if (typeof provider.onDidChange === 'function') { + this._documentProvidersChangeListeners.push(provider.onDidChange(() => this._fetchDocumentSemanticTokens.schedule(0))); + } + } + }; + bindDocumentChangeListeners(); + this._register(DocumentSemanticTokensProviderRegistry.onDidChange(() => { + bindDocumentChangeListeners(); + this._fetchDocumentSemanticTokens.schedule(); + })); + + this._register(themeService.onDidColorThemeChange(_ => { + // clear out existing tokens + this._setDocumentSemanticTokens(null, null, null, []); + this._fetchDocumentSemanticTokens.schedule(); + })); + + this._fetchDocumentSemanticTokens.schedule(0); } public dispose(): void { - if (this._currentResponse) { - this._currentResponse.dispose(); - this._currentResponse = null; + if (this._currentDocumentResponse) { + this._currentDocumentResponse.dispose(); + this._currentDocumentResponse = null; } - if (this._currentRequestCancellationTokenSource) { - this._currentRequestCancellationTokenSource.cancel(); - this._currentRequestCancellationTokenSource = null; + if (this._currentDocumentRequestCancellationTokenSource) { + this._currentDocumentRequestCancellationTokenSource.cancel(); + this._currentDocumentRequestCancellationTokenSource = null; } - this._setSemanticTokens(null, null, null, []); + this._setDocumentSemanticTokens(null, null, null, []); this._isDisposed = true; super.dispose(); } - private _fetchSemanticTokensNow(): void { - if (this._currentRequestCancellationTokenSource) { + private _fetchDocumentSemanticTokensNow(): void { + if (this._currentDocumentRequestCancellationTokenSource) { // there is already a request running, let it finish... return; } @@ -746,7 +792,7 @@ class ModelSemanticColoring extends Disposable { if (!provider) { return; } - this._currentRequestCancellationTokenSource = new CancellationTokenSource(); + this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource(); const pendingChanges: IModelContentChangedEvent[] = []; const contentChangeListener = this._model.onDidChangeContent((e) => { @@ -755,18 +801,29 @@ class ModelSemanticColoring extends Disposable { const styling = this._semanticStyling.get(provider); - const lastResultId = this._currentResponse ? this._currentResponse.resultId || null : null; - const request = Promise.resolve(provider.provideDocumentSemanticTokens(this._model, lastResultId, this._currentRequestCancellationTokenSource.token)); + const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null; + const request = Promise.resolve(provider.provideDocumentSemanticTokens(this._model, lastResultId, this._currentDocumentRequestCancellationTokenSource.token)); request.then((res) => { - this._currentRequestCancellationTokenSource = null; + this._currentDocumentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(provider, res || null, styling, pendingChanges); + this._setDocumentSemanticTokens(provider, res || null, styling, pendingChanges); }, (err) => { - errors.onUnexpectedError(err); - this._currentRequestCancellationTokenSource = null; + if (!err || typeof err.message !== 'string' || err.message.indexOf('busy') === -1) { + errors.onUnexpectedError(err); + } + + // Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available + // The API does not have a special error kind to express this... + this._currentDocumentRequestCancellationTokenSource = null; contentChangeListener.dispose(); - this._setSemanticTokens(provider, null, styling, pendingChanges); + + if (pendingChanges.length > 0) { + // More changes occurred while the request was running + if (!this._fetchDocumentSemanticTokens.isScheduled()) { + this._fetchDocumentSemanticTokens.schedule(); + } + } }); } @@ -784,11 +841,11 @@ class ModelSemanticColoring extends Disposable { } } - private _setSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticColoringProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void { - const currentResponse = this._currentResponse; - if (this._currentResponse) { - this._currentResponse.dispose(); - this._currentResponse = null; + private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticTokensProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void { + const currentResponse = this._currentDocumentResponse; + if (this._currentDocumentResponse) { + this._currentDocumentResponse.dispose(); + this._currentDocumentResponse = null; } if (this._isDisposed) { // disposed! @@ -797,15 +854,19 @@ class ModelSemanticColoring extends Disposable { } return; } - if (!provider || !tokens || !styling) { - this._model.setSemanticTokens(null); + if (!provider || !styling) { + this._model.setSemanticTokens(null, false); + return; + } + if (!tokens) { + this._model.setSemanticTokens(null, true); return; } if (ModelSemanticColoring._isSemanticTokensEdits(tokens)) { if (!currentResponse) { // not possible! - this._model.setSemanticTokens(null); + this._model.setSemanticTokens(null, true); return; } if (tokens.edits.length === 0) { @@ -855,78 +916,9 @@ class ModelSemanticColoring extends Disposable { if (ModelSemanticColoring._isSemanticTokens(tokens)) { - this._currentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data); + this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data); - const srcData = tokens.data; - const tokenCount = (tokens.data.length / 5) | 0; - const tokensPerArea = Math.max(Math.ceil(tokenCount / SemanticColoringConstants.DesiredMaxAreas), SemanticColoringConstants.DesiredTokensPerArea); - - const result: MultilineTokens2[] = []; - - let tokenIndex = 0; - let lastLineNumber = 1; - let lastStartCharacter = 0; - while (tokenIndex < tokenCount) { - const tokenStartIndex = tokenIndex; - let tokenEndIndex = Math.min(tokenStartIndex + tokensPerArea, tokenCount); - - // Keep tokens on the same line in the same area... - if (tokenEndIndex < tokenCount) { - - let smallTokenEndIndex = tokenEndIndex; - while (smallTokenEndIndex - 1 > tokenStartIndex && srcData[5 * smallTokenEndIndex] === 0) { - smallTokenEndIndex--; - } - - if (smallTokenEndIndex - 1 === tokenStartIndex) { - // there are so many tokens on this line that our area would be empty, we must now go right - let bigTokenEndIndex = tokenEndIndex; - while (bigTokenEndIndex + 1 < tokenCount && srcData[5 * bigTokenEndIndex] === 0) { - bigTokenEndIndex++; - } - tokenEndIndex = bigTokenEndIndex; - } else { - tokenEndIndex = smallTokenEndIndex; - } - } - - let destData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 4); - let destOffset = 0; - let areaLine = 0; - while (tokenIndex < tokenEndIndex) { - const srcOffset = 5 * tokenIndex; - const deltaLine = srcData[srcOffset]; - const deltaCharacter = srcData[srcOffset + 1]; - const lineNumber = lastLineNumber + deltaLine; - const startCharacter = (deltaLine === 0 ? lastStartCharacter + deltaCharacter : deltaCharacter); - const length = srcData[srcOffset + 2]; - const tokenTypeIndex = srcData[srcOffset + 3]; - const tokenModifierSet = srcData[srcOffset + 4]; - const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet); - - if (metadata !== Constants.NO_STYLING) { - if (areaLine === 0) { - areaLine = lineNumber; - } - destData[destOffset] = lineNumber - areaLine; - destData[destOffset + 1] = startCharacter; - destData[destOffset + 2] = startCharacter + length; - destData[destOffset + 3] = metadata; - destOffset += 4; - } - - lastLineNumber = lineNumber; - lastStartCharacter = startCharacter; - tokenIndex++; - } - - if (destOffset !== destData.length) { - destData = destData.subarray(0, destOffset); - } - - const tokens = new MultilineTokens2(areaLine, new SparseEncodedTokens(destData)); - result.push(tokens); - } + const result = toMultilineTokens2(tokens, styling, this._model.getLanguageIdentifier()); // Adjust incoming semantic tokens if (pendingChanges.length > 0) { @@ -942,16 +934,16 @@ class ModelSemanticColoring extends Disposable { } } - if (!this._fetchSemanticTokens.isScheduled()) { - this._fetchSemanticTokens.schedule(); + if (!this._fetchDocumentSemanticTokens.isScheduled()) { + this._fetchDocumentSemanticTokens.schedule(); } } - this._model.setSemanticTokens(result); + this._model.setSemanticTokens(result, true); return; } - this._model.setSemanticTokens(null); + this._model.setSemanticTokens(null, true); } private _getSemanticColoringProvider(): DocumentSemanticTokensProvider | null { diff --git a/src/vs/editor/common/services/modelUndoRedoParticipant.ts b/src/vs/editor/common/services/modelUndoRedoParticipant.ts new file mode 100644 index 00000000000..6a03af9e80f --- /dev/null +++ b/src/vs/editor/common/services/modelUndoRedoParticipant.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoDelegate, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; + +export class ModelUndoRedoParticipant extends Disposable implements IUndoRedoDelegate { + constructor( + @IModelService private readonly _modelService: IModelService, + @ITextModelService private readonly _textModelService: ITextModelService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, + ) { + super(); + this._register(this._modelService.onModelRemoved((model) => { + // a model will get disposed, so let's check if the undo redo stack is maintained + const elements = this._undoRedoService.getElements(model.uri); + if (elements.past.length === 0 && elements.future.length === 0) { + return; + } + for (const element of elements.past) { + if (element instanceof MultiModelEditStackElement) { + element.setDelegate(this); + } + } + for (const element of elements.future) { + if (element instanceof MultiModelEditStackElement) { + element.setDelegate(this); + } + } + })); + } + + public prepareUndoRedo(element: MultiModelEditStackElement): IDisposable | Promise { + // Load all the needed text models + const missingModels = element.getMissingModels(); + if (missingModels.length === 0) { + // All models are available! + return Disposable.None; + } + + const disposablesPromises = missingModels.map(async (uri) => { + try { + const reference = await this._textModelService.createModelReference(uri); + return reference; + } catch (err) { + // This model could not be loaded, maybe it was deleted in the meantime? + return Disposable.None; + } + }); + + return Promise.all(disposablesPromises).then(disposables => { + return { + dispose: () => dispose(disposables) + }; + }); + } +} diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 236bc5ef3ff..74470a81017 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -12,7 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const ITextModelService = createDecorator('textModelService'); export interface ITextModelService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Provided a resource URI, it will return a model reference @@ -26,9 +26,9 @@ export interface ITextModelService { registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable; /** - * Check if a provider for the given `scheme` exists + * Check if the given resource can be resolved to a text model. */ - hasTextModelContentProvider(scheme: string): boolean; + canHandleResource(resource: URI): boolean; } export interface ITextModelContentProvider { @@ -61,6 +61,11 @@ export interface ITextEditorModel extends IEditorModel { * Figure out if this model is resolved or not. */ isResolved(): this is IResolvedTextEditorModel; + + /** + * The mode id of the text model if known. + */ + getMode(): string | undefined; } export interface IResolvedTextEditorModel extends ITextEditorModel { diff --git a/src/vs/editor/common/services/semanticTokensProviderStyling.ts b/src/vs/editor/common/services/semanticTokensProviderStyling.ts new file mode 100644 index 00000000000..e29d8f86e81 --- /dev/null +++ b/src/vs/editor/common/services/semanticTokensProviderStyling.ts @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SemanticTokensLegend, TokenMetadata, FontStyle, MetadataConsts, SemanticTokens, LanguageIdentifier } from 'vs/editor/common/modes'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { MultilineTokens2, SparseEncodedTokens } from 'vs/editor/common/model/tokensStore'; + +export const enum SemanticTokensProviderStylingConstants { + NO_STYLING = 0b01111111111111111111111111111111 +} + +export class SemanticTokensProviderStyling { + + private readonly _hashTable: HashTable; + + constructor( + private readonly _legend: SemanticTokensLegend, + private readonly _themeService: IThemeService, + private readonly _logService: ILogService + ) { + this._hashTable = new HashTable(); + } + + public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: LanguageIdentifier): number { + const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet, languageId.id); + let metadata: number; + if (entry) { + metadata = entry.metadata; + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace(`SemanticTokensProviderStyling [CACHED] ${tokenTypeIndex} / ${tokenModifierSet}: foreground ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`); + } + } else { + let tokenType = this._legend.tokenTypes[tokenTypeIndex]; + const tokenModifiers: string[] = []; + if (tokenType) { + let modifierSet = tokenModifierSet; + for (let modifierIndex = 0; modifierSet > 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) { + if (modifierSet & 1) { + tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]); + } + modifierSet = modifierSet >> 1; + } + if (modifierSet > 0 && this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace(`SemanticTokensProviderStyling: unknown token modifier index: ${tokenModifierSet.toString(2)} for legend: ${JSON.stringify(this._legend.tokenModifiers)}`); + tokenModifiers.push('not-in-legend'); + } + + const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers, languageId.language); + if (typeof tokenStyle === 'undefined') { + metadata = SemanticTokensProviderStylingConstants.NO_STYLING; + } else { + metadata = 0; + if (typeof tokenStyle.italic !== 'undefined') { + const italicBit = (tokenStyle.italic ? FontStyle.Italic : 0) << MetadataConsts.FONT_STYLE_OFFSET; + metadata |= italicBit | MetadataConsts.SEMANTIC_USE_ITALIC; + } + if (typeof tokenStyle.bold !== 'undefined') { + const boldBit = (tokenStyle.bold ? FontStyle.Bold : 0) << MetadataConsts.FONT_STYLE_OFFSET; + metadata |= boldBit | MetadataConsts.SEMANTIC_USE_BOLD; + } + if (typeof tokenStyle.underline !== 'undefined') { + const underlineBit = (tokenStyle.underline ? FontStyle.Underline : 0) << MetadataConsts.FONT_STYLE_OFFSET; + metadata |= underlineBit | MetadataConsts.SEMANTIC_USE_UNDERLINE; + } + if (tokenStyle.foreground) { + const foregroundBits = (tokenStyle.foreground) << MetadataConsts.FOREGROUND_OFFSET; + metadata |= foregroundBits | MetadataConsts.SEMANTIC_USE_FOREGROUND; + } + if (metadata === 0) { + // Nothing! + metadata = SemanticTokensProviderStylingConstants.NO_STYLING; + } + } + } else { + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace(`SemanticTokensProviderStyling: unknown token type index: ${tokenTypeIndex} for legend: ${JSON.stringify(this._legend.tokenTypes)}`); + } + metadata = SemanticTokensProviderStylingConstants.NO_STYLING; + tokenType = 'not-in-legend'; + } + this._hashTable.add(tokenTypeIndex, tokenModifierSet, languageId.id, metadata); + + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace(`SemanticTokensProviderStyling ${tokenTypeIndex} (${tokenType}) / ${tokenModifierSet} (${tokenModifiers.join(' ')}): foreground ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`); + } + } + + return metadata; + } +} + +const enum SemanticColoringConstants { + /** + * Let's aim at having 8KB buffers if possible... + * So that would be 8192 / (5 * 4) = 409.6 tokens per area + */ + DesiredTokensPerArea = 400, + + /** + * Try to keep the total number of areas under 1024 if possible, + * simply compensate by having more tokens per area... + */ + DesiredMaxAreas = 1024, +} + +export function toMultilineTokens2(tokens: SemanticTokens, styling: SemanticTokensProviderStyling, languageId: LanguageIdentifier): MultilineTokens2[] { + const srcData = tokens.data; + const tokenCount = (tokens.data.length / 5) | 0; + const tokensPerArea = Math.max(Math.ceil(tokenCount / SemanticColoringConstants.DesiredMaxAreas), SemanticColoringConstants.DesiredTokensPerArea); + const result: MultilineTokens2[] = []; + + let tokenIndex = 0; + let lastLineNumber = 1; + let lastStartCharacter = 0; + while (tokenIndex < tokenCount) { + const tokenStartIndex = tokenIndex; + let tokenEndIndex = Math.min(tokenStartIndex + tokensPerArea, tokenCount); + + // Keep tokens on the same line in the same area... + if (tokenEndIndex < tokenCount) { + + let smallTokenEndIndex = tokenEndIndex; + while (smallTokenEndIndex - 1 > tokenStartIndex && srcData[5 * smallTokenEndIndex] === 0) { + smallTokenEndIndex--; + } + + if (smallTokenEndIndex - 1 === tokenStartIndex) { + // there are so many tokens on this line that our area would be empty, we must now go right + let bigTokenEndIndex = tokenEndIndex; + while (bigTokenEndIndex + 1 < tokenCount && srcData[5 * bigTokenEndIndex] === 0) { + bigTokenEndIndex++; + } + tokenEndIndex = bigTokenEndIndex; + } else { + tokenEndIndex = smallTokenEndIndex; + } + } + + let destData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 4); + let destOffset = 0; + let areaLine = 0; + while (tokenIndex < tokenEndIndex) { + const srcOffset = 5 * tokenIndex; + const deltaLine = srcData[srcOffset]; + const deltaCharacter = srcData[srcOffset + 1]; + const lineNumber = lastLineNumber + deltaLine; + const startCharacter = (deltaLine === 0 ? lastStartCharacter + deltaCharacter : deltaCharacter); + const length = srcData[srcOffset + 2]; + const tokenTypeIndex = srcData[srcOffset + 3]; + const tokenModifierSet = srcData[srcOffset + 4]; + const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId); + + if (metadata !== SemanticTokensProviderStylingConstants.NO_STYLING) { + if (areaLine === 0) { + areaLine = lineNumber; + } + destData[destOffset] = lineNumber - areaLine; + destData[destOffset + 1] = startCharacter; + destData[destOffset + 2] = startCharacter + length; + destData[destOffset + 3] = metadata; + destOffset += 4; + } + + lastLineNumber = lineNumber; + lastStartCharacter = startCharacter; + tokenIndex++; + } + + if (destOffset !== destData.length) { + destData = destData.subarray(0, destOffset); + } + + const tokens = new MultilineTokens2(areaLine, new SparseEncodedTokens(destData)); + result.push(tokens); + } + + return result; +} + +class HashTableEntry { + public readonly tokenTypeIndex: number; + public readonly tokenModifierSet: number; + public readonly languageId: number; + public readonly metadata: number; + public next: HashTableEntry | null; + + constructor(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number) { + this.tokenTypeIndex = tokenTypeIndex; + this.tokenModifierSet = tokenModifierSet; + this.languageId = languageId; + this.metadata = metadata; + this.next = null; + } +} + +class HashTable { + + private static _SIZES = [3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143]; + + private _elementsCount: number; + private _currentLengthIndex: number; + private _currentLength: number; + private _growCount: number; + private _elements: (HashTableEntry | null)[]; + + constructor() { + this._elementsCount = 0; + this._currentLengthIndex = 0; + this._currentLength = HashTable._SIZES[this._currentLengthIndex]; + this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0); + this._elements = []; + HashTable._nullOutEntries(this._elements, this._currentLength); + } + + private static _nullOutEntries(entries: (HashTableEntry | null)[], length: number): void { + for (let i = 0; i < length; i++) { + entries[i] = null; + } + } + + private _hash2(n1: number, n2: number): number { + return (((n1 << 5) - n1) + n2) | 0; // n1 * 31 + n2, keep as int32 + } + + private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): number { + return this._hash2(this._hash2(tokenTypeIndex, tokenModifierSet), languageId) % this._currentLength; + } + + public get(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): HashTableEntry | null { + const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet, languageId); + + let p = this._elements[hash]; + while (p) { + if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet && p.languageId === languageId) { + return p; + } + p = p.next; + } + + return null; + } + + public add(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number): void { + this._elementsCount++; + if (this._growCount !== 0 && this._elementsCount >= this._growCount) { + // expand! + const oldElements = this._elements; + + this._currentLengthIndex++; + this._currentLength = HashTable._SIZES[this._currentLengthIndex]; + this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0); + this._elements = []; + HashTable._nullOutEntries(this._elements, this._currentLength); + + for (const first of oldElements) { + let p = first; + while (p) { + const oldNext = p.next; + p.next = null; + this._add(p); + p = oldNext; + } + } + } + this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, languageId, metadata)); + } + + private _add(element: HashTableEntry): void { + const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet, element.languageId); + element.next = this._elements[hash]; + this._elements[hash] = element; + } +} diff --git a/src/vs/editor/common/services/textResourceConfigurationService.ts b/src/vs/editor/common/services/textResourceConfigurationService.ts index d83fc390eef..23a2335b6df 100644 --- a/src/vs/editor/common/services/textResourceConfigurationService.ts +++ b/src/vs/editor/common/services/textResourceConfigurationService.ts @@ -31,7 +31,7 @@ export interface ITextResourceConfigurationChangeEvent { export interface ITextResourceConfigurationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Event that fires when the configuration changes. @@ -70,10 +70,10 @@ export const ITextResourcePropertiesService = createDecorator this._onDidChangeConfiguration.fire(this.toResourceConfigurationChangeEvent(e)))); } - getValue(resource: URI, section?: string): T; - getValue(resource: URI, at?: IPosition, section?: string): T; - getValue(resource: URI, arg2?: any, arg3?: any): T { + getValue(resource: URI | undefined, section?: string): T; + getValue(resource: URI | undefined, at?: IPosition, section?: string): T; + getValue(resource: URI | undefined, arg2?: any, arg3?: any): T { if (typeof arg3 === 'string') { return this._getValue(resource, Position.isIPosition(arg2) ? arg2 : null, arg3); } @@ -98,7 +98,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex return ConfigurationTarget.USER_LOCAL; } - private _getValue(resource: URI, position: IPosition | null, section: string | undefined): T { + private _getValue(resource: URI | undefined, position: IPosition | null, section: string | undefined): T { const language = resource ? this.getLanguage(resource, position) : undefined; if (typeof section === 'undefined') { return this.configurationService.getValue({ resource, overrideIdentifier: language }); diff --git a/src/vs/editor/common/standalone/promise-polyfill/cgmanifest.json b/src/vs/editor/common/standalone/promise-polyfill/cgmanifest.json deleted file mode 100644 index b62e25bccff..00000000000 --- a/src/vs/editor/common/standalone/promise-polyfill/cgmanifest.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "promise-polyfill", - "repositoryUrl": "https://github.com/taylorhakes/promise-polyfill", - "commitHash": "efe662be6ea569c439ec92a4f8662c0a7faf0b96" - } - }, - "license": "MIT", - "version": "8.0.0" - } - ], - "version": 1 -} diff --git a/src/vs/editor/common/standalone/promise-polyfill/polyfill.js b/src/vs/editor/common/standalone/promise-polyfill/polyfill.js deleted file mode 100644 index 4ddfcab7cd0..00000000000 --- a/src/vs/editor/common/standalone/promise-polyfill/polyfill.js +++ /dev/null @@ -1,291 +0,0 @@ -/*! -Copyright (c) 2014 Taylor Hakes -Copyright (c) 2014 Forbes Lindesay - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory() : - typeof define === 'function' && define.amd ? define(factory) : - (factory()); -}(this, (function () { - 'use strict'; - - /** - * @this {Promise} - */ - function finallyConstructor(callback) { - var constructor = this.constructor; - return this.then( - function (value) { - return constructor.resolve(callback()).then(function () { - return value; - }); - }, - function (reason) { - return constructor.resolve(callback()).then(function () { - return constructor.reject(reason); - }); - } - ); - } - - // Store setTimeout reference so promise-polyfill will be unaffected by - // other code modifying setTimeout (like sinon.useFakeTimers()) - var setTimeoutFunc = setTimeout; - - function noop() { } - - // Polyfill for Function.prototype.bind - function bind(fn, thisArg) { - return function () { - fn.apply(thisArg, arguments); - }; - } - - /** - * @constructor - * @param {Function} fn - */ - function Promise(fn) { - if (!(this instanceof Promise)) - throw new TypeError('Promises must be constructed via new'); - if (typeof fn !== 'function') throw new TypeError('not a function'); - /** @type {!number} */ - this._state = 0; - /** @type {!boolean} */ - this._handled = false; - /** @type {Promise|undefined} */ - this._value = undefined; - /** @type {!Array} */ - this._deferreds = []; - - doResolve(fn, this); - } - - function handle(self, deferred) { - while (self._state === 3) { - self = self._value; - } - if (self._state === 0) { - self._deferreds.push(deferred); - return; - } - self._handled = true; - Promise._immediateFn(function () { - var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; - if (cb === null) { - (self._state === 1 ? resolve : reject)(deferred.promise, self._value); - return; - } - var ret; - try { - ret = cb(self._value); - } catch (e) { - reject(deferred.promise, e); - return; - } - resolve(deferred.promise, ret); - }); - } - - function resolve(self, newValue) { - try { - // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure - if (newValue === self) - throw new TypeError('A promise cannot be resolved with itself.'); - if ( - newValue && - (typeof newValue === 'object' || typeof newValue === 'function') - ) { - var then = newValue.then; - if (newValue instanceof Promise) { - self._state = 3; - self._value = newValue; - finale(self); - return; - } else if (typeof then === 'function') { - doResolve(bind(then, newValue), self); - return; - } - } - self._state = 1; - self._value = newValue; - finale(self); - } catch (e) { - reject(self, e); - } - } - - function reject(self, newValue) { - self._state = 2; - self._value = newValue; - finale(self); - } - - function finale(self) { - if (self._state === 2 && self._deferreds.length === 0) { - Promise._immediateFn(function () { - if (!self._handled) { - Promise._unhandledRejectionFn(self._value); - } - }); - } - - for (var i = 0, len = self._deferreds.length; i < len; i++) { - handle(self, self._deferreds[i]); - } - self._deferreds = null; - } - - /** - * @constructor - */ - function Handler(onFulfilled, onRejected, promise) { - this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; - this.onRejected = typeof onRejected === 'function' ? onRejected : null; - this.promise = promise; - } - - /** - * Take a potentially misbehaving resolver function and make sure - * onFulfilled and onRejected are only called once. - * - * Makes no guarantees about asynchrony. - */ - function doResolve(fn, self) { - var done = false; - try { - fn( - function (value) { - if (done) return; - done = true; - resolve(self, value); - }, - function (reason) { - if (done) return; - done = true; - reject(self, reason); - } - ); - } catch (ex) { - if (done) return; - done = true; - reject(self, ex); - } - } - - Promise.prototype['catch'] = function (onRejected) { - return this.then(null, onRejected); - }; - - Promise.prototype.then = function (onFulfilled, onRejected) { - // @ts-ignore - var prom = new this.constructor(noop); - - handle(this, new Handler(onFulfilled, onRejected, prom)); - return prom; - }; - - Promise.prototype['finally'] = finallyConstructor; - - Promise.all = function (arr) { - return new Promise(function (resolve, reject) { - if (!arr || typeof arr.length === 'undefined') - throw new TypeError('Promise.all accepts an array'); - var args = Array.prototype.slice.call(arr); - if (args.length === 0) return resolve([]); - var remaining = args.length; - - function res(i, val) { - try { - if (val && (typeof val === 'object' || typeof val === 'function')) { - var then = val.then; - if (typeof then === 'function') { - then.call( - val, - function (val) { - res(i, val); - }, - reject - ); - return; - } - } - args[i] = val; - if (--remaining === 0) { - resolve(args); - } - } catch (ex) { - reject(ex); - } - } - - for (var i = 0; i < args.length; i++) { - res(i, args[i]); - } - }); - }; - - Promise.resolve = function (value) { - if (value && typeof value === 'object' && value.constructor === Promise) { - return value; - } - - return new Promise(function (resolve) { - resolve(value); - }); - }; - - Promise.reject = function (value) { - return new Promise(function (resolve, reject) { - reject(value); - }); - }; - - Promise.race = function (values) { - return new Promise(function (resolve, reject) { - for (var i = 0, len = values.length; i < len; i++) { - values[i].then(resolve, reject); - } - }); - }; - - // Use polyfill for setImmediate for performance gains - Promise._immediateFn = - (typeof setImmediate === 'function' && - function (fn) { - setImmediate(fn); - }) || - function (fn) { - setTimeoutFunc(fn, 0); - }; - - Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { - if (typeof console !== 'undefined' && console) { - console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console - } - }; - - /** @suppress {undefinedVars} */ - var globalNS = (function () { - // the only reliable means to get the global object is - // `Function('return this')()` - // However, this causes CSP violations in Chrome apps. - if (typeof self !== 'undefined') { - return self; - } - if (typeof window !== 'undefined') { - return window; - } - if (typeof global !== 'undefined') { - return global; - } - throw new Error('unable to locate global object'); - })(); - - if (!('Promise' in globalNS)) { - globalNS['Promise'] = Promise; - } else if (!globalNS.Promise.prototype['finally']) { - globalNS.Promise.prototype['finally'] = finallyConstructor; - } - -}))); diff --git a/src/vs/editor/common/standalone/standaloneBase.ts b/src/vs/editor/common/standalone/standaloneBase.ts index 377b5185c28..2239e8d0234 100644 --- a/src/vs/editor/common/standalone/standaloneBase.ts +++ b/src/vs/editor/common/standalone/standaloneBase.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/editor/common/standalone/promise-polyfill/polyfill'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { KeyChord, KeyMod as ConstKeyMod } from 'vs/base/common/keyCodes'; diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 505779e7db8..91c07a481a7 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -53,7 +53,9 @@ export enum CompletionItemKind { Customcolor = 22, Folder = 23, TypeParameter = 24, - Snippet = 25 + User = 25, + Issue = 26, + Snippet = 27 } export enum CompletionItemTag { @@ -178,103 +180,113 @@ export enum EditorOption { autoSurround = 10, codeLens = 11, colorDecorators = 12, - comments = 13, - contextmenu = 14, - copyWithSyntaxHighlighting = 15, - cursorBlinking = 16, - cursorSmoothCaretAnimation = 17, - cursorStyle = 18, - cursorSurroundingLines = 19, - cursorSurroundingLinesStyle = 20, - cursorWidth = 21, - disableLayerHinting = 22, - disableMonospaceOptimizations = 23, - dragAndDrop = 24, - emptySelectionClipboard = 25, - extraEditorClassName = 26, - fastScrollSensitivity = 27, - find = 28, - fixedOverflowWidgets = 29, - folding = 30, - foldingStrategy = 31, - foldingHighlight = 32, - fontFamily = 33, - fontInfo = 34, - fontLigatures = 35, - fontSize = 36, - fontWeight = 37, - formatOnPaste = 38, - formatOnType = 39, - glyphMargin = 40, - gotoLocation = 41, - hideCursorInOverviewRuler = 42, - highlightActiveIndentGuide = 43, - hover = 44, - inDiffEditor = 45, - letterSpacing = 46, - lightbulb = 47, - lineDecorationsWidth = 48, - lineHeight = 49, - lineNumbers = 50, - lineNumbersMinChars = 51, - links = 52, - matchBrackets = 53, - minimap = 54, - mouseStyle = 55, - mouseWheelScrollSensitivity = 56, - mouseWheelZoom = 57, - multiCursorMergeOverlapping = 58, - multiCursorModifier = 59, - multiCursorPaste = 60, - occurrencesHighlight = 61, - overviewRulerBorder = 62, - overviewRulerLanes = 63, - parameterHints = 64, - peekWidgetDefaultFocus = 65, - quickSuggestions = 66, - quickSuggestionsDelay = 67, - readOnly = 68, - renderControlCharacters = 69, - renderIndentGuides = 70, - renderFinalNewline = 71, - renderLineHighlight = 72, - renderValidationDecorations = 73, - renderWhitespace = 74, - revealHorizontalRightPadding = 75, - roundedSelection = 76, - rulers = 77, - scrollbar = 78, - scrollBeyondLastColumn = 79, - scrollBeyondLastLine = 80, - selectionClipboard = 81, - selectionHighlight = 82, - selectOnLineNumbers = 83, - semanticHighlighting = 84, - showFoldingControls = 85, - showUnused = 86, - snippetSuggestions = 87, - smoothScrolling = 88, - stopRenderingLineAfter = 89, - suggest = 90, - suggestFontSize = 91, - suggestLineHeight = 92, - suggestOnTriggerCharacters = 93, - suggestSelection = 94, - tabCompletion = 95, - useTabStops = 96, - wordSeparators = 97, - wordWrap = 98, - wordWrapBreakAfterCharacters = 99, - wordWrapBreakBeforeCharacters = 100, - wordWrapColumn = 101, - wordWrapMinified = 102, - wrappingIndent = 103, - wrappingAlgorithm = 104, - editorClassName = 105, - pixelRatio = 106, - tabFocusMode = 107, - layoutInfo = 108, - wrappingInfo = 109 + columnSelection = 13, + comments = 14, + contextmenu = 15, + copyWithSyntaxHighlighting = 16, + cursorBlinking = 17, + cursorSmoothCaretAnimation = 18, + cursorStyle = 19, + cursorSurroundingLines = 20, + cursorSurroundingLinesStyle = 21, + cursorWidth = 22, + disableLayerHinting = 23, + disableMonospaceOptimizations = 24, + dragAndDrop = 25, + emptySelectionClipboard = 26, + extraEditorClassName = 27, + fastScrollSensitivity = 28, + find = 29, + fixedOverflowWidgets = 30, + folding = 31, + foldingStrategy = 32, + foldingHighlight = 33, + unfoldOnClickAfterEndOfLine = 34, + fontFamily = 35, + fontInfo = 36, + fontLigatures = 37, + fontSize = 38, + fontWeight = 39, + formatOnPaste = 40, + formatOnType = 41, + glyphMargin = 42, + gotoLocation = 43, + hideCursorInOverviewRuler = 44, + highlightActiveIndentGuide = 45, + hover = 46, + inDiffEditor = 47, + letterSpacing = 48, + lightbulb = 49, + lineDecorationsWidth = 50, + lineHeight = 51, + lineNumbers = 52, + lineNumbersMinChars = 53, + links = 54, + matchBrackets = 55, + minimap = 56, + mouseStyle = 57, + mouseWheelScrollSensitivity = 58, + mouseWheelZoom = 59, + multiCursorMergeOverlapping = 60, + multiCursorModifier = 61, + multiCursorPaste = 62, + occurrencesHighlight = 63, + overviewRulerBorder = 64, + overviewRulerLanes = 65, + padding = 66, + parameterHints = 67, + peekWidgetDefaultFocus = 68, + definitionLinkOpensInPeek = 69, + quickSuggestions = 70, + quickSuggestionsDelay = 71, + readOnly = 72, + renameOnType = 73, + renderControlCharacters = 74, + renderIndentGuides = 75, + renderFinalNewline = 76, + renderLineHighlight = 77, + renderLineHighlightOnlyWhenFocus = 78, + renderValidationDecorations = 79, + renderWhitespace = 80, + revealHorizontalRightPadding = 81, + roundedSelection = 82, + rulers = 83, + scrollbar = 84, + scrollBeyondLastColumn = 85, + scrollBeyondLastLine = 86, + scrollPredominantAxis = 87, + selectionClipboard = 88, + selectionHighlight = 89, + selectOnLineNumbers = 90, + showFoldingControls = 91, + showUnused = 92, + snippetSuggestions = 93, + smartSelect = 94, + smoothScrolling = 95, + stopRenderingLineAfter = 96, + suggest = 97, + suggestFontSize = 98, + suggestLineHeight = 99, + suggestOnTriggerCharacters = 100, + suggestSelection = 101, + tabCompletion = 102, + tabIndex = 103, + unusualLineTerminators = 104, + useTabStops = 105, + wordSeparators = 106, + wordWrap = 107, + wordWrapBreakAfterCharacters = 108, + wordWrapBreakBeforeCharacters = 109, + wordWrapColumn = 110, + wordWrapMinified = 111, + wrappingIndent = 112, + wrappingStrategy = 113, + showDeprecated = 114, + editorClassName = 115, + pixelRatio = 116, + tabFocusMode = 117, + layoutInfo = 118, + wrappingInfo = 119 } /** diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index f0c107c0cbe..88104a63373 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -36,42 +36,25 @@ export namespace InspectTokensNLS { } export namespace GoToLineNLS { - export const gotoLineLabelValidLineAndColumn = nls.localize('gotoLineLabelValidLineAndColumn', "Go to line {0} and character {1}"); - export const gotoLineLabelValidLine = nls.localize('gotoLineLabelValidLine', "Go to line {0}"); - export const gotoLineLabelEmptyWithLineLimit = nls.localize('gotoLineLabelEmptyWithLineLimit', "Type a line number between 1 and {0} to navigate to"); - export const gotoLineLabelEmptyWithLineAndColumnLimit = nls.localize('gotoLineLabelEmptyWithLineAndColumnLimit', "Type a character between 1 and {0} to navigate to"); - export const gotoLineAriaLabel = nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {1}."); - export const gotoLineActionInput = nls.localize('gotoLineActionInput', "Type a line number, followed by an optional colon and a character number to navigate to"); - export const gotoLineActionLabel = nls.localize('gotoLineActionLabel', "Go to Line..."); + export const gotoLineActionLabel = nls.localize('gotoLineActionLabel', "Go to Line/Column..."); +} + +export namespace QuickHelpNLS { + export const helpQuickAccessActionLabel = nls.localize('helpQuickAccess', "Show all Quick Access Providers"); } export namespace QuickCommandNLS { - export const ariaLabelEntryWithKey = nls.localize('ariaLabelEntryWithKey', "{0}, {1}, commands"); - export const ariaLabelEntry = nls.localize('ariaLabelEntry', "{0}, commands"); - export const quickCommandActionInput = nls.localize('quickCommandActionInput', "Type the name of an action you want to execute"); export const quickCommandActionLabel = nls.localize('quickCommandActionLabel', "Command Palette"); + export const quickCommandHelp = nls.localize('quickCommandActionHelp', "Show And Run Commands"); } export namespace QuickOutlineNLS { - export const entryAriaLabel = nls.localize('entryAriaLabel', "{0}, symbols"); - export const quickOutlineActionInput = nls.localize('quickOutlineActionInput', "Type the name of an identifier you wish to navigate to"); export const quickOutlineActionLabel = nls.localize('quickOutlineActionLabel', "Go to Symbol..."); - export const _symbols_ = nls.localize('symbols', "symbols ({0})"); - export const _modules_ = nls.localize('modules', "modules ({0})"); - export const _class_ = nls.localize('class', "classes ({0})"); - export const _interface_ = nls.localize('interface', "interfaces ({0})"); - export const _method_ = nls.localize('method', "methods ({0})"); - export const _function_ = nls.localize('function', "functions ({0})"); - export const _property_ = nls.localize('property', "properties ({0})"); - export const _variable_ = nls.localize('variable', "variables ({0})"); - export const _variable2_ = nls.localize('variable2', "variables ({0})"); - export const _constructor_ = nls.localize('_constructor', "constructors ({0})"); - export const _call_ = nls.localize('call', "calls ({0})"); + export const quickOutlineByCategoryActionLabel = nls.localize('quickOutlineByCategoryActionLabel', "Go to Symbol by Category..."); } export namespace StandaloneCodeEditorNLS { export const editorViewAccessibleLabel = nls.localize('editorViewAccessibleLabel', "Editor content"); - export const accessibilityHelpMessageIE = nls.localize('accessibilityHelpMessageIE', "Press Ctrl+F1 for Accessibility Options."); export const accessibilityHelpMessage = nls.localize('accessibilityHelpMessage', "Press Alt+F1 for Accessibility Options."); } diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index fbdb785c49b..89c3c90dc59 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -30,12 +30,13 @@ export const editorActiveLineNumber = registerColor('editorLineNumber.activeFore export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hc: Color.white }, nls.localize('editorRuler', 'Color of the editor rulers.')); -export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#999999', hc: '#999999' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor code lenses')); +export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#999999', hc: '#999999' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor CodeLens')); export const editorBracketMatchBackground = registerColor('editorBracketMatch.background', { dark: '#0064001a', light: '#0064001a', hc: '#0064001a' }, nls.localize('editorBracketMatchBackground', 'Background color behind matching brackets')); export const editorBracketMatchBorder = registerColor('editorBracketMatch.border', { dark: '#888', light: '#B9B9B9', hc: contrastBorder }, nls.localize('editorBracketMatchBorder', 'Color for matching brackets boxes')); export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.border', { dark: '#7f7f7f4d', light: '#7f7f7f4d', hc: '#7f7f7f4d' }, nls.localize('editorOverviewRulerBorder', 'Color of the overview ruler border.')); +export const editorOverviewRulerBackground = registerColor('editorOverviewRuler.background', null, nls.localize('editorOverviewRulerBackground', 'Background color of the editor overview ruler. Only used when the minimap is enabled and placed on the right side of the editor.')); export const editorGutter = registerColor('editorGutter.background', { dark: editorBackground, light: editorBackground, hc: editorBackground }, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); @@ -87,6 +88,7 @@ registerThemingParticipant((theme, collector) => { const invisibles = theme.getColor(editorWhitespaces); if (invisibles) { - collector.addRule(`.vs-whitespace { color: ${invisibles} !important; }`); + collector.addRule(`.monaco-editor .mtkw { color: ${invisibles} !important; }`); + collector.addRule(`.monaco-editor .mtkz { color: ${invisibles} !important; }`); } }); diff --git a/src/vs/editor/common/view/viewContext.ts b/src/vs/editor/common/view/viewContext.ts index 5f4c9be5889..b888fbec6ad 100644 --- a/src/vs/editor/common/view/viewContext.ts +++ b/src/vs/editor/common/view/viewContext.ts @@ -4,38 +4,57 @@ *--------------------------------------------------------------------------------------------*/ import { IConfiguration } from 'vs/editor/common/editorCommon'; -import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { ITheme } from 'vs/platform/theme/common/themeService'; +import { IColorTheme } from 'vs/platform/theme/common/themeService'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; + +export class EditorTheme { + + private _theme: IColorTheme; + + public get type(): ColorScheme { + return this._theme.type; + } + + constructor(theme: IColorTheme) { + this._theme = theme; + } + + public update(theme: IColorTheme): void { + this._theme = theme; + } + + public getColor(color: ColorIdentifier): Color | undefined { + return this._theme.getColor(color); + } +} export class ViewContext { public readonly configuration: IConfiguration; public readonly model: IViewModel; public readonly viewLayout: IViewLayout; - public readonly privateViewEventBus: ViewEventDispatcher; - - public theme: ITheme; // will be updated + public readonly theme: EditorTheme; constructor( configuration: IConfiguration, - theme: ITheme, - model: IViewModel, - privateViewEventBus: ViewEventDispatcher + theme: IColorTheme, + model: IViewModel ) { this.configuration = configuration; - this.theme = theme; + this.theme = new EditorTheme(theme); this.model = model; this.viewLayout = model.viewLayout; - this.privateViewEventBus = privateViewEventBus; } public addEventHandler(eventHandler: ViewEventHandler): void { - this.privateViewEventBus.addEventHandler(eventHandler); + this.model.addViewEventHandler(eventHandler); } public removeEventHandler(eventHandler: ViewEventHandler): void { - this.privateViewEventBus.removeEventHandler(eventHandler); + this.model.removeViewEventHandler(eventHandler); } } diff --git a/src/vs/editor/common/view/viewEventDispatcher.ts b/src/vs/editor/common/view/viewEventDispatcher.ts deleted file mode 100644 index 54bd7a6e986..00000000000 --- a/src/vs/editor/common/view/viewEventDispatcher.ts +++ /dev/null @@ -1,92 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ViewEvent } from 'vs/editor/common/view/viewEvents'; -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; - -export class ViewEventDispatcher { - - private readonly _eventHandlerGateKeeper: (callback: () => void) => void; - private readonly _eventHandlers: ViewEventHandler[]; - private _eventQueue: ViewEvent[] | null; - private _isConsumingQueue: boolean; - - constructor(eventHandlerGateKeeper: (callback: () => void) => void) { - this._eventHandlerGateKeeper = eventHandlerGateKeeper; - this._eventHandlers = []; - this._eventQueue = null; - this._isConsumingQueue = false; - } - - public addEventHandler(eventHandler: ViewEventHandler): void { - for (let i = 0, len = this._eventHandlers.length; i < len; i++) { - if (this._eventHandlers[i] === eventHandler) { - console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler); - } - } - this._eventHandlers.push(eventHandler); - } - - public removeEventHandler(eventHandler: ViewEventHandler): void { - for (let i = 0; i < this._eventHandlers.length; i++) { - if (this._eventHandlers[i] === eventHandler) { - this._eventHandlers.splice(i, 1); - break; - } - } - } - - public emit(event: ViewEvent): void { - - if (this._eventQueue) { - this._eventQueue.push(event); - } else { - this._eventQueue = [event]; - } - - if (!this._isConsumingQueue) { - this.consumeQueue(); - } - } - - public emitMany(events: ViewEvent[]): void { - if (this._eventQueue) { - this._eventQueue = this._eventQueue.concat(events); - } else { - this._eventQueue = events; - } - - if (!this._isConsumingQueue) { - this.consumeQueue(); - } - } - - private consumeQueue(): void { - this._eventHandlerGateKeeper(() => { - try { - this._isConsumingQueue = true; - - this._doConsumeQueue(); - - } finally { - this._isConsumingQueue = false; - } - }); - } - - private _doConsumeQueue(): void { - while (this._eventQueue) { - // Empty event queue, as events might come in while sending these off - let events = this._eventQueue; - this._eventQueue = null; - - // Use a clone of the event handlers list, as they might remove themselves - let eventHandlers = this._eventHandlers.slice(0); - for (let i = 0, len = eventHandlers.length; i < len; i++) { - eventHandlers[i].handleEvents(events); - } - } - } -} diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index b72ad327c20..804d1b977f5 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -3,32 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as errors from 'vs/base/common/errors'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; +import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; export const enum ViewEventType { - ViewConfigurationChanged = 1, - ViewContentSizeChanged = 2, - ViewCursorStateChanged = 3, - ViewDecorationsChanged = 4, - ViewFlushed = 5, - ViewFocusChanged = 6, - ViewLanguageConfigurationChanged = 7, - ViewLineMappingChanged = 8, - ViewLinesChanged = 9, - ViewLinesDeleted = 10, - ViewLinesInserted = 11, - ViewRevealRangeRequest = 12, - ViewScrollChanged = 13, - ViewThemeChanged = 14, - ViewTokensChanged = 15, - ViewTokensColorsChanged = 16, - ViewZonesChanged = 17, + ViewConfigurationChanged, + ViewCursorStateChanged, + ViewDecorationsChanged, + ViewFlushed, + ViewFocusChanged, + ViewLanguageConfigurationChanged, + ViewLineMappingChanged, + ViewLinesChanged, + ViewLinesDeleted, + ViewLinesInserted, + ViewRevealRangeRequest, + ViewScrollChanged, + ViewThemeChanged, + ViewTokensChanged, + ViewTokensColorsChanged, + ViewZonesChanged, } export class ViewConfigurationChangedEvent { @@ -46,25 +44,6 @@ export class ViewConfigurationChangedEvent { } } -export class ViewContentSizeChangedEvent implements IContentSizeChangedEvent { - - public readonly type = ViewEventType.ViewContentSizeChanged; - - public readonly contentWidth: number; - public readonly contentHeight: number; - - public readonly contentWidthChanged: boolean; - public readonly contentHeightChanged: boolean; - - constructor(source: IContentSizeChangedEvent) { - this.contentWidth = source.contentWidth; - this.contentHeight = source.contentHeight; - - this.contentWidthChanged = source.contentWidthChanged; - this.contentHeightChanged = source.contentHeightChanged; - } -} - export class ViewCursorStateChangedEvent { public readonly type = ViewEventType.ViewCursorStateChanged; @@ -82,8 +61,17 @@ export class ViewDecorationsChangedEvent { public readonly type = ViewEventType.ViewDecorationsChanged; - constructor() { - // Nothing to do + readonly affectsMinimap: boolean; + readonly affectsOverviewRuler: boolean; + + constructor(source: IModelDecorationsChangedEvent | null) { + if (source) { + this.affectsMinimap = source.affectsMinimap; + this.affectsOverviewRuler = source.affectsOverviewRuler; + } else { + this.affectsMinimap = true; + this.affectsOverviewRuler = true; + } } } @@ -183,7 +171,9 @@ export const enum VerticalRevealType { Center = 1, CenterIfOutsideViewport = 2, Top = 3, - Bottom = 4 + Bottom = 4, + NearTop = 5, + NearTopIfOutsideViewport = 6, } export class ViewRevealRangeRequestEvent { @@ -193,7 +183,12 @@ export class ViewRevealRangeRequestEvent { /** * Range to be reavealed. */ - public readonly range: Range; + public readonly range: Range | null; + + /** + * Selections to be revealed. + */ + public readonly selections: Selection[] | null; public readonly verticalType: VerticalRevealType; /** @@ -207,11 +202,12 @@ export class ViewRevealRangeRequestEvent { /** * Source of the call that caused the event. */ - readonly source: string; + readonly source: string | null | undefined; - constructor(source: string, range: Range, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: ScrollType) { + constructor(source: string | null | undefined, range: Range | null, selections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: ScrollType) { this.source = source; this.range = range; + this.selections = selections; this.verticalType = verticalType; this.revealHorizontal = revealHorizontal; this.scrollType = scrollType; @@ -290,7 +286,6 @@ export class ViewZonesChangedEvent { export type ViewEvent = ( ViewConfigurationChangedEvent - | ViewContentSizeChangedEvent | ViewCursorStateChangedEvent | ViewDecorationsChangedEvent | ViewFlushedEvent @@ -307,94 +302,3 @@ export type ViewEvent = ( | ViewTokensColorsChangedEvent | ViewZonesChangedEvent ); - -export interface IViewEventListener { - (events: ViewEvent[]): void; -} - -export class ViewEventEmitter extends Disposable { - private _listeners: IViewEventListener[]; - private _collector: ViewEventsCollector | null; - private _collectorCnt: number; - - constructor() { - super(); - this._listeners = []; - this._collector = null; - this._collectorCnt = 0; - } - - public dispose(): void { - this._listeners = []; - super.dispose(); - } - - protected _beginEmit(): ViewEventsCollector { - this._collectorCnt++; - if (this._collectorCnt === 1) { - this._collector = new ViewEventsCollector(); - } - return this._collector!; - } - - protected _endEmit(): void { - this._collectorCnt--; - if (this._collectorCnt === 0) { - const events = this._collector!.finalize(); - this._collector = null; - if (events.length > 0) { - this._emit(events); - } - } - } - - private _emit(events: ViewEvent[]): void { - const listeners = this._listeners.slice(0); - for (let i = 0, len = listeners.length; i < len; i++) { - safeInvokeListener(listeners[i], events); - } - } - - public addEventListener(listener: (events: ViewEvent[]) => void): IDisposable { - this._listeners.push(listener); - return toDisposable(() => { - let listeners = this._listeners; - for (let i = 0, len = listeners.length; i < len; i++) { - if (listeners[i] === listener) { - listeners.splice(i, 1); - break; - } - } - }); - } -} - -export class ViewEventsCollector { - - private _events: ViewEvent[]; - private _eventsLen = 0; - - constructor() { - this._events = []; - this._eventsLen = 0; - } - - public emit(event: ViewEvent) { - this._events[this._eventsLen++] = event; - } - - public finalize(): ViewEvent[] { - let result = this._events; - this._events = []; - return result; - } - -} - -function safeInvokeListener(listener: IViewEventListener, events: ViewEvent[]): void { - try { - listener(events); - } catch (e) { - errors.onUnexpectedError(e); - } -} diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 367abd68f82..74428ae28d7 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -6,6 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { Constants } from 'vs/base/common/uint'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { LinePartMetadata } from 'vs/editor/common/viewLayout/viewLineRenderer'; export class LineDecoration { _lineDecorationBrand: void; @@ -28,8 +29,8 @@ export class LineDecoration { } public static equalsArr(a: LineDecoration[], b: LineDecoration[]): boolean { - let aLen = a.length; - let bLen = b.length; + const aLen = a.length; + const bLen = b.length; if (aLen !== bLen) { return false; } @@ -49,8 +50,8 @@ export class LineDecoration { let result: LineDecoration[] = [], resultLen = 0; for (let i = 0, len = lineDecorations.length; i < len; i++) { - let d = lineDecorations[i]; - let range = d.range; + const d = lineDecorations[i]; + const range = d.range; if (range.endLineNumber < lineNumber || range.startLineNumber > lineNumber) { // Ignore decorations that sit outside this line @@ -62,8 +63,8 @@ export class LineDecoration { continue; } - let startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn); - let endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn); + const startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn); + const endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn); result[resultLen++] = new LineDecoration(startColumn, endColumn, d.inlineClassName, d.type); } @@ -71,16 +72,25 @@ export class LineDecoration { return result; } + private static _typeCompare(a: InlineDecorationType, b: InlineDecorationType): number { + const ORDER = [2, 0, 1, 3]; + return ORDER[a] - ORDER[b]; + } + public static compare(a: LineDecoration, b: LineDecoration): number { if (a.startColumn === b.startColumn) { if (a.endColumn === b.endColumn) { - if (a.className < b.className) { - return -1; + const typeCmp = LineDecoration._typeCompare(a.type, b.type); + if (typeCmp === 0) { + if (a.className < b.className) { + return -1; + } + if (a.className > b.className) { + return 1; + } + return 0; } - if (a.className > b.className) { - return 1; - } - return 0; + return typeCmp; } return a.endColumn - b.endColumn; } @@ -92,11 +102,13 @@ export class DecorationSegment { startOffset: number; endOffset: number; className: string; + metadata: number; - constructor(startOffset: number, endOffset: number, className: string) { + constructor(startOffset: number, endOffset: number, className: string, metadata: number) { this.startOffset = startOffset; this.endOffset = endOffset; this.className = className; + this.metadata = metadata; } } @@ -104,13 +116,23 @@ class Stack { public count: number; private readonly stopOffsets: number[]; private readonly classNames: string[]; + private readonly metadata: number[]; constructor() { this.stopOffsets = []; this.classNames = []; + this.metadata = []; this.count = 0; } + private static _metadata(metadata: number[]): number { + let result = 0; + for (let i = 0, len = metadata.length; i < len; i++) { + result |= metadata[i]; + } + return result; + } + public consumeLowerThan(maxStopOffset: number, nextStartOffset: number, result: DecorationSegment[]): number { while (this.count > 0 && this.stopOffsets[0] < maxStopOffset) { @@ -122,34 +144,37 @@ class Stack { } // Basically we are consuming the first i + 1 elements of the stack - result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '))); + result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '), Stack._metadata(this.metadata))); nextStartOffset = this.stopOffsets[i] + 1; // Consume them this.stopOffsets.splice(0, i + 1); this.classNames.splice(0, i + 1); + this.metadata.splice(0, i + 1); this.count -= (i + 1); } if (this.count > 0 && nextStartOffset < maxStopOffset) { - result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '))); + result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '), Stack._metadata(this.metadata))); nextStartOffset = maxStopOffset; } return nextStartOffset; } - public insert(stopOffset: number, className: string): void { + public insert(stopOffset: number, className: string, metadata: number): void { if (this.count === 0 || this.stopOffsets[this.count - 1] <= stopOffset) { // Insert at the end this.stopOffsets.push(stopOffset); this.classNames.push(className); + this.metadata.push(metadata); } else { // Find the insertion position for `stopOffset` for (let i = 0; i < this.count; i++) { if (this.stopOffsets[i] >= stopOffset) { this.stopOffsets.splice(i, 0, stopOffset); this.classNames.splice(i, 0, className); + this.metadata.splice(i, 0, metadata); break; } } @@ -170,14 +195,21 @@ export class LineDecorationsNormalizer { let result: DecorationSegment[] = []; - let stack = new Stack(); + const stack = new Stack(); let nextStartOffset = 0; for (let i = 0, len = lineDecorations.length; i < len; i++) { - let d = lineDecorations[i]; + const d = lineDecorations[i]; let startColumn = d.startColumn; let endColumn = d.endColumn; - let className = d.className; + const className = d.className; + const metadata = ( + d.type === InlineDecorationType.Before + ? LinePartMetadata.PSEUDO_BEFORE + : d.type === InlineDecorationType.After + ? LinePartMetadata.PSEUDO_AFTER + : 0 + ); // If the position would end up in the middle of a high-low surrogate pair, we move it to before the pair if (startColumn > 1) { @@ -194,15 +226,15 @@ export class LineDecorationsNormalizer { } } - let currentStartOffset = startColumn - 1; - let currentEndOffset = endColumn - 2; + const currentStartOffset = startColumn - 1; + const currentEndOffset = endColumn - 2; nextStartOffset = stack.consumeLowerThan(currentStartOffset, nextStartOffset, result); if (stack.count === 0) { nextStartOffset = currentStartOffset; } - stack.insert(currentEndOffset, className); + stack.insert(currentEndOffset, className, metadata); } stack.consumeLowerThan(Constants.MAX_SAFE_SMALL_INTEGER, nextStartOffset, result); diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 90f7335de7a..ceef064de9a 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -111,8 +111,10 @@ export class LinesLayout { private _minWidth: number; private _lineCount: number; private _lineHeight: number; + private _paddingTop: number; + private _paddingBottom: number; - constructor(lineCount: number, lineHeight: number) { + constructor(lineCount: number, lineHeight: number, paddingTop: number, paddingBottom: number) { this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT); this._pendingChanges = new PendingChanges(); this._lastWhitespaceId = 0; @@ -121,6 +123,8 @@ export class LinesLayout { this._minWidth = -1; /* marker for not being computed */ this._lineCount = lineCount; this._lineHeight = lineHeight; + this._paddingTop = paddingTop; + this._paddingBottom = paddingBottom; } /** @@ -158,6 +162,14 @@ export class LinesLayout { this._lineHeight = lineHeight; } + /** + * Changes the padding used to calculate vertical offsets. + */ + public setPadding(paddingTop: number, paddingBottom: number): void { + this._paddingTop = paddingTop; + this._paddingBottom = paddingBottom; + } + /** * Set the number of lines. * @@ -168,33 +180,36 @@ export class LinesLayout { this._lineCount = lineCount; } - public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean { + let hadAChange = false; try { - const accessor = { + const accessor: IWhitespaceChangeAccessor = { insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => { + hadAChange = true; afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; heightInPx = heightInPx | 0; minWidth = minWidth | 0; - const id = this._instanceId + (++this._lastWhitespaceId); this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth)); return id; }, changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => { + hadAChange = true; newAfterLineNumber = newAfterLineNumber | 0; newHeight = newHeight | 0; - this._pendingChanges.change({ id, newAfterLineNumber, newHeight }); }, removeWhitespace: (id: string): void => { + hadAChange = true; this._pendingChanges.remove({ id }); } }; - return callback(accessor); + callback(accessor); } finally { this._pendingChanges.commit(this); } + return hadAChange; } public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { @@ -262,7 +277,6 @@ export class LinesLayout { private _checkPendingChanges(): void { if (this._pendingChanges.mustCommit()) { - console.warn(`Commiting pending changes before change accessor leaves due to read access.`); this._pendingChanges.commit(this); } } @@ -405,7 +419,8 @@ export class LinesLayout { this._checkPendingChanges(); const linesHeight = this._lineHeight * this._lineCount; const whitespacesHeight = this.getWhitespacesTotalHeight(); - return linesHeight + whitespacesHeight; + + return linesHeight + whitespacesHeight + this._paddingTop + this._paddingBottom; } /** @@ -496,7 +511,7 @@ export class LinesLayout { const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber); - return previousLinesHeight + previousWhitespacesHeight; + return previousLinesHeight + previousWhitespacesHeight + this._paddingTop; } /** @@ -720,7 +735,7 @@ export class LinesLayout { } else { previousWhitespacesHeight = 0; } - return previousLinesHeight + previousWhitespacesHeight; + return previousLinesHeight + previousWhitespacesHeight + this._paddingTop; } public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number { diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 31148f59ab0..0964eac915f 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -7,10 +7,11 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IConfiguration, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; +import { ContentSizeChangedEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; const SMOOTH_SCROLLING_TIME = 125; @@ -75,8 +76,8 @@ class EditorScrollable extends Disposable { public readonly onDidScroll: Event; - private readonly _onDidContentSizeChange = this._register(new Emitter()); - public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; + private readonly _onDidContentSizeChange = this._register(new Emitter()); + public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; constructor(smoothScrollDuration: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { super(); @@ -114,18 +115,15 @@ class EditorScrollable extends Disposable { scrollWidth: dimensions.scrollWidth, height: dimensions.height, scrollHeight: dimensions.scrollHeight - }); + }, true); const contentWidthChanged = (oldDimensions.contentWidth !== dimensions.contentWidth); const contentHeightChanged = (oldDimensions.contentHeight !== dimensions.contentHeight); if (contentWidthChanged || contentHeightChanged) { - this._onDidContentSizeChange.fire({ - contentWidth: dimensions.contentWidth, - contentHeight: dimensions.contentHeight, - - contentWidthChanged: contentWidthChanged, - contentHeightChanged: contentHeightChanged - }); + this._onDidContentSizeChange.fire(new ContentSizeChangedEvent( + oldDimensions.contentWidth, oldDimensions.contentHeight, + dimensions.contentWidth, dimensions.contentHeight + )); } } @@ -153,7 +151,7 @@ export class ViewLayout extends Disposable implements IViewLayout { private readonly _scrollable: EditorScrollable; public readonly onDidScroll: Event; - public readonly onDidContentSizeChange: Event; + public readonly onDidContentSizeChange: Event; constructor(configuration: IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { super(); @@ -161,8 +159,9 @@ export class ViewLayout extends Disposable implements IViewLayout { this._configuration = configuration; const options = this._configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); + const padding = options.get(EditorOption.padding); - this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight)); + this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight), padding.top, padding.bottom); this._scrollable = this._register(new EditorScrollable(0, scheduleAtNextAnimationFrame)); this._configureSmoothScrollDuration(); @@ -202,17 +201,21 @@ export class ViewLayout extends Disposable implements IViewLayout { if (e.hasChanged(EditorOption.lineHeight)) { this._linesLayout.setLineHeight(options.get(EditorOption.lineHeight)); } + if (e.hasChanged(EditorOption.padding)) { + const padding = options.get(EditorOption.padding); + this._linesLayout.setPadding(padding.top, padding.bottom); + } if (e.hasChanged(EditorOption.layoutInfo)) { const layoutInfo = options.get(EditorOption.layoutInfo); const width = layoutInfo.contentWidth; const height = layoutInfo.height; const scrollDimensions = this._scrollable.getScrollDimensions(); - const scrollWidth = scrollDimensions.scrollWidth; + const contentWidth = scrollDimensions.contentWidth; this._scrollable.setScrollDimensions(new EditorScrollDimensions( width, scrollDimensions.contentWidth, height, - this._getContentHeight(width, height, scrollWidth) + this._getContentHeight(width, height, contentWidth) )); } else { this._updateHeight(); @@ -247,14 +250,14 @@ export class ViewLayout extends Disposable implements IViewLayout { return scrollbar.horizontalScrollbarSize; } - private _getContentHeight(width: number, height: number, scrollWidth: number): number { + private _getContentHeight(width: number, height: number, contentWidth: number): number { const options = this._configuration.options; let result = this._linesLayout.getLinesTotalHeight(); if (options.get(EditorOption.scrollBeyondLastLine)) { result += height - options.get(EditorOption.lineHeight); } else { - result += this._getHorizontalScrollbarHeight(width, scrollWidth); + result += this._getHorizontalScrollbarHeight(width, contentWidth); } return result; @@ -264,12 +267,12 @@ export class ViewLayout extends Disposable implements IViewLayout { const scrollDimensions = this._scrollable.getScrollDimensions(); const width = scrollDimensions.width; const height = scrollDimensions.height; - const scrollWidth = scrollDimensions.scrollWidth; + const contentWidth = scrollDimensions.contentWidth; this._scrollable.setScrollDimensions(new EditorScrollDimensions( width, scrollDimensions.contentWidth, height, - this._getContentHeight(width, height, scrollWidth) + this._getContentHeight(width, height, contentWidth) )); } @@ -300,16 +303,26 @@ export class ViewLayout extends Disposable implements IViewLayout { private _computeContentWidth(maxLineWidth: number): number { const options = this._configuration.options; const wrappingInfo = options.get(EditorOption.wrappingInfo); - let isViewportWrapping = wrappingInfo.isViewportWrapping; - if (!isViewportWrapping) { - const extraHorizontalSpace = options.get(EditorOption.scrollBeyondLastColumn) * options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; + const fontInfo = options.get(EditorOption.fontInfo); + if (wrappingInfo.isViewportWrapping) { + const layoutInfo = options.get(EditorOption.layoutInfo); + const minimap = options.get(EditorOption.minimap); + if (maxLineWidth > layoutInfo.contentWidth + fontInfo.typicalHalfwidthCharacterWidth) { + // This is a case where viewport wrapping is on, but the line extends above the viewport + if (minimap.enabled && minimap.side === 'right') { + // We need to accomodate the scrollbar width + return maxLineWidth + layoutInfo.verticalScrollbarWidth; + } + } + return maxLineWidth; + } else { + const extraHorizontalSpace = options.get(EditorOption.scrollBeyondLastColumn) * fontInfo.typicalHalfwidthCharacterWidth; const whitespaceMinWidth = this._linesLayout.getWhitespaceMinWidth(); return Math.max(maxLineWidth + extraHorizontalSpace, whitespaceMinWidth); } - return maxLineWidth; } - public onMaxLineWidthChanged(maxLineWidth: number): void { + public setMaxLineWidth(maxLineWidth: number): void { const scrollDimensions = this._scrollable.getScrollDimensions(); // const newScrollWidth = ; this._scrollable.setScrollDimensions(new EditorScrollDimensions( @@ -338,8 +351,12 @@ export class ViewLayout extends Disposable implements IViewLayout { } // ---- IVerticalLayoutProvider - public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { - return this._linesLayout.changeWhitespace(callback); + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean { + const hadAChange = this._linesLayout.changeWhitespace(callback); + if (hadAChange) { + this.onHeightMaybeChanged(); + } + return hadAChange; } public getVerticalOffsetForLineNumber(lineNumber: number): number { return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber); @@ -409,12 +426,12 @@ export class ViewLayout extends Disposable implements IViewLayout { return this._scrollable.validateScrollPosition(scrollPosition); } - public setScrollPositionNow(position: INewScrollPosition): void { - this._scrollable.setScrollPositionNow(position); - } - - public setScrollPositionSmooth(position: INewScrollPosition): void { - this._scrollable.setScrollPositionSmooth(position); + public setScrollPosition(position: INewScrollPosition, type: ScrollType): void { + if (type === ScrollType.Immediate) { + this._scrollable.setScrollPositionNow(position); + } else { + this._scrollable.setScrollPositionSmooth(position); + } } public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void { diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index c0b664a4553..1b75f0f7c3c 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -14,7 +14,18 @@ export const enum RenderWhitespace { None = 0, Boundary = 1, Selection = 2, - All = 3 + Trailing = 3, + All = 4 +} + +export const enum LinePartMetadata { + IS_WHITESPACE = 1, + PSEUDO_BEFORE = 2, + PSEUDO_AFTER = 4, + + IS_WHITESPACE_MASK = 0b001, + PSEUDO_BEFORE_MASK = 0b010, + PSEUDO_AFTER_MASK = 0b100, } class LinePart { @@ -25,10 +36,16 @@ class LinePart { */ public readonly endIndex: number; public readonly type: string; + public readonly metadata: number; - constructor(endIndex: number, type: string) { + constructor(endIndex: number, type: string, metadata: number) { this.endIndex = endIndex; this.type = type; + this.metadata = metadata; + } + + public isWhitespace(): boolean { + return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false); } } @@ -68,7 +85,8 @@ export class RenderLineInput { public readonly tabSize: number; public readonly startVisibleColumn: number; public readonly spaceWidth: number; - public readonly middotWidth: number; + public readonly renderSpaceWidth: number; + public readonly renderSpaceCharCode: number; public readonly stopRenderingLineAfter: number; public readonly renderWhitespace: RenderWhitespace; public readonly renderControlCharacters: boolean; @@ -94,8 +112,9 @@ export class RenderLineInput { startVisibleColumn: number, spaceWidth: number, middotWidth: number, + wsmiddotWidth: number, stopRenderingLineAfter: number, - renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', + renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', renderControlCharacters: boolean, fontLigatures: boolean, selectionsOnLine: LineRange[] | null @@ -112,7 +131,6 @@ export class RenderLineInput { this.tabSize = tabSize; this.startVisibleColumn = startVisibleColumn; this.spaceWidth = spaceWidth; - this.middotWidth = middotWidth; this.stopRenderingLineAfter = stopRenderingLineAfter; this.renderWhitespace = ( renderWhitespace === 'all' @@ -121,11 +139,23 @@ export class RenderLineInput { ? RenderWhitespace.Boundary : renderWhitespace === 'selection' ? RenderWhitespace.Selection - : RenderWhitespace.None + : renderWhitespace === 'trailing' + ? RenderWhitespace.Trailing + : RenderWhitespace.None ); this.renderControlCharacters = renderControlCharacters; this.fontLigatures = fontLigatures; this.selectionsOnLine = selectionsOnLine && selectionsOnLine.sort((a, b) => a.startOffset < b.startOffset ? -1 : 1); + + const wsmiddotDiff = Math.abs(wsmiddotWidth - spaceWidth); + const middotDiff = Math.abs(middotWidth - spaceWidth); + if (wsmiddotDiff < middotDiff) { + this.renderSpaceWidth = wsmiddotWidth; + this.renderSpaceCharCode = 0x2E31; // U+2E31 - WORD SEPARATOR MIDDLE DOT + } else { + this.renderSpaceWidth = middotWidth; + this.renderSpaceCharCode = 0xB7; // U+00B7 - MIDDLE DOT + } } private sameSelection(otherSelections: LineRange[] | null): boolean { @@ -162,6 +192,8 @@ export class RenderLineInput { && this.tabSize === other.tabSize && this.startVisibleColumn === other.startVisibleColumn && this.spaceWidth === other.spaceWidth + && this.renderSpaceWidth === other.renderSpaceWidth + && this.renderSpaceCharCode === other.renderSpaceCharCode && this.stopRenderingLineAfter === other.stopRenderingLineAfter && this.renderWhitespace === other.renderWhitespace && this.renderControlCharacters === other.renderControlCharacters @@ -316,8 +348,7 @@ export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): Rend let containsForeignElements = ForeignElementType.None; - // This is basically for IE's hit test to work - let content: string = '\u00a0'; + let content: string = ''; if (input.lineDecorations.length > 0) { // This line is empty, but it contains inline decorations @@ -383,7 +414,7 @@ class ResolvedRenderLineInput { public readonly startVisibleColumn: number, public readonly containsRTL: boolean, public readonly spaceWidth: number, - public readonly middotWidth: number, + public readonly renderSpaceCharCode: number, public readonly renderWhitespace: RenderWhitespace, public readonly renderControlCharacters: boolean, ) { @@ -392,7 +423,6 @@ class ResolvedRenderLineInput { } function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput { - const useMonospaceOptimizations = input.useMonospaceOptimizations; const lineContent = input.lineContent; let isOverflowing: boolean; @@ -407,8 +437,12 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput } let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); - if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) { - tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, input.startVisibleColumn, useMonospaceOptimizations, input.selectionsOnLine, input.renderWhitespace === RenderWhitespace.Boundary); + if (input.renderWhitespace === RenderWhitespace.All || + input.renderWhitespace === RenderWhitespace.Boundary || + (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine) || + input.renderWhitespace === RenderWhitespace.Trailing) { + + tokens = _applyRenderWhitespace(input, lineContent, len, tokens); } let containsForeignElements = ForeignElementType.None; if (input.lineDecorations.length > 0) { @@ -431,7 +465,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput } return new ResolvedRenderLineInput( - useMonospaceOptimizations, + input.useMonospaceOptimizations, input.canUseHalfwidthRightwardsArrow, lineContent, len, @@ -443,7 +477,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput input.startVisibleColumn, input.containsRTL, input.spaceWidth, - input.middotWidth, + input.renderSpaceCharCode, input.renderWhitespace, input.renderControlCharacters ); @@ -458,7 +492,7 @@ function transformAndRemoveOverflowing(tokens: IViewLineTokens, fauxIndentLength // The faux indent part of the line should have no token type if (fauxIndentLength > 0) { - result[resultLen++] = new LinePart(fauxIndentLength, ''); + result[resultLen++] = new LinePart(fauxIndentLength, '', 0); } for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { @@ -469,10 +503,10 @@ function transformAndRemoveOverflowing(tokens: IViewLineTokens, fauxIndentLength } const type = tokens.getClassName(tokenIndex); if (endIndex >= len) { - result[resultLen++] = new LinePart(len, type); + result[resultLen++] = new LinePart(len, type, 0); break; } - result[resultLen++] = new LinePart(endIndex, type); + result[resultLen++] = new LinePart(endIndex, type, 0); } return result; @@ -486,7 +520,7 @@ const enum Constants { } /** - * See https://github.com/Microsoft/vscode/issues/6885. + * See https://github.com/microsoft/vscode/issues/6885. * It appears that having very large spans causes very slow reading of character positions. * So here we try to avoid that. */ @@ -501,6 +535,7 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: const tokenEndIndex = token.endIndex; if (lastTokenEndIndex + Constants.LongToken < tokenEndIndex) { const tokenType = token.type; + const tokenMetadata = token.metadata; let lastSpaceOffset = -1; let currTokenStart = lastTokenEndIndex; @@ -510,13 +545,13 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: } if (lastSpaceOffset !== -1 && j - currTokenStart >= Constants.LongToken) { // Split at `lastSpaceOffset` + 1 - result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType); + result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType, tokenMetadata); currTokenStart = lastSpaceOffset + 1; lastSpaceOffset = -1; } } if (currTokenStart !== tokenEndIndex) { - result[resultLen++] = new LinePart(tokenEndIndex, tokenType); + result[resultLen++] = new LinePart(tokenEndIndex, tokenType, tokenMetadata); } } else { result[resultLen++] = token; @@ -532,12 +567,13 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: let diff = (tokenEndIndex - lastTokenEndIndex); if (diff > Constants.LongToken) { const tokenType = token.type; + const tokenMetadata = token.metadata; const piecesCount = Math.ceil(diff / Constants.LongToken); for (let j = 1; j < piecesCount; j++) { let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken); - result[resultLen++] = new LinePart(pieceEndIndex, tokenType); + result[resultLen++] = new LinePart(pieceEndIndex, tokenType, tokenMetadata); } - result[resultLen++] = new LinePart(tokenEndIndex, tokenType); + result[resultLen++] = new LinePart(tokenEndIndex, tokenType, tokenMetadata); } else { result[resultLen++] = token; } @@ -549,11 +585,21 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: } /** - * Whitespace is rendered by "replacing" tokens with a special-purpose `vs-whitespace` type that is later recognized in the rendering phase. + * Whitespace is rendered by "replacing" tokens with a special-purpose `mtkw` type that is later recognized in the rendering phase. * Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (→ or ·) do not have the same width as  . * The rendering phase will generate `style="width:..."` for these tokens. */ -function _applyRenderWhitespace(lineContent: string, len: number, continuesWithWrappedLine: boolean, tokens: LinePart[], fauxIndentLength: number, tabSize: number, startVisibleColumn: number, useMonospaceOptimizations: boolean, selections: LineRange[] | null, onlyBoundary: boolean): LinePart[] { +function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len: number, tokens: LinePart[]): LinePart[] { + + const continuesWithWrappedLine = input.continuesWithWrappedLine; + const fauxIndentLength = input.fauxIndentLength; + const tabSize = input.tabSize; + const startVisibleColumn = input.startVisibleColumn; + const useMonospaceOptimizations = input.useMonospaceOptimizations; + const selections = input.selectionsOnLine; + const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary); + const onlyTrailing = (input.renderWhitespace === RenderWhitespace.Trailing); + const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth); let result: LinePart[] = [], resultLen = 0; let tokenIndex = 0; @@ -561,10 +607,11 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW let tokenEndIndex = tokens[tokenIndex].endIndex; const tokensLength = tokens.length; + let lineIsEmptyOrWhitespace = false; let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); let lastNonWhitespaceIndex: number; if (firstNonWhitespaceIndex === -1) { - // The entire line is whitespace + lineIsEmptyOrWhitespace = true; firstNonWhitespaceIndex = len; lastNonWhitespaceIndex = len; } else { @@ -612,17 +659,29 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW isInWhitespace = !!currentSelection && currentSelection.startOffset <= charIndex && currentSelection.endOffset > charIndex; } + // If rendering only trailing whitespace, check that the charIndex points to trailing whitespace. + if (isInWhitespace && onlyTrailing) { + isInWhitespace = lineIsEmptyOrWhitespace || charIndex > lastNonWhitespaceIndex; + } + if (wasInWhitespace) { // was in whitespace token if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) { // leaving whitespace token or entering a new indent - result[resultLen++] = new LinePart(charIndex, 'vs-whitespace'); + if (generateLinePartForEachWhitespace) { + const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength); + for (let i = lastEndIndex + 1; i <= charIndex; i++) { + result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE); + } + } else { + result[resultLen++] = new LinePart(charIndex, 'mtkw', LinePartMetadata.IS_WHITESPACE); + } tmpIndent = tmpIndent % tabSize; } } else { // was in regular token if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) { - result[resultLen++] = new LinePart(charIndex, tokenType); + result[resultLen++] = new LinePart(charIndex, tokenType, 0); tmpIndent = tmpIndent % tabSize; } } @@ -637,7 +696,7 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW wasInWhitespace = isInWhitespace; - if (charIndex === tokenEndIndex) { + while (charIndex === tokenEndIndex) { tokenIndex++; if (tokenIndex < tokensLength) { tokenType = tokens[tokenIndex].type; @@ -661,7 +720,18 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW } } - result[resultLen++] = new LinePart(len, generateWhitespace ? 'vs-whitespace' : tokenType); + if (generateWhitespace) { + if (generateLinePartForEachWhitespace) { + const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength); + for (let i = lastEndIndex + 1; i <= len; i++) { + result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE); + } + } else { + result[resultLen++] = new LinePart(len, 'mtkw', LinePartMetadata.IS_WHITESPACE); + } + } else { + result[resultLen++] = new LinePart(len, tokenType, 0); + } return result; } @@ -681,42 +751,45 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP const token = tokens[tokenIndex]; const tokenEndIndex = token.endIndex; const tokenType = token.type; + const tokenMetadata = token.metadata; while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset < tokenEndIndex) { const lineDecoration = lineDecorations[lineDecorationIndex]; if (lineDecoration.startOffset > lastResultEndIndex) { lastResultEndIndex = lineDecoration.startOffset; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType, tokenMetadata); } if (lineDecoration.endOffset + 1 <= tokenEndIndex) { // This line decoration ends before this token ends lastResultEndIndex = lineDecoration.endOffset + 1; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className, tokenMetadata | lineDecoration.metadata); lineDecorationIndex++; } else { // This line decoration continues on to the next token lastResultEndIndex = tokenEndIndex; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className, tokenMetadata | lineDecoration.metadata); break; } } if (tokenEndIndex > lastResultEndIndex) { lastResultEndIndex = tokenEndIndex; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType, tokenMetadata); } } const lastTokenEndIndex = tokens[tokens.length - 1].endIndex; if (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) { let classNames: string[] = []; + let metadata = 0; while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) { classNames.push(lineDecorations[lineDecorationIndex].className); + metadata |= lineDecorations[lineDecorationIndex].metadata; lineDecorationIndex++; } - result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' ')); + result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '), metadata); } return result; @@ -739,23 +812,25 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const startVisibleColumn = input.startVisibleColumn; const containsRTL = input.containsRTL; const spaceWidth = input.spaceWidth; - const middotWidth = input.middotWidth; + const renderSpaceCharCode = input.renderSpaceCharCode; const renderWhitespace = input.renderWhitespace; const renderControlCharacters = input.renderControlCharacters; - // use U+2E31 - WORD SEPARATOR MIDDLE DOT or U+00B7 - MIDDLE DOT - const spaceRenderWhitespaceCharacter = (middotWidth > spaceWidth ? 0x2E31 : 0xB7); - const characterMapping = new CharacterMapping(len + 1, parts.length); let charIndex = 0; let visibleColumn = startVisibleColumn; let charOffsetInPart = 0; + let partDisplacement = 0; let prevPartContentCnt = 0; let partAbsoluteOffset = 0; - sb.appendASCIIString(''); + if (containsRTL) { + sb.appendASCIIString(''); + } else { + sb.appendASCIIString(''); + } for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) { partAbsoluteOffset += prevPartContentCnt; @@ -763,11 +838,13 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const part = parts[partIndex]; const partEndIndex = part.endIndex; const partType = part.type; - const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('vs-whitespace') >= 0)); + const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace()); + const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements); + const partIsEmptyAndHasPseudoAfter = (charIndex === partEndIndex && part.metadata === LinePartMetadata.PSEUDO_AFTER); charOffsetInPart = 0; sb.appendASCIIString(''); } diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 97a3c4a7655..549496d003a 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -119,6 +119,8 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla let breakingOffsets: number[] = arrPool1; let breakingOffsetsVisibleColumn: number[] = arrPool2; let breakingOffsetsCount: number = 0; + let lastBreakingOffset = 0; + let lastBreakingOffsetVisibleColumn = 0; let breakingColumn = firstLineBreakColumn; const prevLen = prevBreakingOffsets.length; @@ -138,8 +140,12 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla while (prevIndex < prevLen) { // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) - const prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; - const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex]; + let prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; + let prevBreakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex]; + if (lastBreakingOffset > prevBreakOffset) { + prevBreakOffset = lastBreakingOffset; + prevBreakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn; + } let breakOffset = 0; let breakOffsetVisibleColumn = 0; @@ -148,10 +154,10 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla let forcedBreakOffsetVisibleColumn = 0; // initially, we search as much as possible to the right (if it fits) - if (prevBreakoffsetVisibleColumn <= breakingColumn) { - let visibleColumn = prevBreakoffsetVisibleColumn; - let prevCharCode = lineText.charCodeAt(prevBreakOffset - 1); - let prevCharCodeClass = classifier.get(prevCharCode); + if (prevBreakOffsetVisibleColumn <= breakingColumn) { + let visibleColumn = prevBreakOffsetVisibleColumn; + let prevCharCode = prevBreakOffset === 0 ? CharCode.Null : lineText.charCodeAt(prevBreakOffset - 1); + let prevCharCodeClass = prevBreakOffset === 0 ? CharacterClass.NONE : classifier.get(prevCharCode); let entireLineFits = true; for (let i = prevBreakOffset; i < len; i++) { const charStartOffset = i; @@ -169,7 +175,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } - if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } @@ -179,8 +185,14 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla // check if adding character at `i` will go over the breaking column if (visibleColumn > breakingColumn) { // We need to break at least before character at `i`: - forcedBreakOffset = charStartOffset; - forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; + if (charStartOffset > lastBreakingOffset) { + forcedBreakOffset = charStartOffset; + forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; + } else { + // we need to advance at least by one character + forcedBreakOffset = i + 1; + forcedBreakOffsetVisibleColumn = visibleColumn; + } if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) { // Cannot break at `breakOffset` => reset it if it was set @@ -198,7 +210,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla if (entireLineFits) { // there is no more need to break => stop the outer loop! if (breakingOffsetsCount > 0) { - // Add last segment + // Add last segment, no need to assign to `lastBreakingOffset` and `lastBreakingOffsetVisibleColumn` breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; breakingOffsetsCount++; @@ -209,11 +221,11 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla if (breakOffset === 0) { // must search left - let visibleColumn = prevBreakoffsetVisibleColumn; + let visibleColumn = prevBreakOffsetVisibleColumn; let charCode = lineText.charCodeAt(prevBreakOffset); let charCodeClass = classifier.get(charCode); let hitATabCharacter = false; - for (let i = prevBreakOffset - 1; i >= 0; i--) { + for (let i = prevBreakOffset - 1; i >= lastBreakingOffset; i--) { const charStartOffset = i + 1; const prevCharCode = lineText.charCodeAt(i); @@ -290,7 +302,9 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn; } + lastBreakingOffset = breakOffset; breakingOffsets[breakingOffsetsCount] = breakOffset; + lastBreakingOffsetVisibleColumn = breakOffsetVisibleColumn; breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; breakingOffsetsCount++; breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn; diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index dde0d12c37a..3a3eab70a1c 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -13,9 +13,9 @@ import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; -import { ITheme } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { EditorTheme } from 'vs/editor/common/view/viewContext'; export class OutputPosition { outputLineIndex: number; @@ -109,7 +109,7 @@ export interface ISplitLine { export interface IViewModelLinesCollection extends IDisposable { createCoordinatesConverter(): ICoordinatesConverter; - setWrappingSettings(fontInfo: FontInfo, wrappingAlgorithm: 'monospace' | 'dom', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; + setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; setTabSize(newTabSize: number): boolean; getHiddenAreas(): Range[]; setHiddenAreas(_ranges: Range[]): boolean; @@ -131,7 +131,7 @@ export interface IViewModelLinesCollection extends IDisposable { getViewLineData(viewLineNumber: number): ViewLineData; getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): Array; - getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations; + getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations; getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[]; } @@ -277,7 +277,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private tabSize: number; private wrappingColumn: number; private wrappingIndent: WrappingIndent; - private wrappingAlgorithm: 'monospace' | 'dom'; + private wrappingStrategy: 'simple' | 'advanced'; private lines!: ISplitLine[]; private prefixSumComputer!: LineNumberMapper; @@ -290,7 +290,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, fontInfo: FontInfo, tabSize: number, - wrappingAlgorithm: 'monospace' | 'dom', + wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent, ) { @@ -300,7 +300,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { this._monospaceLineBreaksComputerFactory = monospaceLineBreaksComputerFactory; this.fontInfo = fontInfo; this.tabSize = tabSize; - this.wrappingAlgorithm = wrappingAlgorithm; + this.wrappingStrategy = wrappingStrategy; this.wrappingColumn = wrappingColumn; this.wrappingIndent = wrappingIndent; @@ -484,19 +484,19 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return true; } - public setWrappingSettings(fontInfo: FontInfo, wrappingAlgorithm: 'monospace' | 'dom', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { + public setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { const equalFontInfo = this.fontInfo.equals(fontInfo); - const equalWrappingAlgorithm = (this.wrappingAlgorithm === wrappingAlgorithm); + const equalWrappingStrategy = (this.wrappingStrategy === wrappingStrategy); const equalWrappingColumn = (this.wrappingColumn === wrappingColumn); const equalWrappingIndent = (this.wrappingIndent === wrappingIndent); - if (equalFontInfo && equalWrappingAlgorithm && equalWrappingColumn && equalWrappingIndent) { + if (equalFontInfo && equalWrappingStrategy && equalWrappingColumn && equalWrappingIndent) { return false; } - const onlyWrappingColumnChanged = (equalFontInfo && equalWrappingAlgorithm && !equalWrappingColumn && equalWrappingIndent); + const onlyWrappingColumnChanged = (equalFontInfo && equalWrappingStrategy && !equalWrappingColumn && equalWrappingIndent); this.fontInfo = fontInfo; - this.wrappingAlgorithm = wrappingAlgorithm; + this.wrappingStrategy = wrappingStrategy; this.wrappingColumn = wrappingColumn; this.wrappingIndent = wrappingIndent; @@ -515,7 +515,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { public createLineBreaksComputer(): ILineBreaksComputer { const lineBreaksComputerFactory = ( - this.wrappingAlgorithm === 'dom' + this.wrappingStrategy === 'advanced' ? this._domLineBreaksComputerFactory : this._monospaceLineBreaksComputerFactory ); @@ -940,7 +940,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.lines[lineIndex].getViewLineNumberOfModelPosition(deltaLineNumber, this.model.getLineMaxColumn(lineIndex + 1)); } - public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations { + public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations { const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation); const result = new OverviewRulerDecorations(); for (const decoration of decorations) { @@ -1460,7 +1460,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return false; } - public setWrappingSettings(_fontInfo: FontInfo, _wrappingAlgorithm: 'monospace' | 'dom', _wrappingColumn: number, _wrappingIndent: WrappingIndent): boolean { + public setWrappingSettings(_fontInfo: FontInfo, _wrappingStrategy: 'simple' | 'advanced', _wrappingColumn: number, _wrappingIndent: WrappingIndent): boolean { return false; } @@ -1561,7 +1561,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return result; } - public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations { + public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations { const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation); const result = new OverviewRulerDecorations(); for (const decoration of decorations) { diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts index b8d0bc823a6..aad7a0b4957 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -36,9 +36,7 @@ export class ViewEventHandler extends Disposable { public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { return false; } - public onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean { - return false; - } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { return false; } @@ -102,12 +100,6 @@ export class ViewEventHandler extends Disposable { } break; - case viewEvents.ViewEventType.ViewContentSizeChanged: - if (this.onContentSizeChanged(e)) { - shouldRender = true; - } - break; - case viewEvents.ViewEventType.ViewCursorStateChanged: if (this.onCursorStateChanged(e)) { shouldRender = true; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 145d770e7d6..91ab8513c4c 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -3,18 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, Scrollable } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { INewScrollPosition } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model'; -import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; +import { INewScrollPosition, ScrollType } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions, ITextModel } from 'vs/editor/common/model'; +import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; -import { ITheme } from 'vs/platform/theme/common/themeService'; +import { EditorTheme } from 'vs/editor/common/view/viewContext'; +import { ICursorSimpleModel, PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export interface IViewWhitespaceViewportData { readonly id: string; @@ -43,8 +45,6 @@ export interface IViewLayout { getScrollable(): Scrollable; - onMaxLineWidthChanged(width: number): void; - getScrollWidth(): number; getScrollHeight(): number; @@ -55,9 +55,6 @@ export interface IViewLayout { getFutureViewport(): Viewport; validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition; - setScrollPositionNow(position: INewScrollPosition): void; - setScrollPositionSmooth(position: INewScrollPosition): void; - deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void; getLinesViewportData(): IPartialViewLinesViewportData; getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData; @@ -68,18 +65,10 @@ export interface IViewLayout { getVerticalOffsetForLineNumber(lineNumber: number): number; getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null; - // --------------- Begin vertical whitespace management - changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T; - /** * Get the layout information for whitespaces currently in the viewport */ getWhitespaceViewportData(): IViewWhitespaceViewportData[]; - - // TODO@Alex whitespace management should work via a change accessor sort of thing - onHeightMaybeChanged(): void; - - // --------------- End vertical whitespace management } export interface ICoordinatesConverter { @@ -95,20 +84,26 @@ export interface ICoordinatesConverter { modelPositionIsVisible(modelPosition: Position): boolean; } -export interface IViewModel { +export interface IViewModel extends ICursorSimpleModel { - addEventListener(listener: IViewEventListener): IDisposable; + readonly model: ITextModel; readonly coordinatesConverter: ICoordinatesConverter; readonly viewLayout: IViewLayout; + readonly cursorConfig: CursorConfiguration; + + addViewEventHandler(eventHandler: ViewEventHandler): void; + removeViewEventHandler(eventHandler: ViewEventHandler): void; + /** * Gives a hint that a lot of requests are about to come in for these line numbers. */ setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; tokenizeViewport(): void; setHasFocus(hasFocus: boolean): void; + onDidColorThemeChange(): void; getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData; @@ -117,7 +112,7 @@ export interface IViewModel { getCompletelyVisibleViewRange(): Range; getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range; - getOptions(): TextModelResolvedOptions; + getTextModelOptions(): TextModelResolvedOptions; getLineCount(): number; getLineContent(lineNumber: number): string; getLineLength(lineNumber: number): number; @@ -127,7 +122,7 @@ export interface IViewModel { getLineMaxColumn(lineNumber: number): number; getLineFirstNonWhitespaceColumn(lineNumber: number): number; getLineLastNonWhitespaceColumn(lineNumber: number): number; - getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations; + getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations; invalidateOverviewRulerColorCache(): void; invalidateMinimapColorCache(): void; getValueInRange(range: Range, eol: EndOfLinePreference): string; @@ -140,6 +135,38 @@ export interface IViewModel { getEOL(): string; getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[]; getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null; + + //#region model + + pushStackElement(): void; + + //#endregion + + + //#region cursor + getPrimaryCursorState(): CursorState; + getLastAddedCursorIndex(): number; + getCursorStates(): CursorState[]; + setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void; + getCursorColumnSelectData(): IColumnSelectData; + setCursorColumnSelectData(columnSelectData: IColumnSelectData): void; + getPrevEditOperationType(): EditOperationType; + setPrevEditOperationType(type: EditOperationType): void; + revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void; + revealTopMostCursor(source: string | null | undefined): void; + revealBottomMostCursor(source: string | null | undefined): void; + revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void; + //#endregion + + //#region viewLayout + getVerticalOffsetForLineNumber(viewLineNumber: number): number; + getScrollTop(): number; + setScrollTop(newScrollTop: number, scrollType: ScrollType): void; + setScrollPosition(position: INewScrollPosition, type: ScrollType): void; + deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void; + changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void; + setMaxLineWidth(maxLineWidth: number): void; + //#endregion } export class MinimapLinesRenderingData { diff --git a/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts new file mode 100644 index 00000000000..0691b542cd1 --- /dev/null +++ b/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts @@ -0,0 +1,393 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { ViewEvent } from 'vs/editor/common/view/viewEvents'; +import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { Emitter } from 'vs/base/common/event'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; + +export class ViewModelEventDispatcher extends Disposable { + + private readonly _onEvent = this._register(new Emitter()); + public readonly onEvent = this._onEvent.event; + + private readonly _eventHandlers: ViewEventHandler[]; + private _viewEventQueue: ViewEvent[] | null; + private _isConsumingViewEventQueue: boolean; + private _collector: ViewModelEventsCollector | null; + private _collectorCnt: number; + private _outgoingEvents: OutgoingViewModelEvent[]; + + constructor() { + super(); + this._eventHandlers = []; + this._viewEventQueue = null; + this._isConsumingViewEventQueue = false; + this._collector = null; + this._collectorCnt = 0; + this._outgoingEvents = []; + } + + public emitOutgoingEvent(e: OutgoingViewModelEvent): void { + this._addOutgoingEvent(e); + this._emitOugoingEvents(); + } + + private _addOutgoingEvent(e: OutgoingViewModelEvent): void { + for (let i = 0, len = this._outgoingEvents.length; i < len; i++) { + if (this._outgoingEvents[i].kind === e.kind) { + this._outgoingEvents[i] = this._outgoingEvents[i].merge(e); + return; + } + } + // not merged + this._outgoingEvents.push(e); + } + + private _emitOugoingEvents(): void { + while (this._outgoingEvents.length > 0) { + if (this._collector || this._isConsumingViewEventQueue) { + // right now collecting or emitting view events, so let's postpone emitting + return; + } + const event = this._outgoingEvents.shift()!; + if (event.isNoOp()) { + continue; + } + this._onEvent.fire(event); + } + } + + public addViewEventHandler(eventHandler: ViewEventHandler): void { + for (let i = 0, len = this._eventHandlers.length; i < len; i++) { + if (this._eventHandlers[i] === eventHandler) { + console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler); + } + } + this._eventHandlers.push(eventHandler); + } + + public removeViewEventHandler(eventHandler: ViewEventHandler): void { + for (let i = 0; i < this._eventHandlers.length; i++) { + if (this._eventHandlers[i] === eventHandler) { + this._eventHandlers.splice(i, 1); + break; + } + } + } + + public beginEmitViewEvents(): ViewModelEventsCollector { + this._collectorCnt++; + if (this._collectorCnt === 1) { + this._collector = new ViewModelEventsCollector(); + } + return this._collector!; + } + + public endEmitViewEvents(): void { + this._collectorCnt--; + if (this._collectorCnt === 0) { + const outgoingEvents = this._collector!.outgoingEvents; + const viewEvents = this._collector!.viewEvents; + this._collector = null; + + for (const outgoingEvent of outgoingEvents) { + this._addOutgoingEvent(outgoingEvent); + } + + if (viewEvents.length > 0) { + this._emitMany(viewEvents); + } + } + this._emitOugoingEvents(); + } + + public emitSingleViewEvent(event: ViewEvent): void { + try { + const eventsCollector = this.beginEmitViewEvents(); + eventsCollector.emitViewEvent(event); + } finally { + this.endEmitViewEvents(); + } + } + + private _emitMany(events: ViewEvent[]): void { + if (this._viewEventQueue) { + this._viewEventQueue = this._viewEventQueue.concat(events); + } else { + this._viewEventQueue = events; + } + + if (!this._isConsumingViewEventQueue) { + this._consumeViewEventQueue(); + } + } + + private _consumeViewEventQueue(): void { + try { + this._isConsumingViewEventQueue = true; + this._doConsumeQueue(); + } finally { + this._isConsumingViewEventQueue = false; + } + } + + private _doConsumeQueue(): void { + while (this._viewEventQueue) { + // Empty event queue, as events might come in while sending these off + const events = this._viewEventQueue; + this._viewEventQueue = null; + + // Use a clone of the event handlers list, as they might remove themselves + const eventHandlers = this._eventHandlers.slice(0); + for (const eventHandler of eventHandlers) { + eventHandler.handleEvents(events); + } + } + } +} + +export class ViewModelEventsCollector { + + public readonly viewEvents: ViewEvent[]; + public readonly outgoingEvents: OutgoingViewModelEvent[]; + + constructor() { + this.viewEvents = []; + this.outgoingEvents = []; + } + + public emitViewEvent(event: ViewEvent) { + this.viewEvents.push(event); + } + + public emitOutgoingEvent(e: OutgoingViewModelEvent): void { + this.outgoingEvents.push(e); + } +} + +export const enum OutgoingViewModelEventKind { + ContentSizeChanged, + FocusChanged, + ScrollChanged, + ViewZonesChanged, + ReadOnlyEditAttempt, + CursorStateChanged, +} + +export class ContentSizeChangedEvent implements IContentSizeChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.ContentSizeChanged; + + private readonly _oldContentWidth: number; + private readonly _oldContentHeight: number; + + readonly contentWidth: number; + readonly contentHeight: number; + readonly contentWidthChanged: boolean; + readonly contentHeightChanged: boolean; + + constructor(oldContentWidth: number, oldContentHeight: number, contentWidth: number, contentHeight: number) { + this._oldContentWidth = oldContentWidth; + this._oldContentHeight = oldContentHeight; + this.contentWidth = contentWidth; + this.contentHeight = contentHeight; + this.contentWidthChanged = (this._oldContentWidth !== this.contentWidth); + this.contentHeightChanged = (this._oldContentHeight !== this.contentHeight); + } + + public isNoOp(): boolean { + return (!this.contentWidthChanged && !this.contentHeightChanged); + } + + + public merge(other: OutgoingViewModelEvent): ContentSizeChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.ContentSizeChanged) { + return this; + } + return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight); + } +} + +export class FocusChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.FocusChanged; + + readonly oldHasFocus: boolean; + readonly hasFocus: boolean; + + constructor(oldHasFocus: boolean, hasFocus: boolean) { + this.oldHasFocus = oldHasFocus; + this.hasFocus = hasFocus; + } + + public isNoOp(): boolean { + return (this.oldHasFocus === this.hasFocus); + } + + public merge(other: OutgoingViewModelEvent): FocusChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.FocusChanged) { + return this; + } + return new FocusChangedEvent(this.oldHasFocus, other.hasFocus); + } +} + +export class ScrollChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.ScrollChanged; + + private readonly _oldScrollWidth: number; + private readonly _oldScrollLeft: number; + private readonly _oldScrollHeight: number; + private readonly _oldScrollTop: number; + + public readonly scrollWidth: number; + public readonly scrollLeft: number; + public readonly scrollHeight: number; + public readonly scrollTop: number; + + public readonly scrollWidthChanged: boolean; + public readonly scrollLeftChanged: boolean; + public readonly scrollHeightChanged: boolean; + public readonly scrollTopChanged: boolean; + + constructor( + oldScrollWidth: number, oldScrollLeft: number, oldScrollHeight: number, oldScrollTop: number, + scrollWidth: number, scrollLeft: number, scrollHeight: number, scrollTop: number, + ) { + this._oldScrollWidth = oldScrollWidth; + this._oldScrollLeft = oldScrollLeft; + this._oldScrollHeight = oldScrollHeight; + this._oldScrollTop = oldScrollTop; + + this.scrollWidth = scrollWidth; + this.scrollLeft = scrollLeft; + this.scrollHeight = scrollHeight; + this.scrollTop = scrollTop; + + this.scrollWidthChanged = (this._oldScrollWidth !== this.scrollWidth); + this.scrollLeftChanged = (this._oldScrollLeft !== this.scrollLeft); + this.scrollHeightChanged = (this._oldScrollHeight !== this.scrollHeight); + this.scrollTopChanged = (this._oldScrollTop !== this.scrollTop); + } + + public isNoOp(): boolean { + return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged); + } + + public merge(other: OutgoingViewModelEvent): ScrollChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.ScrollChanged) { + return this; + } + return new ScrollChangedEvent( + this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop, + other.scrollWidth, other.scrollLeft, other.scrollHeight, other.scrollTop + ); + } +} + +export class ViewZonesChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.ViewZonesChanged; + + constructor() { + } + + public isNoOp(): boolean { + return false; + } + + public merge(other: OutgoingViewModelEvent): ViewZonesChangedEvent { + return this; + } +} + +export class CursorStateChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.CursorStateChanged; + + public readonly oldSelections: Selection[] | null; + public readonly selections: Selection[]; + public readonly oldModelVersionId: number; + public readonly modelVersionId: number; + public readonly source: string; + public readonly reason: CursorChangeReason; + public readonly reachedMaxCursorCount: boolean; + + constructor(oldSelections: Selection[] | null, selections: Selection[], oldModelVersionId: number, modelVersionId: number, source: string, reason: CursorChangeReason, reachedMaxCursorCount: boolean) { + this.oldSelections = oldSelections; + this.selections = selections; + this.oldModelVersionId = oldModelVersionId; + this.modelVersionId = modelVersionId; + this.source = source; + this.reason = reason; + this.reachedMaxCursorCount = reachedMaxCursorCount; + } + + private static _selectionsAreEqual(a: Selection[] | null, b: Selection[] | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + const aLen = a.length; + const bLen = b.length; + if (aLen !== bLen) { + return false; + } + for (let i = 0; i < aLen; i++) { + if (!a[i].equalsSelection(b[i])) { + return false; + } + } + return true; + } + + public isNoOp(): boolean { + return ( + CursorStateChangedEvent._selectionsAreEqual(this.oldSelections, this.selections) + && this.oldModelVersionId === this.modelVersionId + ); + } + + public merge(other: OutgoingViewModelEvent): CursorStateChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.CursorStateChanged) { + return this; + } + return new CursorStateChangedEvent( + this.oldSelections, other.selections, this.oldModelVersionId, other.modelVersionId, other.source, other.reason, this.reachedMaxCursorCount || other.reachedMaxCursorCount + ); + } +} + +export class ReadOnlyEditAttemptEvent { + + public readonly kind = OutgoingViewModelEventKind.ReadOnlyEditAttempt; + + constructor() { + } + + public isNoOp(): boolean { + return false; + } + + public merge(other: OutgoingViewModelEvent): ReadOnlyEditAttemptEvent { + return this; + } +} + +export type OutgoingViewModelEvent = ( + ContentSizeChangedEvent + | FocusChangedEvent + | ScrollChangedEvent + | ViewZonesChangedEvent + | ReadOnlyEditAttemptEvent + | CursorStateChangedEvent +); diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index aee611b762d..f7f130f0866 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -4,13 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from 'vs/base/common/color'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { IConfiguration, IViewState } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions } from 'vs/editor/common/model'; +import { IConfiguration, IViewState, ScrollType, ICursorState, ICommand, INewScrollPosition } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions, IIdentifiedSingleEditOperation, ICursorStateComputer } from 'vs/editor/common/model'; import { ModelDecorationOverviewRulerOptions, ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; import { ColorId, LanguageId, TokenizationRegistry } from 'vs/editor/common/modes'; @@ -21,26 +23,37 @@ import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; -import { ITheme } from 'vs/platform/theme/common/themeService'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as platform from 'vs/base/common/platform'; +import { EditorTheme } from 'vs/editor/common/view/viewContext'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; +import { ViewModelEventDispatcher, OutgoingViewModelEvent, FocusChangedEvent, ScrollChangedEvent, ViewZonesChangedEvent, ViewModelEventsCollector, ReadOnlyEditAttemptEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; const USE_IDENTITY_LINES_COLLECTION = true; -export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel { +export class ViewModel extends Disposable implements IViewModel { - private readonly editorId: number; - private readonly configuration: IConfiguration; - private readonly model: ITextModel; + private readonly _editorId: number; + private readonly _configuration: IConfiguration; + public readonly model: ITextModel; + private readonly _eventDispatcher: ViewModelEventDispatcher; + public readonly onEvent: Event; + public cursorConfig: CursorConfiguration; private readonly _tokenizeViewportSoon: RunOnceScheduler; - private hasFocus: boolean; - private viewportStartLine: number; - private viewportStartLineTrackedRange: string | null; - private viewportStartLineDelta: number; - private readonly lines: IViewModelLinesCollection; + private readonly _updateConfigurationViewLineCount: RunOnceScheduler; + private _hasFocus: boolean; + private _viewportStartLine: number; + private _viewportStartLineTrackedRange: string | null; + private _viewportStartLineDelta: number; + private readonly _lines: IViewModelLinesCollection; public readonly coordinatesConverter: ICoordinatesConverter; public readonly viewLayout: ViewLayout; - private readonly decorations: ViewModelDecorations; + private readonly _cursor: Cursor; + private readonly _decorations: ViewModelDecorations; constructor( editorId: number, @@ -52,94 +65,104 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel ) { super(); - this.editorId = editorId; - this.configuration = configuration; + this._editorId = editorId; + this._configuration = configuration; this.model = model; + this._eventDispatcher = new ViewModelEventDispatcher(); + this.onEvent = this._eventDispatcher.onEvent; + this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration); this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50)); - this.hasFocus = false; - this.viewportStartLine = -1; - this.viewportStartLineTrackedRange = null; - this.viewportStartLineDelta = 0; + this._updateConfigurationViewLineCount = this._register(new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0)); + this._hasFocus = false; + this._viewportStartLine = -1; + this._viewportStartLineTrackedRange = null; + this._viewportStartLineDelta = 0; if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) { - this.lines = new IdentityLinesCollection(this.model); + this._lines = new IdentityLinesCollection(this.model); } else { - const options = this.configuration.options; + const options = this._configuration.options; const fontInfo = options.get(EditorOption.fontInfo); - const wrappingAlgorithm = options.get(EditorOption.wrappingAlgorithm); + const wrappingStrategy = options.get(EditorOption.wrappingStrategy); const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - this.lines = new SplitLinesCollection( + this._lines = new SplitLinesCollection( this.model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, fontInfo, this.model.getOptions().tabSize, - wrappingAlgorithm, + wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent ); } - this.coordinatesConverter = this.lines.createCoordinatesConverter(); + this.coordinatesConverter = this._lines.createCoordinatesConverter(); - this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); + this._cursor = this._register(new Cursor(model, this, this.coordinatesConverter, this.cursorConfig)); + + this.viewLayout = this._register(new ViewLayout(this._configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); this._register(this.viewLayout.onDidScroll((e) => { if (e.scrollTopChanged) { this._tokenizeViewportSoon.schedule(); } - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewScrollChangedEvent(e)); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewScrollChangedEvent(e)); + this._eventDispatcher.emitOutgoingEvent(new ScrollChangedEvent( + e.oldScrollWidth, e.oldScrollLeft, e.oldScrollHeight, e.oldScrollTop, + e.scrollWidth, e.scrollLeft, e.scrollHeight, e.scrollTop + )); })); this._register(this.viewLayout.onDidContentSizeChange((e) => { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewContentSizeChangedEvent(e)); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitOutgoingEvent(e); })); - this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.lines, this.coordinatesConverter); + this._decorations = new ViewModelDecorations(this._editorId, this.model, this._configuration, this._lines, this.coordinatesConverter); this._registerModelEvents(); - this._register(this.configuration.onDidChange((e) => { + this._register(this._configuration.onDidChangeFast((e) => { try { - const eventsCollector = this._beginEmit(); + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); this._onConfigurationChanged(eventsCollector, e); } finally { - this._endEmit(); + this._eventDispatcher.endEmitViewEvents(); } })); this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewTokensColorsChangedEvent()); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent()); })); + + this._updateConfigurationViewLineCountNow(); } public dispose(): void { // First remove listeners, as disposing the lines might end up sending // model decoration changed events ... and we no longer care about them ... super.dispose(); - this.decorations.dispose(); - this.lines.dispose(); + this._decorations.dispose(); + this._lines.dispose(); this.invalidateMinimapColorCache(); - this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); + this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); + this._eventDispatcher.dispose(); + } + + public addViewEventHandler(eventHandler: ViewEventHandler): void { + this._eventDispatcher.addViewEventHandler(eventHandler); + } + + public removeViewEventHandler(eventHandler: ViewEventHandler): void { + this._eventDispatcher.removeViewEventHandler(eventHandler); + } + + private _updateConfigurationViewLineCountNow(): void { + this._configuration.setViewLineCount(this._lines.getViewLineCount()); } public tokenizeViewport(): void { @@ -150,51 +173,66 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public setHasFocus(hasFocus: boolean): void { - this.hasFocus = hasFocus; + this._hasFocus = hasFocus; + this._cursor.setHasFocus(hasFocus); + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewFocusChangedEvent(hasFocus)); + this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus)); } - private _onConfigurationChanged(eventsCollector: viewEvents.ViewEventsCollector, e: ConfigurationChangedEvent): void { + public onDidColorThemeChange(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent()); + } + + private _onConfigurationChanged(eventsCollector: ViewModelEventsCollector, e: ConfigurationChangedEvent): void { // We might need to restore the current centered view range, so save it (if available) let previousViewportStartModelPosition: Position | null = null; - if (this.viewportStartLine !== -1) { - let previousViewportStartViewPosition = new Position(this.viewportStartLine, this.getLineMinColumn(this.viewportStartLine)); + if (this._viewportStartLine !== -1) { + let previousViewportStartViewPosition = new Position(this._viewportStartLine, this.getLineMinColumn(this._viewportStartLine)); previousViewportStartModelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(previousViewportStartViewPosition); } let restorePreviousViewportStart = false; - const options = this.configuration.options; + const options = this._configuration.options; const fontInfo = options.get(EditorOption.fontInfo); - const wrappingAlgorithm = options.get(EditorOption.wrappingAlgorithm); + const wrappingStrategy = options.get(EditorOption.wrappingStrategy); const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - if (this.lines.setWrappingSettings(fontInfo, wrappingAlgorithm, wrappingInfo.wrappingColumn, wrappingIndent)) { - eventsCollector.emit(new viewEvents.ViewFlushedEvent()); - eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); - this.decorations.onLineMappingChanged(); + if (this._lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) { + eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null)); + this._cursor.onLineMappingChanged(eventsCollector); + this._decorations.onLineMappingChanged(); this.viewLayout.onFlushed(this.getLineCount()); if (this.viewLayout.getCurrentScrollTop() !== 0) { // Never change the scroll position from 0 to something else... restorePreviousViewportStart = true; } + + this._updateConfigurationViewLineCount.schedule(); } if (e.hasChanged(EditorOption.readOnly)) { // Must read again all decorations due to readOnly filtering - this.decorations.reset(); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + this._decorations.reset(); + eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null)); } - eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e)); + eventsCollector.emitViewEvent(new viewEvents.ViewConfigurationChangedEvent(e)); this.viewLayout.onConfigurationChanged(e); if (restorePreviousViewportStart && previousViewportStartModelPosition) { const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(previousViewportStartModelPosition); const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber); - this.viewLayout.setScrollPositionNow({ scrollTop: viewPositionTop + this.viewportStartLineDelta }); + this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, ScrollType.Immediate); + } + + if (CursorConfiguration.shouldRecreate(e)) { + this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration); + this._cursor.updateConfiguration(this.cursorConfig); } } @@ -202,7 +240,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this._register(this.model.onDidChangeRawContentFast((e) => { try { - const eventsCollector = this._beginEmit(); + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); let hadOtherModelChange = false; let hadModelLineChangeThatChangedLineMapping = false; @@ -211,7 +249,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const versionId = e.versionId; // Do a first pass to compute line mappings, and a second pass to actually interpret them - const lineBreaksComputer = this.lines.createLineBreaksComputer(); + const lineBreaksComputer = this._lines.createLineBreaksComputer(); for (const change of changes) { switch (change.changeType) { case textModelEvents.RawContentChangedType.LinesInserted: { @@ -233,17 +271,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel switch (change.changeType) { case textModelEvents.RawContentChangedType.Flush: { - this.lines.onModelFlushed(); - eventsCollector.emit(new viewEvents.ViewFlushedEvent()); - this.decorations.reset(); + this._lines.onModelFlushed(); + eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent()); + this._decorations.reset(); this.viewLayout.onFlushed(this.getLineCount()); hadOtherModelChange = true; break; } case textModelEvents.RawContentChangedType.LinesDeleted: { - const linesDeletedEvent = this.lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber); + const linesDeletedEvent = this._lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber); if (linesDeletedEvent !== null) { - eventsCollector.emit(linesDeletedEvent); + eventsCollector.emitViewEvent(linesDeletedEvent); this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber); } hadOtherModelChange = true; @@ -253,9 +291,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const insertedLineBreaks = lineBreaks.slice(lineBreaksOffset, lineBreaksOffset + change.detail.length); lineBreaksOffset += change.detail.length; - const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks); + const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks); if (linesInsertedEvent !== null) { - eventsCollector.emit(linesInsertedEvent); + eventsCollector.emitViewEvent(linesInsertedEvent); this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); } hadOtherModelChange = true; @@ -265,17 +303,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const changedLineBreakData = lineBreaks[lineBreaksOffset]; lineBreaksOffset++; - const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData); + const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this._lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData); hadModelLineChangeThatChangedLineMapping = lineMappingChanged; if (linesChangedEvent) { - eventsCollector.emit(linesChangedEvent); + eventsCollector.emitViewEvent(linesChangedEvent); } if (linesInsertedEvent) { - eventsCollector.emit(linesInsertedEvent); + eventsCollector.emitViewEvent(linesInsertedEvent); this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); } if (linesDeletedEvent) { - eventsCollector.emit(linesDeletedEvent); + eventsCollector.emitViewEvent(linesDeletedEvent); this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber); } break; @@ -286,31 +324,40 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } } } - this.lines.acceptVersionId(versionId); + this._lines.acceptVersionId(versionId); this.viewLayout.onHeightMaybeChanged(); if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) { - eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); - this.decorations.onLineMappingChanged(); + eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null)); + this._cursor.onLineMappingChanged(eventsCollector); + this._decorations.onLineMappingChanged(); } } finally { - this._endEmit(); + this._eventDispatcher.endEmitViewEvents(); } // Update the configuration and reset the centered view line - this.viewportStartLine = -1; - this.configuration.setMaxLineNumber(this.model.getLineCount()); + this._viewportStartLine = -1; + this._configuration.setMaxLineNumber(this.model.getLineCount()); + this._updateConfigurationViewLineCountNow(); // Recover viewport - if (!this.hasFocus && this.model.getAttachedEditorCount() >= 2 && this.viewportStartLineTrackedRange) { - const modelRange = this.model._getTrackedRange(this.viewportStartLineTrackedRange); + if (!this._hasFocus && this.model.getAttachedEditorCount() >= 2 && this._viewportStartLineTrackedRange) { + const modelRange = this.model._getTrackedRange(this._viewportStartLineTrackedRange); if (modelRange) { const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelRange.getStartPosition()); const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber); - this.viewLayout.setScrollPositionNow({ scrollTop: viewPositionTop + this.viewportStartLineDelta }); + this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, ScrollType.Immediate); } } + + try { + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); + this._cursor.onModelContentChanged(eventsCollector, e); + } finally { + this._eventDispatcher.endEmitViewEvents(); + } })); this._register(this.model.onDidChangeTokens((e) => { @@ -324,12 +371,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel toLineNumber: viewEndLineNumber }; } - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewTokensChangedEvent(viewRanges)); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensChangedEvent(viewRanges)); if (e.tokenizationSupportChanged) { this._tokenizeViewportSoon.schedule(); @@ -337,62 +379,84 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel })); this._register(this.model.onDidChangeLanguageConfiguration((e) => { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewLanguageConfigurationEvent()); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent()); + this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration); + this._cursor.updateConfiguration(this.cursorConfig); + })); + + this._register(this.model.onDidChangeLanguage((e) => { + this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration); + this._cursor.updateConfiguration(this.cursorConfig); })); this._register(this.model.onDidChangeOptions((e) => { // A tab size change causes a line mapping changed event => all view parts will repaint OK, no further event needed here - if (this.lines.setTabSize(this.model.getOptions().tabSize)) { - this.decorations.onLineMappingChanged(); - this.viewLayout.onFlushed(this.getLineCount()); + if (this._lines.setTabSize(this.model.getOptions().tabSize)) { try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewFlushedEvent()); - eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); + eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null)); + this._cursor.onLineMappingChanged(eventsCollector); + this._decorations.onLineMappingChanged(); + this.viewLayout.onFlushed(this.getLineCount()); } finally { - this._endEmit(); + this._eventDispatcher.endEmitViewEvents(); } + this._updateConfigurationViewLineCount.schedule(); } + + this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration); + this._cursor.updateConfiguration(this.cursorConfig); })); this._register(this.model.onDidChangeDecorations((e) => { - this.decorations.onModelDecorationsChanged(); - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); - } finally { - this._endEmit(); - } + this._decorations.onModelDecorationsChanged(); + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e)); })); } public setHiddenAreas(ranges: Range[]): void { try { - const eventsCollector = this._beginEmit(); - let lineMappingChanged = this.lines.setHiddenAreas(ranges); + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); + let lineMappingChanged = this._lines.setHiddenAreas(ranges); if (lineMappingChanged) { - eventsCollector.emit(new viewEvents.ViewFlushedEvent()); - eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); - this.decorations.onLineMappingChanged(); + eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent()); + eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null)); + this._cursor.onLineMappingChanged(eventsCollector); + this._decorations.onLineMappingChanged(); this.viewLayout.onFlushed(this.getLineCount()); this.viewLayout.onHeightMaybeChanged(); } } finally { - this._endEmit(); + this._eventDispatcher.endEmitViewEvents(); } + this._updateConfigurationViewLineCount.schedule(); + } + + public getVisibleRangesPlusViewportAboveBelow(): Range[] { + const layoutInfo = this._configuration.options.get(EditorOption.layoutInfo); + const lineHeight = this._configuration.options.get(EditorOption.lineHeight); + const linesAround = Math.max(20, Math.round(layoutInfo.height / lineHeight)); + const partialData = this.viewLayout.getLinesViewportData(); + const startViewLineNumber = Math.max(1, partialData.completelyVisibleStartLineNumber - linesAround); + const endViewLineNumber = Math.min(this.getLineCount(), partialData.completelyVisibleEndLineNumber + linesAround); + + return this._toModelVisibleRanges(new Range( + startViewLineNumber, this.getLineMinColumn(startViewLineNumber), + endViewLineNumber, this.getLineMaxColumn(endViewLineNumber) + )); } public getVisibleRanges(): Range[] { const visibleViewRange = this.getCompletelyVisibleViewRange(); + return this._toModelVisibleRanges(visibleViewRange); + } + + private _toModelVisibleRanges(visibleViewRange: Range): Range[] { const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange); - const hiddenAreas = this.lines.getHiddenAreas(); + const hiddenAreas = this._lines.getHiddenAreas(); if (hiddenAreas.length === 0) { return [visibleRange]; @@ -497,48 +561,48 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.model.getOptions().tabSize; } - public getOptions(): TextModelResolvedOptions { + public getTextModelOptions(): TextModelResolvedOptions { return this.model.getOptions(); } public getLineCount(): number { - return this.lines.getViewLineCount(); + return this._lines.getViewLineCount(); } /** * Gives a hint that a lot of requests are about to come in for these line numbers. */ public setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void { - this.viewportStartLine = startLineNumber; + this._viewportStartLine = startLineNumber; let position = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(startLineNumber, this.getLineMinColumn(startLineNumber))); - this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); + this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); const viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber); const scrollTop = this.viewLayout.getCurrentScrollTop(); - this.viewportStartLineDelta = scrollTop - viewportStartLineTop; + this._viewportStartLineDelta = scrollTop - viewportStartLineTop; } public getActiveIndentGuide(lineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo { - return this.lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber); + return this._lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber); } public getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[] { - return this.lines.getViewLinesIndentGuides(startLineNumber, endLineNumber); + return this._lines.getViewLinesIndentGuides(startLineNumber, endLineNumber); } public getLineContent(lineNumber: number): string { - return this.lines.getViewLineContent(lineNumber); + return this._lines.getViewLineContent(lineNumber); } public getLineLength(lineNumber: number): number { - return this.lines.getViewLineLength(lineNumber); + return this._lines.getViewLineLength(lineNumber); } public getLineMinColumn(lineNumber: number): number { - return this.lines.getViewLineMinColumn(lineNumber); + return this._lines.getViewLineMinColumn(lineNumber); } public getLineMaxColumn(lineNumber: number): number { - return this.lines.getViewLineMaxColumn(lineNumber); + return this._lines.getViewLineMaxColumn(lineNumber); } public getLineFirstNonWhitespaceColumn(lineNumber: number): number { @@ -558,15 +622,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[] { - return this.decorations.getDecorationsViewportData(visibleRange).decorations; + return this._decorations.getDecorationsViewportData(visibleRange).decorations; } public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData { let mightContainRTL = this.model.mightContainRTL(); let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII(); let tabSize = this.getTabSize(); - let lineData = this.lines.getViewLineData(lineNumber); - let allInlineDecorations = this.decorations.getDecorationsViewportData(visibleRange).inlineDecorations; + let lineData = this._lines.getViewLineData(lineNumber); + let allInlineDecorations = this._decorations.getDecorationsViewportData(visibleRange).inlineDecorations; let inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber]; return new ViewLineRenderingData( @@ -584,19 +648,19 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public getViewLineData(lineNumber: number): ViewLineData { - return this.lines.getViewLineData(lineNumber); + return this._lines.getViewLineData(lineNumber); } public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { - let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); + let result = this._lines.getViewLinesData(startLineNumber, endLineNumber, needed); return new MinimapLinesRenderingData( this.getTabSize(), result ); } - public getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations { - return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme); + public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations { + return this._lines.getAllOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options), theme); } public invalidateOverviewRulerColorCache(): void { @@ -738,7 +802,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber)); } - const fontInfo = this.configuration.options.get(EditorOption.fontInfo); + const fontInfo = this._configuration.options.get(EditorOption.fontInfo); const colorMap = this._getColorMap(); const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`; @@ -796,4 +860,150 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } return result; } + + //#region model + + public pushStackElement(): void { + this.model.pushStackElement(); + } + + //#endregion + + //#region cursor operations + + public getPrimaryCursorState(): CursorState { + return this._cursor.getPrimaryCursorState(); + } + public getLastAddedCursorIndex(): number { + return this._cursor.getLastAddedCursorIndex(); + } + public getCursorStates(): CursorState[] { + return this._cursor.getCursorStates(); + } + public setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void { + this._withViewEventsCollector(eventsCollector => this._cursor.setStates(eventsCollector, source, reason, states)); + } + public getCursorColumnSelectData(): IColumnSelectData { + return this._cursor.getCursorColumnSelectData(); + } + public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void { + this._cursor.setCursorColumnSelectData(columnSelectData); + } + public getPrevEditOperationType(): EditOperationType { + return this._cursor.getPrevEditOperationType(); + } + public setPrevEditOperationType(type: EditOperationType): void { + this._cursor.setPrevEditOperationType(type); + } + public getSelection(): Selection { + return this._cursor.getSelection(); + } + public getSelections(): Selection[] { + return this._cursor.getSelections(); + } + public getPosition(): Position { + return this._cursor.getPrimaryCursorState().modelState.position; + } + public setSelections(source: string | null | undefined, selections: readonly ISelection[]): void { + this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections)); + } + public saveCursorState(): ICursorState[] { + return this._cursor.saveState(); + } + public restoreCursorState(states: ICursorState[]): void { + this._withViewEventsCollector(eventsCollector => this._cursor.restoreState(eventsCollector, states)); + } + + private _executeCursorEdit(callback: (eventsCollector: ViewModelEventsCollector) => void): void { + if (this._cursor.context.cursorConfig.readOnly) { + // we cannot edit when read only... + this._eventDispatcher.emitOutgoingEvent(new ReadOnlyEditAttemptEvent()); + return; + } + this._withViewEventsCollector(callback); + } + public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void { + this._executeCursorEdit(eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer)); + } + public startComposition(): void { + this._cursor.setIsDoingComposition(true); + this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector)); + } + public endComposition(source?: string | null | undefined): void { + this._cursor.setIsDoingComposition(false); + this._executeCursorEdit(eventsCollector => this._cursor.endComposition(eventsCollector, source)); + } + public type(text: string, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source)); + } + public replacePreviousChar(text: string, replaceCharCnt: number, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source)); + } + public paste(text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source)); + } + public cut(source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.cut(eventsCollector, source)); + } + public executeCommand(command: ICommand, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.executeCommand(eventsCollector, command, source)); + } + public executeCommands(commands: ICommand[], source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.executeCommands(eventsCollector, commands, source)); + } + public revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void { + this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(eventsCollector, source, revealHorizontal, ScrollType.Smooth)); + } + public revealTopMostCursor(source: string | null | undefined): void { + const viewPosition = this._cursor.getTopMostViewPosition(); + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth))); + } + public revealBottomMostCursor(source: string | null | undefined): void { + const viewPosition = this._cursor.getBottomMostViewPosition(); + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth))); + } + public revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: ScrollType): void { + this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType))); + } + + //#endregion + + //#region viewLayout + public getVerticalOffsetForLineNumber(viewLineNumber: number): number { + return this.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber); + } + public getScrollTop(): number { + return this.viewLayout.getCurrentScrollTop(); + } + public setScrollTop(newScrollTop: number, scrollType: ScrollType): void { + this.viewLayout.setScrollPosition({ scrollTop: newScrollTop }, scrollType); + } + public setScrollPosition(position: INewScrollPosition, type: ScrollType): void { + this.viewLayout.setScrollPosition(position, type); + } + public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void { + this.viewLayout.deltaScrollNow(deltaScrollLeft, deltaScrollTop); + } + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void { + const hadAChange = this.viewLayout.changeWhitespace(callback); + if (hadAChange) { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewZonesChangedEvent()); + this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent()); + } + } + public setMaxLineWidth(maxLineWidth: number): void { + this.viewLayout.setMaxLineWidth(maxLineWidth); + } + //#endregion + + private _withViewEventsCollector(callback: (eventsCollector: ViewModelEventsCollector) => void): void { + try { + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); + callback(eventsCollector); + } finally { + this._eventDispatcher.endEmitViewEvents(); + } + } } diff --git a/src/vs/platform/files/node/files.ts b/src/vs/editor/contrib/anchorSelect/anchorSelect.css similarity index 79% rename from src/vs/platform/files/node/files.ts rename to src/vs/editor/contrib/anchorSelect/anchorSelect.css index bbb85376228..46ea76d2011 100644 --- a/src/vs/platform/files/node/files.ts +++ b/src/vs/editor/contrib/anchorSelect/anchorSelect.css @@ -3,5 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export const MIN_MAX_MEMORY_SIZE_MB = 2048; -export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; +.monaco-editor .selection-anchor { + background-color: #007ACC; + width: 2px !important; +} diff --git a/src/vs/editor/contrib/anchorSelect/anchorSelect.ts b/src/vs/editor/contrib/anchorSelect/anchorSelect.ts new file mode 100644 index 00000000000..80011cf5909 --- /dev/null +++ b/src/vs/editor/contrib/anchorSelect/anchorSelect.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./anchorSelect'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Selection } from 'vs/editor/common/core/selection'; +import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { TrackedRangeStickiness } from 'vs/editor/common/model'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { alert } from 'vs/base/browser/ui/aria/aria'; + +export const SelectionAnchorSet = new RawContextKey('selectionAnchorSet', false); + +class SelectionAnchorController implements IEditorContribution { + + public static readonly ID = 'editor.contrib.selectionAnchorController'; + + static get(editor: ICodeEditor): SelectionAnchorController { + return editor.getContribution(SelectionAnchorController.ID); + } + + private decorationId: string | undefined; + private selectionAnchorSetContextKey: IContextKey; + private modelChangeListener: IDisposable; + + constructor( + private editor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.selectionAnchorSetContextKey = SelectionAnchorSet.bindTo(contextKeyService); + this.modelChangeListener = editor.onDidChangeModel(() => this.selectionAnchorSetContextKey.reset()); + } + + setSelectionAnchor(): void { + if (this.editor.hasModel()) { + const position = this.editor.getPosition(); + const previousDecorations = this.decorationId ? [this.decorationId] : []; + const newDecorationId = this.editor.deltaDecorations(previousDecorations, [{ + range: Selection.fromPositions(position, position), + options: { + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + hoverMessage: new MarkdownString().appendText(localize('selectionAnchor', "Selection Anchor")), + className: 'selection-anchor' + } + }]); + this.decorationId = newDecorationId[0]; + this.selectionAnchorSetContextKey.set(!!this.decorationId); + alert(localize('anchorSet', "Anchor set at {0}:{1}", position.lineNumber, position.column)); + } + } + + goToSelectionAnchor(): void { + if (this.editor.hasModel() && this.decorationId) { + const anchorPosition = this.editor.getModel().getDecorationRange(this.decorationId); + if (anchorPosition) { + this.editor.setPosition(anchorPosition.getStartPosition()); + } + } + } + + selectFromAnchorToCursor(): void { + if (this.editor.hasModel() && this.decorationId) { + const start = this.editor.getModel().getDecorationRange(this.decorationId); + if (start) { + const end = this.editor.getPosition(); + this.editor.setSelection(Selection.fromPositions(start.getStartPosition(), end)); + this.cancelSelectionAnchor(); + } + } + } + + cancelSelectionAnchor(): void { + if (this.decorationId) { + this.editor.deltaDecorations([this.decorationId], []); + this.decorationId = undefined; + this.selectionAnchorSetContextKey.set(false); + } + } + + dispose(): void { + this.cancelSelectionAnchor(); + this.modelChangeListener.dispose(); + } +} + +class SetSelectionAnchor extends EditorAction { + constructor() { + super({ + id: 'editor.action.setSelectionAnchor', + label: localize('setSelectionAnchor', "Set Selection Anchor"), + alias: 'Set Selection Anchor', + precondition: undefined, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_B), + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.setSelectionAnchor(); + } +} + +class GoToSelectionAnchor extends EditorAction { + constructor() { + super({ + id: 'editor.action.goToSelectionAnchor', + label: localize('goToSelectionAnchor', "Go to Selection Anchor"), + alias: 'Go to Selection Anchor', + precondition: SelectionAnchorSet, + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.goToSelectionAnchor(); + } +} + +class SelectFromAnchorToCursor extends EditorAction { + constructor() { + super({ + id: 'editor.action.selectFromAnchorToCursor', + label: localize('selectFromAnchorToCursor', "Select from Anchor to Cursor"), + alias: 'Select from Anchor to Cursor', + precondition: SelectionAnchorSet, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.selectFromAnchorToCursor(); + } +} + +class CancelSelectionAnchor extends EditorAction { + constructor() { + super({ + id: 'editor.action.cancelSelectionAnchor', + label: localize('cancelSelectionAnchor', "Cancel Selection Anchor"), + alias: 'Cancel Selection Anchor', + precondition: SelectionAnchorSet, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyCode.Escape, + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.cancelSelectionAnchor(); + } +} + +registerEditorContribution(SelectionAnchorController.ID, SelectionAnchorController); +registerEditorAction(SetSelectionAnchor); +registerEditorAction(GoToSelectionAnchor); +registerEditorAction(SelectFromAnchorToCursor); +registerEditorAction(CancelSelectionAnchor); diff --git a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts index 87886115932..0a11a1a5f99 100644 --- a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts +++ b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/bracketMatching'; @@ -31,10 +31,10 @@ suite('bracket matching', () => { test('issue #183: jump to matching bracket position', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); + withTestCodeEditor(null, { model: model }, (editor) => { + let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // start on closing bracket editor.setPosition(new Position(1, 20)); @@ -63,10 +63,10 @@ suite('bracket matching', () => { test('Jump to next bracket', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); + withTestCodeEditor(null, { model: model }, (editor) => { + let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // start position between brackets editor.setPosition(new Position(1, 16)); @@ -100,10 +100,10 @@ suite('bracket matching', () => { test('Select to next bracket', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); + withTestCodeEditor(null, { model: model }, (editor) => { + let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // start position in open brackets @@ -152,10 +152,10 @@ suite('bracket matching', () => { '};', ].join('\n'); const mode = new BracketMode(); - const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier()); + const model = createTextModel(text, undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); + withTestCodeEditor(null, { model: model }, (editor) => { + const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); editor.setPosition(new Position(3, 5)); bracketMatchingController.jumpToBracket(); @@ -177,10 +177,10 @@ suite('bracket matching', () => { '};', ].join('\n'); const mode = new BracketMode(); - const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier()); + const model = createTextModel(text, undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); + withTestCodeEditor(null, { model: model }, (editor) => { + const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); editor.setPosition(new Position(3, 5)); bracketMatchingController.selectToBracket(false); @@ -195,10 +195,10 @@ suite('bracket matching', () => { test('issue #45369: Select to Bracket with multicursor', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('{ } { } { }', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('{ } { } { }', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); + withTestCodeEditor(null, { model: model }, (editor) => { + let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // cursors inside brackets become selections of the entire bracket contents editor.setSelections([ diff --git a/src/vs/editor/contrib/caretOperations/caretOperations.ts b/src/vs/editor/contrib/caretOperations/caretOperations.ts index 3b715b871e7..abd6ab267fd 100644 --- a/src/vs/editor/contrib/caretOperations/caretOperations.ts +++ b/src/vs/editor/contrib/caretOperations/caretOperations.ts @@ -42,8 +42,8 @@ class MoveCaretLeftAction extends MoveCaretAction { constructor() { super(true, { id: 'editor.action.moveCarretLeftAction', - label: nls.localize('caret.moveLeft', "Move Caret Left"), - alias: 'Move Caret Left', + label: nls.localize('caret.moveLeft', "Move Selected Text Left"), + alias: 'Move Selected Text Left', precondition: EditorContextKeys.writable }); } @@ -53,8 +53,8 @@ class MoveCaretRightAction extends MoveCaretAction { constructor() { super(false, { id: 'editor.action.moveCarretRightAction', - label: nls.localize('caret.moveRight', "Move Caret Right"), - alias: 'Move Caret Right', + label: nls.localize('caret.moveRight', "Move Selected Text Right"), + alias: 'Move Selected Text Right', precondition: EditorContextKeys.writable }); } diff --git a/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts b/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts index 0ce4feb6174..81db0fd6217 100644 --- a/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts +++ b/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts @@ -13,66 +13,43 @@ export class MoveCaretCommand implements ICommand { private readonly _selection: Selection; private readonly _isMovingLeft: boolean; - private _cutStartIndex: number; - private _cutEndIndex: number; - private _moved: boolean; - - private _selectionId: string | null; - constructor(selection: Selection, isMovingLeft: boolean) { this._selection = selection; this._isMovingLeft = isMovingLeft; - this._cutStartIndex = -1; - this._cutEndIndex = -1; - this._moved = false; - this._selectionId = null; } public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { - let s = this._selection; - this._selectionId = builder.trackSelection(s); - if (s.startLineNumber !== s.endLineNumber) { + if (this._selection.startLineNumber !== this._selection.endLineNumber || this._selection.isEmpty()) { return; } - if (this._isMovingLeft && s.startColumn === 0) { - return; - } else if (!this._isMovingLeft && s.endColumn === model.getLineMaxColumn(s.startLineNumber)) { + const lineNumber = this._selection.startLineNumber; + const startColumn = this._selection.startColumn; + const endColumn = this._selection.endColumn; + if (this._isMovingLeft && startColumn === 1) { + return; + } + if (!this._isMovingLeft && endColumn === model.getLineMaxColumn(lineNumber)) { return; } - - let lineNumber = s.selectionStartLineNumber; - let lineContent = model.getLineContent(lineNumber); - - let left: string; - let middle: string; - let right: string; if (this._isMovingLeft) { - left = lineContent.substring(0, s.startColumn - 2); - middle = lineContent.substring(s.startColumn - 1, s.endColumn - 1); - right = lineContent.substring(s.startColumn - 2, s.startColumn - 1) + lineContent.substring(s.endColumn - 1); + const rangeBefore = new Range(lineNumber, startColumn - 1, lineNumber, startColumn); + const charBefore = model.getValueInRange(rangeBefore); + builder.addEditOperation(rangeBefore, null); + builder.addEditOperation(new Range(lineNumber, endColumn, lineNumber, endColumn), charBefore); } else { - left = lineContent.substring(0, s.startColumn - 1) + lineContent.substring(s.endColumn - 1, s.endColumn); - middle = lineContent.substring(s.startColumn - 1, s.endColumn - 1); - right = lineContent.substring(s.endColumn); + const rangeAfter = new Range(lineNumber, endColumn, lineNumber, endColumn + 1); + const charAfter = model.getValueInRange(rangeAfter); + builder.addEditOperation(rangeAfter, null); + builder.addEditOperation(new Range(lineNumber, startColumn, lineNumber, startColumn), charAfter); } - - let newLineContent = left + middle + right; - - builder.addEditOperation(new Range(lineNumber, 1, lineNumber, model.getLineMaxColumn(lineNumber)), null); - builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), newLineContent); - - this._cutStartIndex = s.startColumn + (this._isMovingLeft ? -1 : 1); - this._cutEndIndex = this._cutStartIndex + s.endColumn - s.startColumn; - this._moved = true; } public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { - let result = helper.getTrackedSelection(this._selectionId!); - if (this._moved) { - result = result.setStartPosition(result.startLineNumber, this._cutStartIndex); - result = result.setEndPosition(result.startLineNumber, this._cutEndIndex); + if (this._isMovingLeft) { + return new Selection(this._selection.startLineNumber, this._selection.startColumn - 1, this._selection.endLineNumber, this._selection.endColumn - 1); + } else { + return new Selection(this._selection.startLineNumber, this._selection.startColumn + 1, this._selection.endLineNumber, this._selection.endColumn + 1); } - return result; } } diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index cb106b53b82..6860a7bb610 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -3,196 +3,134 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./clipboard'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput'; +import { CopyOptions, InMemoryClipboardMetadataManager } from 'vs/editor/browser/controller/textAreaInput'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, IActionOptions, ICommandKeybindingsOptions, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, registerEditorAction, Command, MultiCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { MenuId } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Handler } from 'vs/editor/common/editorCommon'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; const supportsCut = (platform.isNative || document.queryCommandSupported('cut')); const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); // IE and Edge have trouble with setting html content in clipboard -const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE); -// Chrome incorrectly returns true for document.queryCommandSupported('paste') -// when the paste feature is available but the calling script has insufficient -// privileges to actually perform the action -const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste'))); +const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge); +// Firefox only supports navigator.clipboard.readText() in browser extensions. +// See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility +const supportsPaste = (browser.isFirefox ? document.queryCommandSupported('paste') : true); -type ExecCommand = 'cut' | 'copy' | 'paste'; - -abstract class ExecCommandAction extends EditorAction { - - private readonly browserCommand: ExecCommand; - - constructor(browserCommand: ExecCommand, opts: IActionOptions) { - super(opts); - this.browserCommand = browserCommand; - } - - public runCommand(accessor: ServicesAccessor, args: any): void { - let focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); - // Only if editor text focus (i.e. not if editor has widget focus). - if (focusedEditor && focusedEditor.hasTextFocus()) { - focusedEditor.trigger('keyboard', this.id, args); - return; - } - - document.execCommand(this.browserCommand); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - editor.focus(); - document.execCommand(this.browserCommand); - } +function registerCommand(command: T): T { + command.register(); + return command; } -class ExecCommandCutAction extends ExecCommandAction { - - constructor() { - let kbOpts: ICommandKeybindingsOptions | undefined = { - kbExpr: EditorContextKeys.textInputFocus, +export const CutAction = supportsCut ? registerCommand(new MultiCommand({ + id: 'editor.action.clipboardCutAction', + precondition: undefined, + kbOpts: ( + // Do not bind cut keybindings in the browser, + // since browsers do that for us and it avoids security prompts + platform.isNative ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, weight: KeybindingWeight.EditorContrib - }; - // Do not bind cut keybindings in the browser, + } : undefined + ), + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), + order: 1 + }, { + menuId: MenuId.EditorContext, + group: CLIPBOARD_CONTEXT_MENU_GROUP, + title: nls.localize('actions.clipboard.cutLabel', "Cut"), + when: EditorContextKeys.writable, + order: 1, + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('actions.clipboard.cutLabel', "Cut"), + order: 1 + }] +})) : undefined; + +export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({ + id: 'editor.action.clipboardCopyAction', + precondition: undefined, + kbOpts: ( + // Do not bind copy keybindings in the browser, // since browsers do that for us and it avoids security prompts - if (!platform.isNative) { - kbOpts = undefined; - } - super('cut', { - id: 'editor.action.clipboardCutAction', - label: nls.localize('actions.clipboard.cutLabel', "Cut"), - alias: 'Cut', - precondition: EditorContextKeys.writable, - kbOpts: kbOpts, - contextMenuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 1 - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), - order: 1 - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!editor.hasModel()) { - return; - } - - const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard); - - if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { - return; - } - - super.run(accessor, editor); - } -} - -class ExecCommandCopyAction extends ExecCommandAction { - - constructor() { - let kbOpts: ICommandKeybindingsOptions | undefined = { - kbExpr: EditorContextKeys.textInputFocus, + platform.isNative ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, weight: KeybindingWeight.EditorContrib - }; - // Do not bind copy keybindings in the browser, - // since browsers do that for us and it avoids security prompts - if (!platform.isNative) { - kbOpts = undefined; - } + } : undefined + ), + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), + order: 2 + }, { + menuId: MenuId.EditorContext, + group: CLIPBOARD_CONTEXT_MENU_GROUP, + title: nls.localize('actions.clipboard.copyLabel', "Copy"), + order: 2, + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('actions.clipboard.copyLabel', "Copy"), + order: 1 + }] +})) : undefined; - super('copy', { - id: 'editor.action.clipboardCopyAction', - label: nls.localize('actions.clipboard.copyLabel', "Copy"), - alias: 'Copy', - precondition: undefined, - kbOpts: kbOpts, - contextMenuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 2 - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), - order: 2 - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!editor.hasModel()) { - return; - } - - const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard); - - if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { - return; - } - - super.run(accessor, editor); - } -} - -class ExecCommandPasteAction extends ExecCommandAction { - - constructor() { - let kbOpts: ICommandKeybindingsOptions | undefined = { - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, - weight: KeybindingWeight.EditorContrib - }; +export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({ + id: 'editor.action.clipboardPasteAction', + precondition: undefined, + kbOpts: ( // Do not bind paste keybindings in the browser, // since browsers do that for us and it avoids security prompts - if (!platform.isNative) { - kbOpts = undefined; - } + platform.isNative ? { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + weight: KeybindingWeight.EditorContrib + } : undefined + ), + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), + order: 3 + }, { + menuId: MenuId.EditorContext, + group: CLIPBOARD_CONTEXT_MENU_GROUP, + title: nls.localize('actions.clipboard.pasteLabel', "Paste"), + when: EditorContextKeys.writable, + order: 3, + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('actions.clipboard.pasteLabel', "Paste"), + order: 1 + }] +})) : undefined; - super('paste', { - id: 'editor.action.clipboardPasteAction', - label: nls.localize('actions.clipboard.pasteLabel', "Paste"), - alias: 'Paste', - precondition: EditorContextKeys.writable, - kbOpts: kbOpts, - contextMenuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 3 - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), - order: 3 - } - }); - } -} - -class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { +class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { constructor() { - super('copy', { + super({ id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction', label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"), alias: 'Copy With Syntax Highlighting', @@ -217,20 +155,90 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { } CopyOptions.forceCopyWithSyntaxHighlighting = true; - super.run(accessor, editor); + editor.focus(); + document.execCommand('copy'); CopyOptions.forceCopyWithSyntaxHighlighting = false; } } -if (supportsCut) { - registerEditorAction(ExecCommandCutAction); +function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy'): void { + if (!target) { + return; + } + + // 1. handle case when focus is in editor. + target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { + // Only if editor text focus (i.e. not if editor has widget focus). + const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (focusedEditor && focusedEditor.hasTextFocus()) { + // Do not execute if there is no selection and empty selection clipboard is off + const emptySelectionClipboard = focusedEditor.getOption(EditorOption.emptySelectionClipboard); + const selection = focusedEditor.getSelection(); + if (selection && selection.isEmpty() && !emptySelectionClipboard) { + return true; + } + document.execCommand(browserCommand); + return true; + } + return false; + }); + + // 2. (default) handle case when focus is somewhere else. + target.addImplementation(0, (accessor: ServicesAccessor, args: any) => { + document.execCommand(browserCommand); + return true; + }); } -if (supportsCopy) { - registerEditorAction(ExecCommandCopyAction); -} -if (supportsPaste) { - registerEditorAction(ExecCommandPasteAction); + +registerExecCommandImpl(CutAction, 'cut'); +registerExecCommandImpl(CopyAction, 'copy'); + +if (PasteAction) { + // 1. Paste: handle case when focus is in editor. + PasteAction.addImplementation(10000, (accessor: ServicesAccessor, args: any) => { + const codeEditorService = accessor.get(ICodeEditorService); + const clipboardService = accessor.get(IClipboardService); + + // Only if editor text focus (i.e. not if editor has widget focus). + const focusedEditor = codeEditorService.getFocusedCodeEditor(); + if (focusedEditor && focusedEditor.hasTextFocus()) { + const result = document.execCommand('paste'); + // Use the clipboard service if document.execCommand('paste') was not successful + if (!result && platform.isWeb) { + (async () => { + const clipboardText = await clipboardService.readText(); + if (clipboardText !== '') { + const metadata = InMemoryClipboardMetadataManager.INSTANCE.get(clipboardText); + let pasteOnNewLine = false; + let multicursorText: string[] | null = null; + let mode: string | null = null; + if (metadata) { + pasteOnNewLine = (focusedEditor.getOption(EditorOption.emptySelectionClipboard) && !!metadata.isFromEmptySelection); + multicursorText = (typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null); + mode = metadata.mode; + } + focusedEditor.trigger('keyboard', Handler.Paste, { + text: clipboardText, + pasteOnNewLine, + multicursorText, + mode + }); + } + })(); + return true; + } + return true; + } + return false; + }); + + // 2. Paste: (default) handle case when focus is somewhere else. + PasteAction.addImplementation(0, (accessor: ServicesAccessor, args: any) => { + document.execCommand('paste'); + return true; + }); } + if (supportsCopyWithSyntaxHighlighting) { registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction); } diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index b0a9aa8051f..a488d79cae1 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays'; +import { equals, flatten, isNonEmptyArray, mergeSort, coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; @@ -16,6 +16,7 @@ import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; +import { IProgress, Progress } from 'vs/platform/progress/common/progress'; export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; @@ -23,15 +24,46 @@ export const sourceActionCommandId = 'editor.action.sourceAction'; export const organizeImportsCommandId = 'editor.action.organizeImports'; export const fixAllCommandId = 'editor.action.fixAll'; +export class CodeActionItem { + + constructor( + readonly action: modes.CodeAction, + readonly provider: modes.CodeActionProvider | undefined, + ) { } + + async resolve(token: CancellationToken): Promise { + if (this.provider?.resolveCodeAction && !this.action.edit) { + let action: modes.CodeAction | undefined | null; + try { + action = await this.provider.resolveCodeAction(this.action, token); + } catch (err) { + onUnexpectedExternalError(err); + } + if (action) { + this.action.edit = action.edit; + } + } + return this; + } +} + export interface CodeActionSet extends IDisposable { - readonly validActions: readonly modes.CodeAction[]; - readonly allActions: readonly modes.CodeAction[]; + readonly validActions: readonly CodeActionItem[]; + readonly allActions: readonly CodeActionItem[]; readonly hasAutoFix: boolean; + + readonly documentation: readonly modes.Command[]; } class ManagedCodeActionSet extends Disposable implements CodeActionSet { - private static codeActionsComparator(a: modes.CodeAction, b: modes.CodeAction): number { + private static codeActionsComparator({ action: a }: CodeActionItem, { action: b }: CodeActionItem): number { + if (a.isPreferred && !b.isPreferred) { + return -1; + } else if (!a.isPreferred && b.isPreferred) { + return 1; + } + if (isNonEmptyArray(a.diagnostics)) { if (isNonEmptyArray(b.diagnostics)) { return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message); @@ -45,26 +77,34 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet { } } - public readonly validActions: readonly modes.CodeAction[]; - public readonly allActions: readonly modes.CodeAction[]; + public readonly validActions: readonly CodeActionItem[]; + public readonly allActions: readonly CodeActionItem[]; - public constructor(actions: readonly modes.CodeAction[], disposables: DisposableStore) { + public constructor( + actions: readonly CodeActionItem[], + public readonly documentation: readonly modes.Command[], + disposables: DisposableStore, + ) { super(); this._register(disposables); this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); - this.validActions = this.allActions.filter(action => !action.disabled); + this.validActions = this.allActions.filter(({ action }) => !action.disabled); } public get hasAutoFix() { - return this.validActions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred); + return this.validActions.some(({ action: fix }) => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred); } } + +const emptyCodeActionsResponse = { actions: [] as CodeActionItem[], documentation: undefined }; + export function getCodeActions( model: ITextModel, rangeOrSelection: Range | Selection, trigger: CodeActionTrigger, - token: CancellationToken + progress: IProgress, + token: CancellationToken, ): Promise { const filter = trigger.filter || {}; @@ -79,18 +119,28 @@ export function getCodeActions( const disposables = new DisposableStore(); const promises = providers.map(async provider => { try { + progress.report(provider); const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token); - if (cts.token.isCancellationRequested || !providedCodeActions) { - return []; + if (providedCodeActions) { + disposables.add(providedCodeActions); } - disposables.add(providedCodeActions); - return providedCodeActions.actions.filter(action => action && filtersAction(filter, action)); + + if (cts.token.isCancellationRequested) { + return emptyCodeActionsResponse; + } + + const filteredActions = (providedCodeActions?.actions || []).filter(action => action && filtersAction(filter, action)); + const documentation = getDocumentation(provider, filteredActions, filter.include); + return { + actions: filteredActions.map(action => new CodeActionItem(action, provider)), + documentation + }; } catch (err) { if (isPromiseCanceledError(err)) { throw err; } onUnexpectedExternalError(err); - return []; + return emptyCodeActionsResponse; } }); @@ -101,9 +151,11 @@ export function getCodeActions( } }); - return Promise.all(promises) - .then(flatten) - .then(actions => new ManagedCodeActionSet(actions, disposables)) + return Promise.all(promises).then(actions => { + const allActions = flatten(actions.map(x => x.actions)); + const allDocumentation = coalesce(actions.map(x => x.documentation)); + return new ManagedCodeActionSet(allActions, allDocumentation, disposables); + }) .finally(() => { listener.dispose(); cts.dispose(); @@ -125,8 +177,54 @@ function getCodeActionProviders( }); } +function getDocumentation( + provider: modes.CodeActionProvider, + providedCodeActions: readonly modes.CodeAction[], + only?: CodeActionKind +): modes.Command | undefined { + if (!provider.documentation) { + return undefined; + } + + const documentation = provider.documentation.map(entry => ({ kind: new CodeActionKind(entry.kind), command: entry.command })); + + if (only) { + let currentBest: { readonly kind: CodeActionKind, readonly command: modes.Command } | undefined; + for (const entry of documentation) { + if (entry.kind.contains(only)) { + if (!currentBest) { + currentBest = entry; + } else { + // Take best match + if (currentBest.kind.contains(entry.kind)) { + currentBest = entry; + } + } + } + } + if (currentBest) { + return currentBest?.command; + } + } + + // Otherwise, check to see if any of the provided actions match. + for (const action of providedCodeActions) { + if (!action.kind) { + continue; + } + + for (const entry of documentation) { + if (entry.kind.contains(new CodeActionKind(action.kind))) { + return entry.command; + } + } + } + + return undefined; +} + registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { - const { resource, rangeOrSelection, kind } = args; + const { resource, rangeOrSelection, kind, itemResolveCount } = args; if (!(resource instanceof URI)) { throw illegalArgument(); } @@ -150,8 +248,20 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, model, validatedRangeOrSelection, { type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, + Progress.None, CancellationToken.None); - setTimeout(() => codeActionSet.dispose(), 100); - return codeActionSet.validActions; + + const resolving: Promise[] = []; + const resolveCount = Math.min(codeActionSet.validActions.length, typeof itemResolveCount === 'number' ? itemResolveCount : 0); + for (let i = 0; i < resolveCount; i++) { + resolving.push(codeActionSet.validActions[i].resolve(CancellationToken.None)); + } + + try { + await Promise.all(resolving); + return codeActionSet.validActions.map(item => item.action); + } finally { + setTimeout(() => codeActionSet.dispose(), 100); + } }); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 185ebd86867..65c19a781f6 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; @@ -11,12 +12,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { IPosition } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; -import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; +import { CodeActionTriggerType } from 'vs/editor/common/modes'; +import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionUi } from 'vs/editor/contrib/codeAction/codeActionUi'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import * as nls from 'vs/nls'; @@ -39,7 +40,6 @@ function contextKeyForSupportedActions(kind: CodeActionKind) { const argsSchema: IJSONSchema = { type: 'object', - required: ['kind'], defaultSnippets: [{ body: { kind: '' } }], properties: { 'kind': { @@ -131,14 +131,14 @@ export class QuickFixController extends Disposable implements IEditorContributio return this._model.trigger(trigger); } - private _applyCodeAction(action: CodeAction): Promise { + private _applyCodeAction(action: CodeActionItem): Promise { return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor); } } export async function applyCodeAction( accessor: ServicesAccessor, - action: CodeAction, + item: CodeActionItem, editor?: ICodeEditor, ): Promise { const bulkEditService = accessor.get(IBulkEditService); @@ -158,18 +158,20 @@ export async function applyCodeAction( }; telemetryService.publicLog2('codeAction.applyCodeAction', { - codeActionTitle: action.title, - codeActionKind: action.kind, - codeActionIsPreferred: !!action.isPreferred, + codeActionTitle: item.action.title, + codeActionKind: item.action.kind, + codeActionIsPreferred: !!item.action.isPreferred, }); - if (action.edit) { - await bulkEditService.apply(action.edit, { editor }); + await item.resolve(CancellationToken.None); + + if (item.action.edit) { + await bulkEditService.apply(ResourceEdit.convert(item.action.edit), { editor, label: item.action.title }); } - if (action.command) { + if (item.action.command) { try { - await commandService.executeCommand(action.command.id, ...(action.command.arguments || [])); + await commandService.executeCommand(item.action.command.id, ...(item.action.command.arguments || [])); } catch (err) { const message = asMessage(err); notificationService.error( diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts index e732dec94c3..e4a05011d01 100644 --- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { getDomNodePagePosition } from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; import { canceled } from 'vs/base/common/errors'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; @@ -14,15 +13,15 @@ import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes'; -import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; +import { CodeAction, CodeActionProviderRegistry, Command } from 'vs/editor/common/modes'; +import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionTrigger, CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; interface CodeActionWidgetDelegate { - onSelectCodeAction: (action: CodeAction) => Promise; + onSelectCodeAction: (action: CodeActionItem) => Promise; } interface ResolveCodeActionKeybinding { @@ -36,10 +35,14 @@ class CodeActionAction extends Action { public readonly action: CodeAction, callback: () => Promise, ) { - super(action.command ? action.command.id : action.title, action.title, undefined, !action.disabled, callback); + super(action.command ? action.command.id : action.title, stripNewlines(action.title), undefined, !action.disabled, callback); } } +function stripNewlines(str: string): string { + return str.replace(/\r\n|\r|\n/g, ' '); +} + export interface CodeActionShowOptions { readonly includeDisabledActions: boolean; } @@ -84,12 +87,13 @@ export class CodeActionMenu extends Disposable { this._visible = true; this._showingActions.value = codeActions; - const menuActions = this.getMenuActions(trigger, actionsToShow); + const menuActions = this.getMenuActions(trigger, actionsToShow, codeActions.documentation); const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 }; const resolver = this._keybindingResolver.getResolver(); this._contextMenuService.showContextMenu({ + domForShadowRoot: this._editor.getDomNode()!, getAnchor: () => anchor, getActions: () => menuActions, onHide: () => { @@ -101,28 +105,34 @@ export class CodeActionMenu extends Disposable { }); } - private getMenuActions(trigger: CodeActionTrigger, actionsToShow: readonly CodeAction[]): IAction[] { - const toCodeActionAction = (action: CodeAction): CodeActionAction => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action)); + private getMenuActions( + trigger: CodeActionTrigger, + actionsToShow: readonly CodeActionItem[], + documentation: readonly Command[] + ): IAction[] { + const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item)); const result: IAction[] = actionsToShow .map(toCodeActionAction); + const allDocumentation: Command[] = [...documentation]; const model = this._editor.getModel(); if (model && result.length) { for (const provider of CodeActionProviderRegistry.all(model)) { if (provider._getAdditionalMenuItems) { - const items = provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow); - if (items.length) { - result.push(new Separator(), ...items.map(command => toCodeActionAction({ - title: command.title, - command: command, - }))); - } + allDocumentation.push(...provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow.map(item => item.action))); } } } + if (allDocumentation.length) { + result.push(new Separator(), ...allDocumentation.map(command => toCodeActionAction(new CodeActionItem({ + title: command.title, + command: command, + }, undefined)))); + } + return result; } @@ -218,3 +228,5 @@ export class CodeActionKeybindingResolver { }, undefined as ResolveCodeActionKeybinding | undefined); } } + + diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index e8645271f35..4afe90c451e 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -14,7 +14,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/common/modes'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress'; import { getCodeActions, CodeActionSet } from './codeAction'; import { CodeActionTrigger } from './types'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -213,9 +213,9 @@ export class CodeActionModel extends Disposable { return; } - const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token)); - if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) { - this._progressService.showWhile(actions, 250); + const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token)); + if (trigger.trigger.type === CodeActionTriggerType.Manual) { + this._progressService?.showWhile(actions, 250); } this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions)); diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 7d2aeff74f3..7a645a7bd0b 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { find } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; -import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; -import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; +import { CodeActionTriggerType } from 'vs/editor/common/modes'; +import { CodeActionItem, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; @@ -30,7 +29,7 @@ export class CodeActionUi extends Disposable { quickFixActionId: string, preferredFixActionId: string, private readonly delegate: { - applyCodeAction: (action: CodeAction, regtriggerAfterApply: boolean) => Promise + applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean) => Promise }, @IInstantiationService instantiationService: IInstantiationService, ) { @@ -84,8 +83,8 @@ export class CodeActionUi extends Disposable { // Check to see if there is an action that we would have applied were it not invalid if (newState.trigger.context) { const invalidAction = this.getInvalidActionThatWouldHaveBeenApplied(newState.trigger, actions); - if (invalidAction && invalidAction.disabled) { - MessageController.get(this._editor).showMessage(invalidAction.disabled, newState.trigger.context.position); + if (invalidAction && invalidAction.action.disabled) { + MessageController.get(this._editor).showMessage(invalidAction.action.disabled, newState.trigger.context.position); actions.dispose(); return; } @@ -115,7 +114,7 @@ export class CodeActionUi extends Disposable { } } - private getInvalidActionThatWouldHaveBeenApplied(trigger: CodeActionTrigger, actions: CodeActionSet): CodeAction | undefined { + private getInvalidActionThatWouldHaveBeenApplied(trigger: CodeActionTrigger, actions: CodeActionSet): CodeActionItem | undefined { if (!actions.allActions.length) { return undefined; } @@ -123,13 +122,13 @@ export class CodeActionUi extends Disposable { if ((trigger.autoApply === CodeActionAutoApply.First && actions.validActions.length === 0) || (trigger.autoApply === CodeActionAutoApply.IfSingle && actions.allActions.length === 1) ) { - return find(actions.allActions, action => action.disabled); + return actions.allActions.find(({ action }) => action.disabled); } return undefined; } - private tryGetValidActionToApply(trigger: CodeActionTrigger, actions: CodeActionSet): CodeAction | undefined { + private tryGetValidActionToApply(trigger: CodeActionTrigger, actions: CodeActionSet): CodeActionItem | undefined { if (!actions.validActions.length) { return undefined; } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.css b/src/vs/editor/contrib/codeAction/lightBulbWidget.css index aadcb4fd6e6..29c4fe0c043 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.css +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.css @@ -3,18 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .lightbulb-glyph, -.monaco-editor .codicon-lightbulb { +.monaco-editor .contentWidgets .codicon-light-bulb, +.monaco-editor .contentWidgets .codicon-lightbulb-autofix { display: flex; align-items: center; justify-content: center; - height: 16px; - width: 20px; - padding-left: 2px; } -.monaco-editor .lightbulb-glyph:hover, -.monaco-editor .codicon-lightbulb:hover { +.monaco-editor .contentWidgets .codicon-light-bulb:hover, +.monaco-editor .contentWidgets .codicon-lightbulb-autofix:hover { cursor: pointer; - /* transform: scale(1.3, 1.3); */ } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 04712201151..8eac58026d9 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -15,10 +15,11 @@ import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import * as nls from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { editorLightBulbForeground, editorLightBulbAutoFixForeground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { Gesture } from 'vs/base/browser/touch'; import type { CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; +import { Codicon } from 'vs/base/common/codicons'; namespace LightBulbState { @@ -63,7 +64,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { ) { super(); this._domNode = document.createElement('div'); - this._domNode.className = 'codicon codicon-lightbulb'; + this._domNode.className = Codicon.lightBulb.classNames; this._editor.addContentWidget(this); @@ -121,8 +122,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } })); - this._updateLightBulbTitle(); - this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)); + this._updateLightBulbTitleAndIcon(); + this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitleAndIcon, this)); } dispose(): void { @@ -152,12 +153,13 @@ export class LightBulbWidget extends Disposable implements IContentWidget { return this.hide(); } - const { lineNumber, column } = atPosition; const model = this._editor.getModel(); if (!model) { return this.hide(); } + const { lineNumber, column } = model.validatePosition(atPosition); + const tabSize = model.getOptions().tabSize; const fontInfo = options.get(EditorOption.fontInfo); const lineContent = model.getLineContent(lineNumber); @@ -184,7 +186,6 @@ export class LightBulbWidget extends Disposable implements IContentWidget { position: { lineNumber: effectiveLineNumber, column: 1 }, preference: LightBulbWidget._posPref }); - dom.toggleClass(this._domNode, 'codicon-lightbulb-autofix', actions.hasAutoFix); this._editor.layoutContentWidget(this); } @@ -197,11 +198,15 @@ export class LightBulbWidget extends Disposable implements IContentWidget { private set state(value) { this._state = value; - this._updateLightBulbTitle(); + this._updateLightBulbTitleAndIcon(); } - private _updateLightBulbTitle(): void { + private _updateLightBulbTitleAndIcon(): void { if (this.state.type === LightBulbState.Type.Showing && this.state.actions.hasAutoFix) { + // update icon + this._domNode.classList.remove(...Codicon.lightBulb.classNamesArray); + this._domNode.classList.add(...Codicon.lightbulbAutofix.classNamesArray); + const preferredKb = this._keybindingService.lookupKeybinding(this._preferredFixActionId); if (preferredKb) { this.title = nls.localize('prefferedQuickFixWithKb', "Show Fixes. Preferred Fix Available ({0})", preferredKb.getLabel()); @@ -209,6 +214,10 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } } + // update icon + this._domNode.classList.remove(...Codicon.lightbulbAutofix.classNamesArray); + this._domNode.classList.add(...Codicon.lightBulb.classNamesArray); + const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId); if (kb) { this.title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel()); @@ -222,14 +231,17 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } } -registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + + const editorBackgroundColor = theme.getColor(editorBackground)?.transparent(0.7); // Lightbulb Icon const editorLightBulbForegroundColor = theme.getColor(editorLightBulbForeground); if (editorLightBulbForegroundColor) { collector.addRule(` - .monaco-editor .contentWidgets .codicon-lightbulb { + .monaco-editor .contentWidgets ${Codicon.lightBulb.cssSelector} { color: ${editorLightBulbForegroundColor}; + background-color: ${editorBackgroundColor}; }`); } @@ -237,8 +249,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const editorLightBulbAutoFixForegroundColor = theme.getColor(editorLightBulbAutoFixForeground); if (editorLightBulbAutoFixForegroundColor) { collector.addRule(` - .monaco-editor .contentWidgets .codicon-lightbulb-autofix { + .monaco-editor .contentWidgets ${Codicon.lightbulbAutofix.cssSelector} { color: ${editorLightBulbAutoFixForegroundColor}; + background-color: ${editorBackgroundColor}; }`); } diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 0af6b11bb24..53a80a51108 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -8,10 +8,12 @@ import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; -import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; +import { CodeActionItem, getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Progress } from 'vs/platform/progress/common/progress'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; function staticCodeActionProvider(...actions: modes.CodeAction[]): modes.CodeActionProvider { return new class implements modes.CodeActionProvider { @@ -92,7 +94,7 @@ suite('CodeAction', () => { setup(function () { disposables.clear(); - model = TextModel.createFromString('test1\ntest2\ntest3', undefined, langId, uri); + model = createTextModel('test1\ntest2\ntest3', undefined, langId, uri); disposables.add(model); }); @@ -115,17 +117,17 @@ suite('CodeAction', () => { const expected = [ // CodeActions with a diagnostics array are shown first ordered by diagnostics.message - testData.diagnostics.abc, - testData.diagnostics.bcd, + new CodeActionItem(testData.diagnostics.abc, provider), + new CodeActionItem(testData.diagnostics.bcd, provider), // CodeActions without diagnostics are shown in the given order without any further sorting - testData.command.abc, - testData.spelling.bcd, // empty diagnostics array - testData.tsLint.bcd, - testData.tsLint.abc + new CodeActionItem(testData.command.abc, provider), + new CodeActionItem(testData.spelling.bcd, provider), // empty diagnostics array + new CodeActionItem(testData.tsLint.bcd, provider), + new CodeActionItem(testData.tsLint.abc, provider) ]; - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); @@ -140,20 +142,20 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 2); - assert.strictEqual(actions[0].title, 'a'); - assert.strictEqual(actions[1].title, 'a.b'); + assert.strictEqual(actions[0].action.title, 'a'); + assert.strictEqual(actions[1].action.title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); - assert.strictEqual(actions[0].title, 'a.b'); + assert.strictEqual(actions[0].action.title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 0); } }); @@ -172,9 +174,9 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); - assert.strictEqual(actions[0].title, 'a'); + assert.strictEqual(actions[0].action.title, 'a'); }); test('getCodeActions should not return source code action by default', async function () { @@ -186,15 +188,15 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); - assert.strictEqual(actions[0].title, 'b'); + assert.strictEqual(actions[0].action.title, 'b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); - assert.strictEqual(actions[0].title, 'a'); + assert.strictEqual(actions[0].action.title, 'a'); } }); @@ -214,9 +216,9 @@ suite('CodeAction', () => { excludes: [CodeActionKind.Source], includeSourceActions: true, } - }, CancellationToken.None); + }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); - assert.strictEqual(actions[0].title, 'b'); + assert.strictEqual(actions[0].action.title, 'b'); } }); @@ -250,10 +252,10 @@ suite('CodeAction', () => { include: baseType, excludes: [subType], } - }, CancellationToken.None); + }, Progress.None, CancellationToken.None); assert.strictEqual(didInvoke, false); assert.equal(actions.length, 1); - assert.strictEqual(actions[0].title, 'a'); + assert.strictEqual(actions[0].action.title, 'a'); } }); @@ -275,9 +277,8 @@ suite('CodeAction', () => { filter: { include: CodeActionKind.QuickFix } - }, CancellationToken.None); + }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 0); assert.strictEqual(wasInvoked, false); }); }); - diff --git a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts index 9a939303ece..a0092488f75 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts @@ -89,6 +89,7 @@ function createCodeActionKeybinding(keycode: KeyCode, command: string, commandAr command, commandArgs, undefined, - false); + false, + null); } diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index c605ed49ab6..5edcc7ae851 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -15,6 +15,7 @@ import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/ import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; const testProvider = { provideCodeActions(): modes.CodeActionList { @@ -38,7 +39,7 @@ suite('CodeActionModel', () => { setup(() => { disposables.clear(); markerService = new MarkerService(); - model = TextModel.createFromString('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri); + model = createTextModel('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri); editor = createTestCodeEditor({ model: model }); editor.setPosition({ lineNumber: 1, column: 1 }); }); diff --git a/src/vs/editor/contrib/codeAction/types.ts b/src/vs/editor/contrib/codeAction/types.ts index b8f8c5e741e..8f558329017 100644 --- a/src/vs/editor/contrib/codeAction/types.ts +++ b/src/vs/editor/contrib/codeAction/types.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { startsWith } from 'vs/base/common/strings'; import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; @@ -27,7 +26,7 @@ export class CodeActionKind { } public contains(other: CodeActionKind): boolean { - return this.equals(other) || this.value === '' || startsWith(other.value, this.value + CodeActionKind.sep); + return this.equals(other) || this.value === '' || other.value.startsWith(this.value + CodeActionKind.sep); } public intersects(other: CodeActionKind): boolean { diff --git a/src/vs/editor/contrib/codelens/codeLensCache.ts b/src/vs/editor/contrib/codelens/codeLensCache.ts index b83619ffef5..5f8c147657d 100644 --- a/src/vs/editor/contrib/codelens/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/codeLensCache.ts @@ -7,7 +7,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { CodeLensModel } from 'vs/editor/contrib/codelens/codelens'; -import { LRUCache, values } from 'vs/base/common/map'; +import { LRUCache } from 'vs/base/common/map'; import { CodeLensProvider, CodeLensList, CodeLens } from 'vs/editor/common/modes'; import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { Range } from 'vs/editor/common/core/range'; @@ -17,7 +17,7 @@ import { once } from 'vs/base/common/functional'; export const ICodeLensCache = createDecorator('ICodeLensCache'); export interface ICodeLensCache { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; put(model: ITextModel, data: CodeLensModel): void; get(model: ITextModel): CodeLensModel | undefined; delete(model: ITextModel): void; @@ -38,7 +38,7 @@ class CacheItem { export class CodeLensCache implements ICodeLensCache { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _fakeProvider = new class implements CodeLensProvider { provideCodeLenses(): CodeLensList { @@ -96,16 +96,16 @@ export class CodeLensCache implements ICodeLensCache { private _serialize(): string { const data: Record = Object.create(null); - this._cache.forEach((value, key) => { + for (const [key, value] of this._cache) { const lines = new Set(); for (const d of value.data.lenses) { lines.add(d.symbol.range.startLineNumber); } data[key] = { lineCount: value.lineCount, - lines: values(lines) + lines: [...lines.values()] }; - }); + } return JSON.stringify(data); } diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index 76c9cd84fd7..fbcfb19913b 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -36,44 +36,47 @@ export class CodeLensModel { } } -export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise { +export async function getCodeLensModel(model: ITextModel, token: CancellationToken): Promise { const provider = CodeLensProviderRegistry.ordered(model); const providerRanks = new Map(); const result = new CodeLensModel(); - const promises = provider.map((provider, i) => { + const promises = provider.map(async (provider, i) => { providerRanks.set(provider, i); - return Promise.resolve(provider.provideCodeLenses(model, token)) - .then(list => list && result.add(list, provider)) - .catch(onUnexpectedExternalError); - }); - - return Promise.all(promises).then(() => { - - result.lenses = mergeSort(result.lenses, (a, b) => { - // sort by lineNumber, provider-rank, and column - if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { - return -1; - } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { - return 1; - } else if (providerRanks.get(a.provider)! < providerRanks.get(b.provider)!) { - return -1; - } else if (providerRanks.get(a.provider)! > providerRanks.get(b.provider)!) { - return 1; - } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { - return -1; - } else if (a.symbol.range.startColumn > b.symbol.range.startColumn) { - return 1; - } else { - return 0; + try { + const list = await Promise.resolve(provider.provideCodeLenses(model, token)); + if (list) { + result.add(list, provider); } - }); - - return result; + } catch (err) { + onUnexpectedExternalError(err); + } }); + + await Promise.all(promises); + + result.lenses = mergeSort(result.lenses, (a, b) => { + // sort by lineNumber, provider-rank, and column + if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { + return -1; + } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { + return 1; + } else if ((providerRanks.get(a.provider)!) < (providerRanks.get(b.provider)!)) { + return -1; + } else if ((providerRanks.get(a.provider)!) > (providerRanks.get(b.provider)!)) { + return 1; + } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { + return -1; + } else if (a.symbol.range.startColumn > b.symbol.range.startColumn) { + return 1; + } else { + return 0; + } + }); + return result; } registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { @@ -90,7 +93,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { const result: CodeLens[] = []; const disposables = new DisposableStore(); - return getCodeLensData(model, CancellationToken.None).then(value => { + return getCodeLensModel(model, CancellationToken.None).then(value => { disposables.add(value); let resolve: Promise[] = []; diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 2b4865134c3..360889a81e2 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -5,14 +5,14 @@ import { CancelablePromise, RunOnceScheduler, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; -import { toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import { ICodeEditor, MouseTargetType, IViewZoneChangeAccessor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { registerEditorContribution, ServicesAccessor, registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; -import { CodeLensProviderRegistry, CodeLens } from 'vs/editor/common/modes'; -import { CodeLensModel, getCodeLensData, CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; +import { CodeLensProviderRegistry, CodeLens, Command } from 'vs/editor/common/modes'; +import { CodeLensModel, getCodeLensModel, CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; import { CodeLensWidget, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -20,24 +20,28 @@ import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import * as dom from 'vs/base/browser/dom'; import { hash } from 'vs/base/common/hash'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { localize } from 'vs/nls'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; export class CodeLensContribution implements IEditorContribution { - public static readonly ID: string = 'css.editor.codeLens'; + static readonly ID: string = 'css.editor.codeLens'; - private _isEnabled: boolean; - - private readonly _globalToDispose = new DisposableStore(); + private readonly _disposables = new DisposableStore(); private readonly _localToDispose = new DisposableStore(); private readonly _styleElement: HTMLStyleElement; private readonly _styleClassName: string; - private _lenses: CodeLensWidget[] = []; - private _currentFindCodeLensSymbolsPromise: CancelablePromise | undefined; + private readonly _lenses: CodeLensWidget[] = []; + + private readonly _getCodeLensModelDelays = new LanguageFeatureRequestDelays(CodeLensProviderRegistry, 250, 2500); + private _getCodeLensModelPromise: CancelablePromise | undefined; private _oldCodeLensModels = new DisposableStore(); private _currentCodeLensModel: CodeLensModel | undefined; - private _modelChangeCounter: number = 0; - private _currentResolveCodeLensSymbolsPromise: CancelablePromise | undefined; - private _detectVisibleLenses: RunOnceScheduler | undefined; + private readonly _resolveCodeLensesDelays = new LanguageFeatureRequestDelays(CodeLensProviderRegistry, 250, 2500); + private readonly _resolveCodeLensesScheduler = new RunOnceScheduler(() => this._resolveCodeLensesInViewport(), this._resolveCodeLensesDelays.min); + private _resolveCodeLensesPromise: CancelablePromise | undefined; constructor( private readonly _editor: ICodeEditor, @@ -45,26 +49,21 @@ export class CodeLensContribution implements IEditorContribution { @INotificationService private readonly _notificationService: INotificationService, @ICodeLensCache private readonly _codeLensCache: ICodeLensCache ) { - this._isEnabled = this._editor.getOption(EditorOption.codeLens); - this._globalToDispose.add(this._editor.onDidChangeModel(() => this._onModelChange())); - this._globalToDispose.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); - this._globalToDispose.add(this._editor.onDidChangeConfiguration(() => { - const prevIsEnabled = this._isEnabled; - this._isEnabled = this._editor.getOption(EditorOption.codeLens); - if (prevIsEnabled !== this._isEnabled) { - this._onModelChange(); - } - })); - this._globalToDispose.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); - this._globalToDispose.add(this._editor.onDidChangeConfiguration(e => { + this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); + this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); + this._disposables.add(this._editor.onDidChangeConfiguration((e) => { if (e.hasChanged(EditorOption.fontInfo)) { this._updateLensStyle(); } + if (e.hasChanged(EditorOption.codeLens)) { + this._onModelChange(); + } })); + this._disposables.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); this._onModelChange(); - this._styleClassName = hash(this._editor.getId()).toString(16); + this._styleClassName = '_' + hash(this._editor.getId()).toString(16); this._styleElement = dom.createStyleSheet( dom.isInShadowDOM(this._editor.getContainerDomNode()) ? this._editor.getContainerDomNode() @@ -75,9 +74,9 @@ export class CodeLensContribution implements IEditorContribution { dispose(): void { this._localDispose(); - this._globalToDispose.dispose(); + this._disposables.dispose(); this._oldCodeLensModels.dispose(); - dispose(this._currentCodeLensModel); + this._currentCodeLensModel?.dispose(); } private _updateLensStyle(): void { @@ -92,22 +91,17 @@ export class CodeLensContribution implements IEditorContribution { .monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;} .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; } `; - this._styleElement.innerHTML = newStyle; + this._styleElement.textContent = newStyle; } private _localDispose(): void { - if (this._currentFindCodeLensSymbolsPromise) { - this._currentFindCodeLensSymbolsPromise.cancel(); - this._currentFindCodeLensSymbolsPromise = undefined; - this._modelChangeCounter++; - } - if (this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise.cancel(); - this._currentResolveCodeLensSymbolsPromise = undefined; - } + this._getCodeLensModelPromise?.cancel(); + this._getCodeLensModelPromise = undefined; + this._resolveCodeLensesPromise?.cancel(); + this._resolveCodeLensesPromise = undefined; this._localToDispose.clear(); this._oldCodeLensModels.clear(); - dispose(this._currentCodeLensModel); + this._currentCodeLensModel?.dispose(); } private _onModelChange(): void { @@ -119,7 +113,7 @@ export class CodeLensContribution implements IEditorContribution { return; } - if (!this._isEnabled) { + if (!this._editor.getOption(EditorOption.codeLens)) { return; } @@ -150,34 +144,34 @@ export class CodeLensContribution implements IEditorContribution { } } - const detectVisibleLenses = this._detectVisibleLenses = new RunOnceScheduler(() => this._onViewportChanged(), 250); - const scheduler = new RunOnceScheduler(() => { - const counterValue = ++this._modelChangeCounter; - if (this._currentFindCodeLensSymbolsPromise) { - this._currentFindCodeLensSymbolsPromise.cancel(); - } + const t1 = Date.now(); - this._currentFindCodeLensSymbolsPromise = createCancelablePromise(token => getCodeLensData(model, token)); + this._getCodeLensModelPromise?.cancel(); + this._getCodeLensModelPromise = createCancelablePromise(token => getCodeLensModel(model, token)); - this._currentFindCodeLensSymbolsPromise.then(result => { - if (counterValue === this._modelChangeCounter) { // only the last one wins - if (this._currentCodeLensModel) { - this._oldCodeLensModels.add(this._currentCodeLensModel); - } - this._currentCodeLensModel = result; - - // cache model to reduce flicker - this._codeLensCache.put(model, result); - - // render lenses - this._renderCodeLensSymbols(result); - detectVisibleLenses.schedule(); + this._getCodeLensModelPromise.then(result => { + if (this._currentCodeLensModel) { + this._oldCodeLensModels.add(this._currentCodeLensModel); } + this._currentCodeLensModel = result; + + // cache model to reduce flicker + this._codeLensCache.put(model, result); + + // update moving average + const newDelay = this._getCodeLensModelDelays.update(model, Date.now() - t1); + scheduler.delay = newDelay; + + // render lenses + this._renderCodeLensSymbols(result); + this._resolveCodeLensesInViewportSoon(); }, onUnexpectedError); - }, 250); + + }, this._getCodeLensModelDelays.get(model)); + this._localToDispose.add(scheduler); - this._localToDispose.add(detectVisibleLenses); + this._localToDispose.add(toDisposable(() => this._resolveCodeLensesScheduler.cancel())); this._localToDispose.add(this._editor.onDidChangeModelContent(() => { this._editor.changeDecorations(decorationsAccessor => { this._editor.changeViewZones(viewZonesAccessor => { @@ -206,17 +200,17 @@ export class CodeLensContribution implements IEditorContribution { }); // Compute new `visible` code lenses - detectVisibleLenses.schedule(); + this._resolveCodeLensesInViewportSoon(); // Ask for all references again scheduler.schedule(); })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { - detectVisibleLenses.schedule(); + this._resolveCodeLensesInViewportSoon(); } })); this._localToDispose.add(this._editor.onDidLayoutChange(() => { - detectVisibleLenses.schedule(); + this._resolveCodeLensesInViewportSoon(); })); this._localToDispose.add(toDisposable(() => { if (this._editor.getModel()) { @@ -232,7 +226,7 @@ export class CodeLensContribution implements IEditorContribution { this._disposeAllLenses(undefined, undefined); } })); - this._localToDispose.add(this._editor.onMouseUp(e => { + this._localToDispose.add(this._editor.onMouseDown(e => { if (e.target.type !== MouseTargetType.CONTENT_WIDGET) { return; } @@ -261,7 +255,7 @@ export class CodeLensContribution implements IEditorContribution { if (decChangeAccessor) { helper.commit(decChangeAccessor); } - this._lenses = []; + this._lenses.length = 0; } private _renderCodeLensSymbols(symbols: CodeLensModel): void { @@ -310,7 +304,7 @@ export class CodeLensContribution implements IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); codeLensIndex++; groupsIndex++; } @@ -324,7 +318,7 @@ export class CodeLensContribution implements IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); groupsIndex++; } @@ -335,11 +329,17 @@ export class CodeLensContribution implements IEditorContribution { scrollState.restore(this._editor); } - private _onViewportChanged(): void { - if (this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise.cancel(); - this._currentResolveCodeLensSymbolsPromise = undefined; + private _resolveCodeLensesInViewportSoon(): void { + const model = this._editor.getModel(); + if (model) { + this._resolveCodeLensesScheduler.schedule(); } + } + + private _resolveCodeLensesInViewport(): void { + + this._resolveCodeLensesPromise?.cancel(); + this._resolveCodeLensesPromise = undefined; const model = this._editor.getModel(); if (!model) { @@ -360,6 +360,8 @@ export class CodeLensContribution implements IEditorContribution { return; } + const t1 = Date.now(); + const resolvePromise = createCancelablePromise(token => { const promises = toResolve.map((request, i) => { @@ -385,23 +387,90 @@ export class CodeLensContribution implements IEditorContribution { return Promise.all(promises); }); - this._currentResolveCodeLensSymbolsPromise = resolvePromise; + this._resolveCodeLensesPromise = resolvePromise; + + this._resolveCodeLensesPromise.then(() => { + + // update moving average + const newDelay = this._resolveCodeLensesDelays.update(model, Date.now() - t1); + this._resolveCodeLensesScheduler.delay = newDelay; - this._currentResolveCodeLensSymbolsPromise.then(() => { if (this._currentCodeLensModel) { // update the cached state with new resolved items this._codeLensCache.put(model, this._currentCodeLensModel); } this._oldCodeLensModels.clear(); // dispose old models once we have updated the UI with the current model - if (resolvePromise === this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise = undefined; + if (resolvePromise === this._resolveCodeLensesPromise) { + this._resolveCodeLensesPromise = undefined; } }, err => { onUnexpectedError(err); // can also be cancellation! - if (resolvePromise === this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise = undefined; + if (resolvePromise === this._resolveCodeLensesPromise) { + this._resolveCodeLensesPromise = undefined; } }); } + + getLenses(): readonly CodeLensWidget[] { + return this._lenses; + } } registerEditorContribution(CodeLensContribution.ID, CodeLensContribution); + +registerEditorAction(class ShowLensesInCurrentLine extends EditorAction { + + constructor() { + super({ + id: 'codelens.showLensesInCurrentLine', + precondition: EditorContextKeys.hasCodeLensProvider, + label: localize('showLensOnLine', "Show CodeLens Commands For Current Line"), + alias: 'Show CodeLens Commands For Current Line', + }); + } + + async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + + if (!editor.hasModel()) { + return; + } + + const quickInputService = accessor.get(IQuickInputService); + const commandService = accessor.get(ICommandService); + const notificationService = accessor.get(INotificationService); + + const lineNumber = editor.getSelection().positionLineNumber; + const codelensController = editor.getContribution(CodeLensContribution.ID); + const items: { label: string, command: Command }[] = []; + + for (let lens of codelensController.getLenses()) { + if (lens.getLineNumber() === lineNumber) { + for (let item of lens.getItems()) { + const { command } = item.symbol; + if (command) { + items.push({ + label: command.title, + command: command + }); + } + } + } + } + + if (items.length === 0) { + // We dont want an empty picker + return; + } + + const item = await quickInputService.pick(items, { canPickMany: false }); + if (!item) { + // Nothing picked + return; + } + + try { + await commandService.executeCommand(item.command.id, ...(item.command.arguments || [])); + } catch (err) { + notificationService.error(err); + } + } +}); diff --git a/src/vs/editor/contrib/codelens/codelensWidget.css b/src/vs/editor/contrib/codelens/codelensWidget.css index 7bd66d08fd8..03315707679 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.css +++ b/src/vs/editor/contrib/codelens/codelensWidget.css @@ -7,6 +7,7 @@ overflow: hidden; display: inline-block; text-overflow: ellipsis; + white-space: nowrap; } .monaco-editor .codelens-decoration > span, diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 622e7785aaf..215e48f22de 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -5,8 +5,6 @@ import 'vs/css!./codelensWidget'; import * as dom from 'vs/base/browser/dom'; -import { renderCodicons } from 'vs/base/common/codicons'; -import { escape } from 'vs/base/common/strings'; import { IViewZone, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; @@ -16,6 +14,7 @@ import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegis import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { renderCodicons } from 'vs/base/browser/codicons'; class CodeLensViewZone implements IViewZone { @@ -80,7 +79,7 @@ class CodeLensContentWidget implements IContentWidget { withCommands(lenses: Array, animate: boolean): void { this._commands.clear(); - let innerHtml = ''; + let children: HTMLElement[] = []; let hasSymbol = false; for (let i = 0; i < lenses.length; i++) { const lens = lenses[i]; @@ -89,31 +88,28 @@ class CodeLensContentWidget implements IContentWidget { } hasSymbol = true; if (lens.command) { - const title = renderCodicons(escape(lens.command.title)); + const title = renderCodicons(lens.command.title.trim()); if (lens.command.id) { - innerHtml += `${title}`; + children.push(dom.$('a', { id: String(i) }, ...title)); this._commands.set(String(i), lens.command); } else { - innerHtml += `${title}`; + children.push(dom.$('span', undefined, ...title)); } if (i + 1 < lenses.length) { - innerHtml += ' | '; + children.push(dom.$('span', undefined, '\u00a0|\u00a0')); } } } if (!hasSymbol) { // symbols but no commands - this._domNode.innerHTML = 'no commands'; + dom.reset(this._domNode, dom.$('span', undefined, 'no commands')); } else { // symbols and commands - if (!innerHtml) { - innerHtml = ' '; - } - this._domNode.innerHTML = innerHtml; + dom.reset(this._domNode, ...children); if (this._isEmpty && animate) { - dom.addClass(this._domNode, 'fadein'); + this._domNode.classList.add('fadein'); } this._isEmpty = false; } @@ -336,6 +332,10 @@ export class CodeLensWidget { } } } + + getItems(): CodeLensItem[] { + return this._data; + } } registerThemingParticipant((theme, collector) => { diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 90f0db8ac67..4b678d0edda 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -173,7 +173,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { for (let i = 0; i < colorData.length && decorations.length < MAX_DECORATORS; i++) { const { red, green, blue, alpha } = colorData[i].colorInfo.color; const rgba = new RGBA(Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255), alpha); - let subKey = hash(rgba).toString(16); + let subKey = hash(`rgba(${rgba.r},${rgba.g},${rgba.b},${rgba.a})`).toString(16); let color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`; let key = 'colorBox-' + subKey; diff --git a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts index 9b974876732..772f252613f 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts @@ -64,7 +64,7 @@ export class ColorPickerModel { guessColorPresentation(color: Color, originalText: string): void { for (let i = 0; i < this.colorPresentations.length; i++) { - if (originalText === this.colorPresentations[i].label) { + if (originalText.toLowerCase() === this.colorPresentations[i].label) { this.presentationIndex = i; this._onDidChangePresentation.fire(this.presentation); break; diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 68de080e7d6..4bcdaa3974d 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -34,7 +34,7 @@ export class ColorPickerHeader extends Disposable { const colorBox = dom.append(this.domNode, $('.original-color')); colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || ''; - this.backgroundColor = themeService.getTheme().getColor(editorHoverBackground) || Color.white; + this.backgroundColor = themeService.getColorTheme().getColor(editorHoverBackground) || Color.white; this._register(registerThemingParticipant((theme, collector) => { this.backgroundColor = theme.getColor(editorHoverBackground) || Color.white; })); @@ -47,12 +47,12 @@ export class ColorPickerHeader extends Disposable { this._register(model.onDidChangeColor(this.onDidChangeColor, this)); this._register(model.onDidChangePresentation(this.onDidChangePresentation, this)); this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(model.color) || ''; - dom.toggleClass(this.pickedColorNode, 'light', model.color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : model.color.isLighter()); + this.pickedColorNode.classList.toggle('light', model.color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : model.color.isLighter()); } private onDidChangeColor(color: Color): void { this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color) || ''; - dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter()); + this.pickedColorNode.classList.toggle('light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter()); this.onDidChangePresentation(); } @@ -264,7 +264,7 @@ abstract class Strip extends Disposable { private onMouseDown(e: MouseEvent): void { const monitor = this._register(new GlobalMouseMoveMonitor()); const origin = dom.getDomNodePagePosition(this.domNode); - dom.addClass(this.domNode, 'grabbing'); + this.domNode.classList.add('grabbing'); if (e.target !== this.slider) { this.onDidChangeTop(e.offsetY); @@ -276,7 +276,7 @@ abstract class Strip extends Disposable { this._onColorFlushed.fire(); mouseUpListener.dispose(); monitor.stopMonitoring(true); - dom.removeClass(this.domNode, 'grabbing'); + this.domNode.classList.remove('grabbing'); }, true); } @@ -298,7 +298,7 @@ class OpacityStrip extends Strip { constructor(container: HTMLElement, model: ColorPickerModel) { super(container, model); - dom.addClass(this.domNode, 'opacity-strip'); + this.domNode.classList.add('opacity-strip'); this._register(model.onDidChangeColor(this.onDidChangeColor, this)); this.onDidChangeColor(this.model.color); @@ -321,7 +321,7 @@ class HueStrip extends Strip { constructor(container: HTMLElement, model: ColorPickerModel) { super(container, model); - dom.addClass(this.domNode, 'hue-strip'); + this.domNode.classList.add('hue-strip'); } protected getValue(color: Color): number { diff --git a/src/vs/editor/contrib/comment/blockCommentCommand.ts b/src/vs/editor/contrib/comment/blockCommentCommand.ts index efa593a811d..0a6d6a59ea5 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -9,7 +9,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; export class BlockCommentCommand implements ICommand { diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 243a45655bf..1dbe337e1ce 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -36,7 +36,13 @@ abstract class CommentLineAction extends EditorAction { const commentsOptions = editor.getOption(EditorOption.comments); for (const selection of selections) { - commands.push(new LineCommentCommand(selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace)); + commands.push(new LineCommentCommand( + selection, + modelOptions.tabSize, + this._type, + commentsOptions.insertSpace, + commentsOptions.ignoreEmptyLines + )); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index f266b70380a..8952727a9d9 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -53,11 +53,18 @@ export class LineCommentCommand implements ICommand { private readonly _tabSize: number; private readonly _type: Type; private readonly _insertSpace: boolean; + private readonly _ignoreEmptyLines: boolean; private _selectionId: string | null; private _deltaColumn: number; private _moveEndPositionDown: boolean; - constructor(selection: Selection, tabSize: number, type: Type, insertSpace: boolean) { + constructor( + selection: Selection, + tabSize: number, + type: Type, + insertSpace: boolean, + ignoreEmptyLines: boolean + ) { this._selection = selection; this._tabSize = tabSize; this._type = type; @@ -65,6 +72,7 @@ export class LineCommentCommand implements ICommand { this._selectionId = null; this._deltaColumn = 0; this._moveEndPositionDown = false; + this._ignoreEmptyLines = ignoreEmptyLines; } /** @@ -100,7 +108,7 @@ export class LineCommentCommand implements ICommand { * Analyze lines and decide which lines are relevant and what the toggle should do. * Also, build up several offsets and lengths useful in the generation of editor operations. */ - public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData { + public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { let onlyWhitespaceLines = true; let shouldRemoveComments: boolean; @@ -121,13 +129,7 @@ export class LineCommentCommand implements ICommand { if (lineContentStartOffset === -1) { // Empty or whitespace only line - if (type === Type.Toggle) { - lineData.ignore = true; - } else if (type === Type.ForceAdd) { - lineData.ignore = true; - } else { - lineData.ignore = true; - } + lineData.ignore = ignoreEmptyLines; lineData.commentStrOffset = lineContent.length; continue; } @@ -176,7 +178,7 @@ export class LineCommentCommand implements ICommand { /** * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments */ - public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData { + public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber); if (lines === null) { return { @@ -184,7 +186,7 @@ export class LineCommentCommand implements ICommand { }; } - return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines); } /** @@ -205,7 +207,7 @@ export class LineCommentCommand implements ICommand { for (let i = 0, len = ops.length; i < len; i++) { builder.addEditOperation(ops[i].range, ops[i].text); - if (ops[i].range.isEmpty() && ops[i].range.getStartPosition().equals(cursorPosition)) { + if (Range.isEmpty(ops[i].range) && Range.getStartPosition(ops[i].range).equals(cursorPosition)) { const lineContent = model.getLineContent(cursorPosition.lineNumber); if (lineContent.length + 1 === cursorPosition.column) { this._deltaColumn = (ops[i].text || '').length; @@ -326,7 +328,15 @@ export class LineCommentCommand implements ICommand { s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); } - const data = LineCommentCommand._gatherPreflightData(this._type, this._insertSpace, model, s.startLineNumber, s.endLineNumber); + const data = LineCommentCommand._gatherPreflightData( + this._type, + this._insertSpace, + model, + s.startLineNumber, + s.endLineNumber, + this._ignoreEmptyLines + ); + if (data.supported) { return this._executeLineComments(model, builder, data, s); } diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 66593786b96..0689b4209d5 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -19,13 +19,13 @@ suite('Editor Contrib - Line Comment Command', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -47,7 +47,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('case insensitive', function () { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: 'rem' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -91,7 +91,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' ', ' c', '\t\td' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); if (!r.supported) { throw new Error(`unexpected`); } @@ -122,7 +122,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' rem ', ' !@# c', '\t\t!@#d' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); if (!r.supported) { throw new Error(`unexpected`); } @@ -631,7 +631,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('insertSpace false', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false, true), expectedLines, expectedSelection); mode.dispose(); } @@ -650,7 +650,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('insertSpace false does not remove space', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false, true), expectedLines, expectedSelection); mode.dispose(); } @@ -665,13 +665,104 @@ suite('Editor Contrib - Line Comment Command', () => { new Selection(1, 1, 1, 1) ); }); + + suite('ignoreEmptyLines false', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, false), expectedLines, expectedSelection); + mode.dispose(); + } + + test('does not ignore whitespace lines', () => { + testLineCommentCommand( + [ + '\tsome text', + '\t ', + '', + '\tsome more text' + ], + new Selection(4, 2, 1, 1), + [ + '!@# \tsome text', + '!@# \t ', + '!@# ', + '!@# \tsome more text' + ], + new Selection(4, 6, 1, 5) + ); + }); + + test('removes its own', function () { + testLineCommentCommand( + [ + '\t!@# some text', + '\t ', + '\t\t!@# some more text' + ], + new Selection(3, 2, 1, 1), + [ + '\tsome text', + '\t ', + '\t\tsome more text' + ], + new Selection(3, 2, 1, 1) + ); + }); + + test('works in only whitespace', function () { + testLineCommentCommand( + [ + '\t ', + '\t', + '\t\tsome more text' + ], + new Selection(3, 1, 1, 1), + [ + '\t!@# ', + '\t!@# ', + '\t\tsome more text' + ], + new Selection(3, 1, 1, 1) + ); + }); + + test('comments single line', function () { + testLineCommentCommand( + [ + 'some text', + '\tsome more text' + ], + new Selection(1, 1, 1, 1), + [ + '!@# some text', + '\tsome more text' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('detects indentation', function () { + testLineCommentCommand( + [ + '\tsome text', + '\tsome more text' + ], + new Selection(2, 2, 1, 1), + [ + '\t!@# some text', + '\t!@# some more text' + ], + new Selection(2, 2, 1, 1) + ); + }); + }); }); suite('Editor Contrib - Line Comment As Block Comment', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -782,7 +873,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => { suite('Editor Contrib - Line Comment As Block Comment 2', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: null, blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -1023,7 +1114,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { lines, outerMode.getLanguageIdentifier(), selection, - (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), + (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection, true diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 6114c170cce..8a2a53b8dde 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -6,9 +6,8 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -23,7 +22,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ITextModel } from 'vs/editor/common/model'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { ContextSubMenu } from 'vs/base/browser/contextmenu'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class ContextMenuController implements IEditorContribution { @@ -50,7 +49,14 @@ export class ContextMenuController implements IEditorContribution { this._toDispose.add(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e))); this._toDispose.add(this._editor.onMouseWheel((e: IMouseWheelEvent) => { if (this._contextMenuIsBeingShownCount > 0) { - this._contextViewService.hideContextView(); + const view = this._contextViewService.getContextViewElement(); + const target = e.srcElement as HTMLElement; + + // Event triggers on shadow root host first + // Check if the context view is under this host before hiding it #103169 + if (!(target.shadowRoot && dom.getShadowRoot(view) === target.shadowRoot)) { + this._contextViewService.hideContextView(); + } } })); this._toDispose.add(this._editor.onKeyDown((e: IKeyboardEvent) => { @@ -153,7 +159,7 @@ export class ContextMenuController implements IEditorContribution { if (action instanceof SubmenuItemAction) { const subActions = this._getMenuActions(model, action.item.submenu); if (subActions.length > 0) { - result.push(new ContextSubMenu(action.label, subActions)); + result.push(new SubmenuAction(action.id, action.label, subActions)); addedItems++; } } else { @@ -174,7 +180,7 @@ export class ContextMenuController implements IEditorContribution { return result; } - private _doShowContextMenu(actions: ReadonlyArray, anchor: IAnchor | null = null): void { + private _doShowContextMenu(actions: IAction[], anchor: IAnchor | null = null): void { if (!this._editor.hasModel()) { return; } @@ -205,6 +211,8 @@ export class ContextMenuController implements IEditorContribution { // Show menu this._contextMenuIsBeingShownCount++; this._contextMenuService.showContextMenu({ + domForShadowRoot: this._editor.getDomNode(), + getAnchor: () => anchor!, getActions: () => actions, diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index ea3cda654bc..b4bdd4336fd 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -54,6 +54,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); + this._register(this._editor.onDidBlurEditorText(() => this.onEditorBlur())); this._dndDecorationIds = []; this._mouseDown = false; this._modifierPressed = false; @@ -68,7 +69,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu } private onEditorKeyDown(e: IKeyboardEvent): void { - if (!this._editor.getOption(EditorOption.dragAndDrop)) { + if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) { return; } @@ -84,7 +85,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu } private onEditorKeyUp(e: IKeyboardEvent): void { - if (!this._editor.getOption(EditorOption.dragAndDrop)) { + if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) { return; } diff --git a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css index e7953bf8ec6..ccdfe57a4f4 100644 --- a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css +++ b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css @@ -20,8 +20,9 @@ color: var(--outline-element-color); } -.monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail { - visibility: inherit; +.monaco-list .outline-element .monaco-icon-label-container .monaco-highlighted-label, +.monaco-list .outline-element .monaco-icon-label-container .label-description { + white-space: nowrap; } .monaco-list .outline-element .outline-element-decoration { diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 8059fdbdcfd..c85081502dd 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -5,7 +5,6 @@ import { binarySearch, coalesceInPlace, equals } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { first, forEach, size } from 'vs/base/common/collections'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { LRUCache } from 'vs/base/common/map'; import { commonPrefixLength } from 'vs/base/common/strings'; @@ -14,18 +13,21 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { Iterable } from 'vs/base/common/iterator'; +import { URI } from 'vs/base/common/uri'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; export abstract class TreeElement { abstract id: string; - abstract children: { [id: string]: TreeElement }; + abstract children: Map; abstract parent: TreeElement | undefined; abstract adopt(newParent: TreeElement): TreeElement; remove(): void { if (this.parent) { - delete this.parent.children[this.id]; + this.parent.children.delete(this.id); } } @@ -37,13 +39,13 @@ export abstract class TreeElement { candidateId = `${container.id}/${candidate}`; } else { candidateId = `${container.id}/${candidate.name}`; - if (container.children[candidateId] !== undefined) { + if (container.children.get(candidateId) !== undefined) { candidateId = `${container.id}/${candidate.name}_${candidate.range.startLineNumber}_${candidate.range.startColumn}`; } } let id = candidateId; - for (let i = 0; container.children[id] !== undefined; i++) { + for (let i = 0; container.children.get(id) !== undefined; i++) { id = `${candidateId}_${i}`; } @@ -61,8 +63,8 @@ export abstract class TreeElement { if (len < element.id.length) { return undefined; } - for (const key in element.children) { - let candidate = TreeElement.getElementById(id, element.children[key]); + for (const [, child] of element.children) { + let candidate = TreeElement.getElementById(id, child); if (candidate) { return candidate; } @@ -72,17 +74,14 @@ export abstract class TreeElement { static size(element: TreeElement): number { let res = 1; - for (const key in element.children) { - res += TreeElement.size(element.children[key]); + for (const [, child] of element.children) { + res += TreeElement.size(child); } return res; } static empty(element: TreeElement): boolean { - for (const _key in element.children) { - return false; - } - return true; + return element.children.size === 0; } } @@ -96,7 +95,7 @@ export interface IOutlineMarker { export class OutlineElement extends TreeElement { - children: { [id: string]: OutlineElement; } = Object.create(null); + children = new Map(); marker: { count: number, topSev: MarkerSeverity } | undefined; constructor( @@ -109,27 +108,31 @@ export class OutlineElement extends TreeElement { adopt(parent: TreeElement): OutlineElement { let res = new OutlineElement(this.id, parent, this.symbol); - forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res)); + for (const [key, value] of this.children) { + res.children.set(key, value.adopt(res)); + } return res; } } export class OutlineGroup extends TreeElement { - children: { [id: string]: OutlineElement; } = Object.create(null); + children = new Map(); constructor( readonly id: string, public parent: TreeElement | undefined, - readonly provider: DocumentSymbolProvider, - readonly providerIndex: number, + readonly label: string, + readonly order: number, ) { super(); } adopt(parent: TreeElement): OutlineGroup { - let res = new OutlineGroup(this.id, parent, this.provider, this.providerIndex); - forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res)); + let res = new OutlineGroup(this.id, parent, this.label, this.order); + for (const [key, value] of this.children) { + res.children.set(key, value.adopt(res)); + } return res; } @@ -137,9 +140,8 @@ export class OutlineGroup extends TreeElement { return position ? this._getItemEnclosingPosition(position, this.children) : undefined; } - private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement | undefined { - for (let key in children) { - let item = children[key]; + private _getItemEnclosingPosition(position: IPosition, children: Map): OutlineElement | undefined { + for (const [, item] of children) { if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) { continue; } @@ -149,8 +151,8 @@ export class OutlineGroup extends TreeElement { } updateMarker(marker: IOutlineMarker[]): void { - for (const key in this.children) { - this._updateMarker(marker, this.children[key]); + for (const [, child] of this.children) { + this._updateMarker(marker, child); } } @@ -187,8 +189,8 @@ export class OutlineGroup extends TreeElement { // this outline element. This might remove markers from this element and // therefore we remember that we have had markers. That allows us to render // the dot, saying 'this element has children with markers' - for (const key in item.children) { - this._updateMarker(myMarkers, item.children[key]); + for (const [, child] of item.children) { + this._updateMarker(myMarkers, child); } if (myTopSev) { @@ -202,25 +204,11 @@ export class OutlineGroup extends TreeElement { } } -class MovingAverage { - private _n = 1; - private _val = 0; - - update(value: number): this { - this._val = this._val + (value - this._val) / this._n; - this._n += 1; - return this; - } - - get value(): number { - return this._val; - } -} export class OutlineModel extends TreeElement { - private static readonly _requestDurations = new LRUCache(50, 0.7); + private static readonly _requestDurations = new LanguageFeatureRequestDelays(DocumentSymbolProviderRegistry, 350); private static readonly _requests = new LRUCache, model: OutlineModel | undefined }>(9, 0.75); private static readonly _keys = new class { @@ -264,13 +252,7 @@ export class OutlineModel extends TreeElement { // keep moving average of request durations const now = Date.now(); data.promise.then(() => { - let key = this._keys.for(textModel, false); - let avg = this._requestDurations.get(key); - if (!avg) { - avg = new MovingAverage(); - this._requestDurations.set(key, avg); - } - avg.update(Date.now() - now); + this._requestDurations.update(textModel, Date.now() - now); }); } @@ -302,27 +284,20 @@ export class OutlineModel extends TreeElement { } static getRequestDelay(textModel: ITextModel | null): number { - if (!textModel) { - return 350; - } - const avg = this._requestDurations.get(this._keys.for(textModel, false)); - if (!avg) { - return 350; - } - return Math.max(350, Math.floor(1.3 * avg.value)); + return textModel ? this._requestDurations.get(textModel) : this._requestDurations.min; } private static _create(textModel: ITextModel, token: CancellationToken): Promise { const cts = new CancellationTokenSource(token); - const result = new OutlineModel(textModel); + const result = new OutlineModel(textModel.uri); const provider = DocumentSymbolProviderRegistry.ordered(textModel); const promises = provider.map((provider, index) => { let id = TreeElement.findId(`provider_${index}`, result); - let group = new OutlineGroup(id, result, provider, index); + let group = new OutlineGroup(id, result, provider.displayName ?? 'Unknown Outline Provider', index); - return Promise.resolve(provider.provideDocumentSymbols(result.textModel, cts.token)).then(result => { + return Promise.resolve(provider.provideDocumentSymbols(textModel, cts.token)).then(result => { for (const info of result || []) { OutlineModel._makeOutlineElement(info, group); } @@ -332,7 +307,7 @@ export class OutlineModel extends TreeElement { return group; }).then(group => { if (!TreeElement.empty(group)) { - result._groups[id] = group; + result._groups.set(id, group); } else { group.remove(); } @@ -365,7 +340,7 @@ export class OutlineModel extends TreeElement { OutlineModel._makeOutlineElement(childInfo, res); } } - container.children[res.id] = res; + container.children.set(res.id, res); } static get(element: TreeElement | undefined): OutlineModel | undefined { @@ -381,10 +356,10 @@ export class OutlineModel extends TreeElement { readonly id = 'root'; readonly parent = undefined; - protected _groups: { [id: string]: OutlineGroup; } = Object.create(null); - children: { [id: string]: OutlineGroup | OutlineElement; } = Object.create(null); + protected _groups = new Map(); + children = new Map(); - protected constructor(readonly textModel: ITextModel) { + protected constructor(readonly uri: URI) { super(); this.id = 'root'; @@ -392,17 +367,18 @@ export class OutlineModel extends TreeElement { } adopt(): OutlineModel { - let res = new OutlineModel(this.textModel); - forEach(this._groups, entry => res._groups[entry.key] = entry.value.adopt(res)); + let res = new OutlineModel(this.uri); + for (const [key, value] of this._groups) { + res._groups.set(key, value.adopt(res)); + } return res._compact(); } private _compact(): this { let count = 0; - for (const key in this._groups) { - let group = this._groups[key]; - if (first(group.children) === undefined) { // empty - delete this._groups[key]; + for (const [key, group] of this._groups) { + if (group.children.size === 0) { // empty + this._groups.delete(key); } else { count += 1; } @@ -412,21 +388,20 @@ export class OutlineModel extends TreeElement { this.children = this._groups; } else { // adopt all elements of the first group - let group = first(this._groups); - for (let key in group!.children) { - let child = group!.children[key]; + let group = Iterable.first(this._groups.values())!; + for (let [, child] of group.children) { child.parent = this; - this.children[child.id] = child; + this.children.set(child.id, child); } } return this; } merge(other: OutlineModel): boolean { - if (this.textModel.uri.toString() !== other.textModel.uri.toString()) { + if (this.uri.toString() !== other.uri.toString()) { return false; } - if (size(this._groups) !== size(other._groups)) { + if (this._groups.size !== other._groups.size) { return false; } this._groups = other._groups; @@ -448,8 +423,7 @@ export class OutlineModel extends TreeElement { } let result: OutlineElement | undefined = undefined; - for (const key in this._groups) { - const group = this._groups[key]; + for (const [, group] of this._groups) { result = group.getItemEnclosingPosition(position); if (result && (!preferredGroup || preferredGroup === group)) { break; @@ -467,8 +441,8 @@ export class OutlineModel extends TreeElement { // outline element starts for quicker look up marker.sort(Range.compareRangesUsingStarts); - for (const key in this._groups) { - this._groups[key].updateMarker(marker.slice(0)); + for (const [, group] of this._groups) { + group.updateMarker(marker.slice(0)); } } } diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 03068119208..c0ef5526390 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter, ITreeFilter } from 'vs/base/browser/ui/tree/tree'; -import { values } from 'vs/base/common/collections'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import 'vs/css!./media/outlineTree'; import 'vs/css!./media/symbol-icons'; @@ -19,11 +18,14 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { registerColor, listErrorForeground, listWarningForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; import { IdleValue } from 'vs/base/common/async'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { URI } from 'vs/base/common/uri'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { Iterable } from 'vs/base/common/iterator'; +import { Codicon } from 'vs/base/common/codicons'; export type OutlineItem = OutlineGroup | OutlineElement; @@ -31,13 +33,29 @@ export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelP getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } { if (element instanceof OutlineGroup) { - return element.provider.displayName || element.id; + return element.label; } else { return element.symbol.name; } } } +export class OutlineAccessibilityProvider implements IListAccessibilityProvider { + + constructor(private readonly ariaLabel: string) { } + + getWidgetAriaLabel(): string { + return this.ariaLabel; + } + + getAriaLabel(element: OutlineItem): string | null { + if (element instanceof OutlineGroup) { + return element.label; + } else { + return element.symbol.name; + } + } +} export class OutlineIdentityProvider implements IIdentityProvider { getId(element: OutlineItem): { toString(): string; } { @@ -84,14 +102,14 @@ export class OutlineGroupRenderer implements ITreeRenderer, index: number, template: OutlineGroupTemplate): void { template.label.set( - node.element.provider.displayName || localize('provider', "Outline Provider"), + node.element.label, createMatches(node.filterData) ); } @@ -111,7 +129,7 @@ export class OutlineElementRenderer implements ITreeRenderer= 0) { options.extraClasses.push(`deprecated`); @@ -150,7 +168,7 @@ export class OutlineElementRenderer implements ITreeRenderer 0) { dom.show(template.decoration); - dom.removeClass(template.decoration, 'bubble'); + template.decoration.classList.remove('bubble'); template.decoration.innerText = count < 10 ? count.toString() : '+9'; template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count); template.decoration.style.setProperty('--outline-element-color', cssColor); } else { dom.show(template.decoration); - dom.addClass(template.decoration, 'bubble'); + template.decoration.classList.add('bubble'); template.decoration.innerText = '\uea71'; template.decoration.title = localize('deep.problem', "Contains elements with problems"); template.decoration.style.setProperty('--outline-element-color', cssColor); @@ -290,7 +308,7 @@ export class OutlineFilter implements ITreeFilter { let uri: URI | undefined; if (outline) { - uri = outline.textModel.uri; + uri = outline.uri; } if (!(element instanceof OutlineElement)) { @@ -313,15 +331,15 @@ export class OutlineItemComparator implements ITreeSorter { compare(a: OutlineItem, b: OutlineItem): number { if (a instanceof OutlineGroup && b instanceof OutlineGroup) { - return a.providerIndex - b.providerIndex; + return a.order - b.order; } else if (a instanceof OutlineElement && b instanceof OutlineElement) { if (this.type === OutlineSortOrder.ByKind) { - return a.symbol.kind - b.symbol.kind || this._collator.getValue().compare(a.symbol.name, b.symbol.name); + return a.symbol.kind - b.symbol.kind || this._collator.value.compare(a.symbol.name, b.symbol.name); } else if (this.type === OutlineSortOrder.ByName) { - return this._collator.getValue().compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); + return this._collator.value.compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); } else if (this.type === OutlineSortOrder.ByPosition) { - return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.getValue().compare(a.symbol.name, b.symbol.name); + return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.value.compare(a.symbol.name, b.symbol.name); } } return 0; @@ -330,11 +348,11 @@ export class OutlineItemComparator implements ITreeSorter { export class OutlineDataSource implements IDataSource { - getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement): OutlineItem[] { + getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement) { if (!element) { - return []; + return Iterable.empty(); } - return values(element.children); + return element.children.values(); } } @@ -536,172 +554,172 @@ export const SYMBOL_ICON_VARIABLE_FOREGROUND = registerColor('symbolIcon.variabl hc: '#75BEFF' }, localize('symbolIcon.variableForeground', 'The foreground color for variable symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND); if (symbolIconArrayColor) { - collector.addRule(`.codicon-symbol-array { color: ${symbolIconArrayColor} !important; }`); + collector.addRule(`${Codicon.symbolArray.cssSelector} { color: ${symbolIconArrayColor}; }`); } const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND); if (symbolIconBooleanColor) { - collector.addRule(`.codicon-symbol-boolean { color: ${symbolIconBooleanColor} !important; }`); + collector.addRule(`${Codicon.symbolBoolean.cssSelector} { color: ${symbolIconBooleanColor}; }`); } const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND); if (symbolIconClassColor) { - collector.addRule(`.codicon-symbol-class { color: ${symbolIconClassColor} !important; }`); + collector.addRule(`${Codicon.symbolClass.cssSelector} { color: ${symbolIconClassColor}; }`); } const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND); if (symbolIconMethodColor) { - collector.addRule(`.codicon-symbol-method { color: ${symbolIconMethodColor} !important; }`); + collector.addRule(`${Codicon.symbolMethod.cssSelector} { color: ${symbolIconMethodColor}; }`); } const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND); if (symbolIconColorColor) { - collector.addRule(`.codicon-symbol-color { color: ${symbolIconColorColor} !important; }`); + collector.addRule(`${Codicon.symbolColor.cssSelector} { color: ${symbolIconColorColor}; }`); } const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND); if (symbolIconConstantColor) { - collector.addRule(`.codicon-symbol-constant { color: ${symbolIconConstantColor} !important; }`); + collector.addRule(`${Codicon.symbolConstant.cssSelector} { color: ${symbolIconConstantColor}; }`); } const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND); if (symbolIconConstructorColor) { - collector.addRule(`.codicon-symbol-constructor { color: ${symbolIconConstructorColor} !important; }`); + collector.addRule(`${Codicon.symbolConstructor.cssSelector} { color: ${symbolIconConstructorColor}; }`); } const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND); if (symbolIconEnumeratorColor) { collector.addRule(` - .codicon-symbol-value,.codicon-symbol-enum { color: ${symbolIconEnumeratorColor} !important; }`); + ${Codicon.symbolValue.cssSelector},${Codicon.symbolEnum.cssSelector} { color: ${symbolIconEnumeratorColor}; }`); } const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND); if (symbolIconEnumeratorMemberColor) { - collector.addRule(`.codicon-symbol-enum-member { color: ${symbolIconEnumeratorMemberColor} !important; }`); + collector.addRule(`${Codicon.symbolEnumMember.cssSelector} { color: ${symbolIconEnumeratorMemberColor}; }`); } const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND); if (symbolIconEventColor) { - collector.addRule(`.codicon-symbol-event { color: ${symbolIconEventColor} !important; }`); + collector.addRule(`${Codicon.symbolEvent.cssSelector} { color: ${symbolIconEventColor}; }`); } const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND); if (symbolIconFieldColor) { - collector.addRule(`.codicon-symbol-field { color: ${symbolIconFieldColor} !important; }`); + collector.addRule(`${Codicon.symbolField.cssSelector} { color: ${symbolIconFieldColor}; }`); } const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND); if (symbolIconFileColor) { - collector.addRule(`.codicon-symbol-file { color: ${symbolIconFileColor} !important; }`); + collector.addRule(`${Codicon.symbolFile.cssSelector} { color: ${symbolIconFileColor}; }`); } const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND); if (symbolIconFolderColor) { - collector.addRule(`.codicon-symbol-folder { color: ${symbolIconFolderColor} !important; }`); + collector.addRule(`${Codicon.symbolFolder.cssSelector} { color: ${symbolIconFolderColor}; }`); } const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND); if (symbolIconFunctionColor) { - collector.addRule(`.codicon-symbol-function { color: ${symbolIconFunctionColor} !important; }`); + collector.addRule(`${Codicon.symbolFunction.cssSelector} { color: ${symbolIconFunctionColor}; }`); } const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND); if (symbolIconInterfaceColor) { - collector.addRule(`.codicon-symbol-interface { color: ${symbolIconInterfaceColor} !important; }`); + collector.addRule(`${Codicon.symbolInterface.cssSelector} { color: ${symbolIconInterfaceColor}; }`); } const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND); if (symbolIconKeyColor) { - collector.addRule(`.codicon-symbol-key { color: ${symbolIconKeyColor} !important; }`); + collector.addRule(`${Codicon.symbolKey.cssSelector} { color: ${symbolIconKeyColor}; }`); } const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND); if (symbolIconKeywordColor) { - collector.addRule(`.codicon-symbol-keyword { color: ${symbolIconKeywordColor} !important; }`); + collector.addRule(`${Codicon.symbolKeyword.cssSelector} { color: ${symbolIconKeywordColor}; }`); } const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND); if (symbolIconModuleColor) { - collector.addRule(`.codicon-symbol-module { color: ${symbolIconModuleColor} !important; }`); + collector.addRule(`${Codicon.symbolModule.cssSelector} { color: ${symbolIconModuleColor}; }`); } const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND); if (outlineNamespaceColor) { - collector.addRule(`.codicon-symbol-namespace { color: ${outlineNamespaceColor} !important; }`); + collector.addRule(`${Codicon.symbolNamespace.cssSelector} { color: ${outlineNamespaceColor}; }`); } const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND); if (symbolIconNullColor) { - collector.addRule(`.codicon-symbol-null { color: ${symbolIconNullColor} !important; }`); + collector.addRule(`${Codicon.symbolNull.cssSelector} { color: ${symbolIconNullColor}; }`); } const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND); if (symbolIconNumberColor) { - collector.addRule(`.codicon-symbol-number { color: ${symbolIconNumberColor} !important; }`); + collector.addRule(`${Codicon.symbolNumber.cssSelector} { color: ${symbolIconNumberColor}; }`); } const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND); if (symbolIconObjectColor) { - collector.addRule(`.codicon-symbol-object { color: ${symbolIconObjectColor} !important; }`); + collector.addRule(`${Codicon.symbolObject.cssSelector} { color: ${symbolIconObjectColor}; }`); } const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND); if (symbolIconOperatorColor) { - collector.addRule(`.codicon-symbol-operator { color: ${symbolIconOperatorColor} !important; }`); + collector.addRule(`${Codicon.symbolOperator.cssSelector} { color: ${symbolIconOperatorColor}; }`); } const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND); if (symbolIconPackageColor) { - collector.addRule(`.codicon-symbol-package { color: ${symbolIconPackageColor} !important; }`); + collector.addRule(`${Codicon.symbolPackage.cssSelector} { color: ${symbolIconPackageColor}; }`); } const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND); if (symbolIconPropertyColor) { - collector.addRule(`.codicon-symbol-property { color: ${symbolIconPropertyColor} !important; }`); + collector.addRule(`${Codicon.symbolProperty.cssSelector} { color: ${symbolIconPropertyColor}; }`); } const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND); if (symbolIconReferenceColor) { - collector.addRule(`.codicon-symbol-reference { color: ${symbolIconReferenceColor} !important; }`); + collector.addRule(`${Codicon.symbolReference.cssSelector} { color: ${symbolIconReferenceColor}; }`); } const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND); if (symbolIconSnippetColor) { - collector.addRule(`.codicon-symbol-snippet { color: ${symbolIconSnippetColor} !important; }`); + collector.addRule(`${Codicon.symbolSnippet.cssSelector} { color: ${symbolIconSnippetColor}; }`); } const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND); if (symbolIconStringColor) { - collector.addRule(`.codicon-symbol-string { color: ${symbolIconStringColor} !important; }`); + collector.addRule(`${Codicon.symbolString.cssSelector} { color: ${symbolIconStringColor}; }`); } const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND); if (symbolIconStructColor) { - collector.addRule(`.codicon-symbol-struct { color: ${symbolIconStructColor} !important; }`); + collector.addRule(`${Codicon.symbolStruct.cssSelector} { color: ${symbolIconStructColor}; }`); } const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND); if (symbolIconTextColor) { - collector.addRule(`.codicon-symbol-text { color: ${symbolIconTextColor} !important; }`); + collector.addRule(`${Codicon.symbolText.cssSelector} { color: ${symbolIconTextColor}; }`); } const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND); if (symbolIconTypeParameterColor) { - collector.addRule(`.codicon-symbol-type-parameter { color: ${symbolIconTypeParameterColor} !important; }`); + collector.addRule(`${Codicon.symbolTypeParameter.cssSelector} { color: ${symbolIconTypeParameterColor}; }`); } const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND); if (symbolIconUnitColor) { - collector.addRule(`.codicon-symbol-unit { color: ${symbolIconUnitColor} !important; }`); + collector.addRule(`${Codicon.symbolUnit.cssSelector} { color: ${symbolIconUnitColor}; }`); } const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND); if (symbolIconVariableColor) { - collector.addRule(`.codicon-symbol-variable { color: ${symbolIconVariableColor} !important; }`); + collector.addRule(`${Codicon.symbolVariable.cssSelector} { color: ${symbolIconVariableColor}; }`); } }); diff --git a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts index b41a7f8dd8f..37cd2ad17f4 100644 --- a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts @@ -8,7 +8,7 @@ import { OutlineElement, OutlineGroup, OutlineModel } from '../outlineModel'; import { SymbolKind, DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { URI } from 'vs/base/common/uri'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -16,7 +16,7 @@ suite('OutlineModel', function () { test('OutlineModel#create, cached', async function () { - let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo')); + let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo')); let count = 0; let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { provideDocumentSymbols() { @@ -42,7 +42,7 @@ suite('OutlineModel', function () { test('OutlineModel#create, cached/cancel', async function () { - let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo')); + let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo')); let isCancelled = false; let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { @@ -93,9 +93,9 @@ suite('OutlineModel', function () { let e2 = new OutlineElement('foo3', null!, fakeSymbolInformation(new Range(6, 1, 10, 10))); let group = new OutlineGroup('group', null!, null!, 1); - group.children[e0.id] = e0; - group.children[e1.id] = e1; - group.children[e2.id] = e2; + group.children.set(e0.id, e0); + group.children.set(e1.id, e1); + group.children.set(e2.id, e2); const data = [fakeMarker(new Range(6, 1, 6, 7)), fakeMarker(new Range(1, 1, 1, 4)), fakeMarker(new Range(10, 2, 14, 1))]; data.sort(Range.compareRangesUsingStarts); // model does this @@ -119,9 +119,9 @@ suite('OutlineModel', function () { let c2 = new OutlineElement('A/C', null!, fakeSymbolInformation(new Range(6, 4, 9, 4))); let group = new OutlineGroup('group', null!, null!, 1); - group.children[p.id] = p; - p.children[c1.id] = c1; - p.children[c2.id] = c2; + group.children.set(p.id, p); + p.children.set(c1.id, c1); + p.children.set(c2.id, c2); let data = [ fakeMarker(new Range(2, 4, 5, 4)) @@ -162,13 +162,13 @@ suite('OutlineModel', function () { this._groups = this.children as any; } }; - model.children['g1'] = new OutlineGroup('g1', model, null!, 1); - model.children['g1'].children['c1'] = new OutlineElement('c1', model.children['g1'], fakeSymbolInformation(new Range(1, 1, 11, 1))); + model.children.set('g1', new OutlineGroup('g1', model, null!, 1)); + model.children.get('g1')!.children.set('c1', new OutlineElement('c1', model.children.get('g1')!, fakeSymbolInformation(new Range(1, 1, 11, 1)))); - model.children['g2'] = new OutlineGroup('g2', model, null!, 1); - model.children['g2'].children['c2'] = new OutlineElement('c2', model.children['g2'], fakeSymbolInformation(new Range(1, 1, 7, 1))); - model.children['g2'].children['c2'].children['c2.1'] = new OutlineElement('c2.1', model.children['g2'].children['c2'], fakeSymbolInformation(new Range(1, 3, 2, 19))); - model.children['g2'].children['c2'].children['c2.2'] = new OutlineElement('c2.2', model.children['g2'].children['c2'], fakeSymbolInformation(new Range(4, 1, 6, 10))); + model.children.set('g2', new OutlineGroup('g2', model, null!, 1)); + model.children.get('g2')!.children.set('c2', new OutlineElement('c2', model.children.get('g2')!, fakeSymbolInformation(new Range(1, 1, 7, 1)))); + model.children.get('g2')!.children.get('c2')!.children.set('c2.1', new OutlineElement('c2.1', model.children.get('g2')!.children.get('c2')!, fakeSymbolInformation(new Range(1, 3, 2, 19)))); + model.children.get('g2')!.children.get('c2')!.children.set('c2.2', new OutlineElement('c2.2', model.children.get('g2')!.children.get('c2')!, fakeSymbolInformation(new Range(4, 1, 6, 10)))); model.readyForTesting(); @@ -179,9 +179,9 @@ suite('OutlineModel', function () { model.updateMarker(data); - assert.equal(model.children['g1']!.children['c1'].marker!.count, 2); - assert.equal(model.children['g2']!.children['c2'].children['c2.1'].marker!.count, 1); - assert.equal(model.children['g2']!.children['c2'].children['c2.2'].marker!.count, 1); + assert.equal(model.children.get('g1')!.children.get('c1')!.marker!.count, 2); + assert.equal(model.children.get('g2')!.children.get('c2')!.children.get('c2.1')!.marker!.count, 1); + assert.equal(model.children.get('g2')!.children.get('c2')!.children.get('c2.2')!.marker!.count, 1); }); }); diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index fee09b8429d..c5850601299 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -9,10 +9,10 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution, MultiEditorAction, registerMultiEditorAction } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; +import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, TogglePreserveCaseKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState'; import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget'; @@ -20,7 +20,6 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -30,16 +29,18 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; const SEARCH_STRING_MAX_LENGTH = 524288; -export function getSelectionSearchString(editor: ICodeEditor): string | null { +export function getSelectionSearchString(editor: ICodeEditor, seedSearchStringFromSelection: 'single' | 'multiple' = 'single'): string | null { if (!editor.hasModel()) { return null; } const selection = editor.getSelection(); // if selection spans multiple lines, default search string to empty - if (selection.startLineNumber === selection.endLineNumber) { + + if ((seedSearchStringFromSelection === 'single' && selection.startLineNumber === selection.endLineNumber) + || seedSearchStringFromSelection === 'multiple') { if (selection.isEmpty()) { - let wordAtPosition = editor.getModel().getWordAtPosition(selection.getStartPosition()); + const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition()); if (wordAtPosition) { return wordAtPosition.word; } @@ -61,11 +62,12 @@ export const enum FindStartFocusAction { export interface IFindStartOptions { forceRevealReplace: boolean; - seedSearchStringFromSelection: boolean; + seedSearchStringFromSelection: 'none' | 'single' | 'multiple'; seedSearchStringFromGlobalClipboard: boolean; shouldFocus: FindStartFocusAction; shouldAnimate: boolean; updateSearchScope: boolean; + loop: boolean; } export class CommonFindController extends Disposable implements IEditorContribution { @@ -121,11 +123,12 @@ export class CommonFindController extends Disposable implements IEditorContribut if (shouldRestartFind) { this._start({ forceRevealReplace: false, - seedSearchStringFromSelection: false && this._editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: false + updateSearchScope: false, + loop: this._editor.getOption(EditorOption.find).loop }); } })); @@ -222,7 +225,9 @@ export class CommonFindController extends Disposable implements IEditorContribut public togglePreserveCase(): void { this._state.change({ preserveCase: !this._state.preserveCase }, false); - this.highlightFindOptions(); + if (!this._state.isRevealed) { + this.highlightFindOptions(); + } } public toggleSearchScope(): void { @@ -230,12 +235,22 @@ export class CommonFindController extends Disposable implements IEditorContribut this._state.change({ searchScope: null }, true); } else { if (this._editor.hasModel()) { - let selection = this._editor.getSelection(); - if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { - selection = selection.setEndPosition(selection.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(selection.endLineNumber - 1)); - } - if (!selection.isEmpty()) { - this._state.change({ searchScope: selection }, true); + let selections = this._editor.getSelections(); + selections.map(selection => { + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition( + selection.endLineNumber - 1, + this._editor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1) + ); + } + if (!selection.isEmpty()) { + return selection; + } + return null; + }).filter(element => !!element); + + if (selections.length) { + this._state.change({ searchScope: selections }, true); } } } @@ -252,7 +267,7 @@ export class CommonFindController extends Disposable implements IEditorContribut // overwritten in subclass } - protected _start(opts: IFindStartOptions): void { + protected async _start(opts: IFindStartOptions): Promise { this.disposeModel(); if (!this._editor.hasModel()) { @@ -264,8 +279,8 @@ export class CommonFindController extends Disposable implements IEditorContribut isRevealed: true }; - if (opts.seedSearchStringFromSelection) { - let selectionSearchString = getSelectionSearchString(this._editor); + if (opts.seedSearchStringFromSelection === 'single') { + let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection); if (selectionSearchString) { if (this._state.isRegex) { stateChanges.searchString = strings.escapeRegExpCharacters(selectionSearchString); @@ -273,10 +288,21 @@ export class CommonFindController extends Disposable implements IEditorContribut stateChanges.searchString = selectionSearchString; } } + } else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) { + let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection); + if (selectionSearchString) { + stateChanges.searchString = selectionSearchString; + } } if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) { - let selectionSearchString = this.getGlobalBufferTerm(); + let selectionSearchString = await this.getGlobalBufferTerm(); + + if (!this._editor.hasModel()) { + // the editor has lost its model in the meantime + return; + } + if (selectionSearchString) { stateChanges.searchString = selectionSearchString; } @@ -290,12 +316,14 @@ export class CommonFindController extends Disposable implements IEditorContribut } if (opts.updateSearchScope) { - let currentSelection = this._editor.getSelection(); - if (!currentSelection.isEmpty()) { - stateChanges.searchScope = currentSelection; + let currentSelections = this._editor.getSelections(); + if (currentSelections.some(selection => !selection.isEmpty())) { + stateChanges.searchScope = currentSelections; } } + stateChanges.loop = opts.loop; + this._state.change(stateChanges, false); if (!this._model) { @@ -303,8 +331,8 @@ export class CommonFindController extends Disposable implements IEditorContribut } } - public start(opts: IFindStartOptions): void { - this._start(opts); + public start(opts: IFindStartOptions): Promise { + return this._start(opts); } public moveToNextMatch(): boolean { @@ -348,9 +376,8 @@ export class CommonFindController extends Disposable implements IEditorContribut return false; } - public getGlobalBufferTerm(): string { + public async getGlobalBufferTerm(): Promise { if (this._editor.getOption(EditorOption.find).globalFindClipboard - && this._clipboardService && this._editor.hasModel() && !this._editor.getModel().isTooLargeForSyncing() ) { @@ -359,12 +386,12 @@ export class CommonFindController extends Disposable implements IEditorContribut return ''; } - public setGlobalBufferTerm(text: string) { + public setGlobalBufferTerm(text: string): void { if (this._editor.getOption(EditorOption.find).globalFindClipboard - && this._clipboardService && this._editor.hasModel() && !this._editor.getModel().isTooLargeForSyncing() ) { + // intentionally not awaited this._clipboardService.writeFindText(text); } } @@ -383,14 +410,14 @@ export class FindController extends CommonFindController implements IFindControl @IThemeService private readonly _themeService: IThemeService, @INotificationService private readonly _notificationService: INotificationService, @IStorageService _storageService: IStorageService, - @optional(IClipboardService) clipboardService: IClipboardService, + @IClipboardService clipboardService: IClipboardService, ) { super(editor, _contextKeyService, _storageService, clipboardService); this._widget = null; this._findOptionsWidget = null; } - protected _start(opts: IFindStartOptions): void { + protected async _start(opts: IFindStartOptions): Promise { if (!this._widget) { this._createFindWidget(); } @@ -416,12 +443,14 @@ export class FindController extends CommonFindController implements IFindControl opts.updateSearchScope = updateSearchScope; - super._start(opts); + await super._start(opts); - if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) { - this._widget!.focusReplaceInput(); - } else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) { - this._widget!.focusFindInput(); + if (this._widget) { + if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) { + this._widget.focusReplaceInput(); + } else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) { + this._widget.focusFindInput(); + } } } @@ -442,14 +471,14 @@ export class FindController extends CommonFindController implements IFindControl } } -export class StartFindAction extends EditorAction { +export class StartFindAction extends MultiEditorAction { constructor() { super({ id: FIND_IDS.StartFindAction, label: nls.localize('startFindAction', "Find"), alias: 'Find', - precondition: undefined, + precondition: ContextKeyExpr.or(ContextKeyExpr.has('editorFocus'), ContextKeyExpr.has('editorIsOpen')), kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_F, @@ -464,16 +493,17 @@ export class StartFindAction extends EditorAction { }); } - public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (controller) { - controller.start({ + await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); } } @@ -498,16 +528,17 @@ export class StartFindWithSelectionAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (controller) { - controller.start({ + await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: true, + seedSearchStringFromSelection: 'multiple', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); controller.setGlobalBufferTerm(controller.getState().searchString); @@ -515,16 +546,17 @@ export class StartFindWithSelectionAction extends EditorAction { } } export abstract class MatchFindAction extends EditorAction { - public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (controller && !this._run(controller)) { - controller.start({ + await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: true, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); this._run(controller); } @@ -620,7 +652,7 @@ export class PreviousMatchFindAction2 extends MatchFindAction { } export abstract class SelectionMatchFindAction extends EditorAction { - public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (!controller) { return; @@ -630,13 +662,14 @@ export abstract class SelectionMatchFindAction extends EditorAction { controller.setSearchString(selectionSearchString); } if (!this._run(controller)) { - controller.start({ + await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); this._run(controller); } @@ -687,14 +720,14 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction { } } -export class StartFindReplaceAction extends EditorAction { +export class StartFindReplaceAction extends MultiEditorAction { constructor() { super({ id: FIND_IDS.StartFindReplaceAction, label: nls.localize('startReplace', "Replace"), alias: 'Replace', - precondition: undefined, + precondition: ContextKeyExpr.or(ContextKeyExpr.has('editorFocus'), ContextKeyExpr.has('editorIsOpen')), kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_H, @@ -710,7 +743,7 @@ export class StartFindReplaceAction extends EditorAction { }); } - public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { if (!editor.hasModel() || editor.getOption(EditorOption.readOnly)) { return; } @@ -735,13 +768,14 @@ export class StartFindReplaceAction extends EditorAction { if (controller) { - controller.start({ + await controller.start({ forceRevealReplace: true, - seedSearchStringFromSelection: seedSearchStringFromSelection, + seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection, shouldFocus: shouldFocus, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); } } @@ -749,7 +783,8 @@ export class StartFindReplaceAction extends EditorAction { registerEditorContribution(CommonFindController.ID, FindController); -registerEditorAction(StartFindAction); +export const EditorStartFindAction = new StartFindAction(); +registerMultiEditorAction(EditorStartFindAction); registerEditorAction(StartFindWithSelectionAction); registerEditorAction(NextMatchFindAction); registerEditorAction(NextMatchFindAction2); @@ -757,7 +792,8 @@ registerEditorAction(PreviousMatchFindAction); registerEditorAction(PreviousMatchFindAction2); registerEditorAction(NextSelectionMatchFindAction); registerEditorAction(PreviousSelectionMatchFindAction); -registerEditorAction(StartFindReplaceAction); +export const EditorStartFindReplaceAction = new StartFindReplaceAction(); +registerMultiEditorAction(EditorStartFindReplaceAction); const FindCommand = EditorCommand.bindToContribution(CommonFindController.get); @@ -767,7 +803,7 @@ registerEditorCommand(new FindCommand({ handler: x => x.closeFindWidget(), kbOpts: { weight: KeybindingWeight.EditorContrib + 5, - kbExpr: EditorContextKeys.focus, + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, ContextKeyExpr.not('isComposing')), primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] } @@ -829,6 +865,20 @@ registerEditorCommand(new FindCommand({ } })); +registerEditorCommand(new FindCommand({ + id: FIND_IDS.TogglePreserveCaseCommand, + precondition: undefined, + handler: x => x.togglePreserveCase(), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 5, + kbExpr: EditorContextKeys.focus, + primary: TogglePreserveCaseKeybinding.primary, + mac: TogglePreserveCaseKeybinding.mac, + win: TogglePreserveCaseKeybinding.win, + linux: TogglePreserveCaseKeybinding.linux + } +})); + registerEditorCommand(new FindCommand({ id: FIND_IDS.ReplaceOneAction, precondition: CONTEXT_FIND_WIDGET_VISIBLE, diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index ee93bccc3cf..c6b966e77c1 100644 --- a/src/vs/editor/contrib/find/findDecorations.ts +++ b/src/vs/editor/contrib/find/findDecorations.ts @@ -17,7 +17,7 @@ export class FindDecorations implements IDisposable { private readonly _editor: IActiveCodeEditor; private _decorations: string[]; private _overviewRulerApproximateDecorations: string[]; - private _findScopeDecorationId: string | null; + private _findScopeDecorationIds: string[]; private _rangeHighlightDecorationId: string | null; private _highlightedDecorationId: string | null; private _startPosition: Position; @@ -26,7 +26,7 @@ export class FindDecorations implements IDisposable { this._editor = editor; this._decorations = []; this._overviewRulerApproximateDecorations = []; - this._findScopeDecorationId = null; + this._findScopeDecorationIds = []; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; this._startPosition = this._editor.getPosition(); @@ -37,7 +37,7 @@ export class FindDecorations implements IDisposable { this._decorations = []; this._overviewRulerApproximateDecorations = []; - this._findScopeDecorationId = null; + this._findScopeDecorationIds = []; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; } @@ -45,7 +45,7 @@ export class FindDecorations implements IDisposable { public reset(): void { this._decorations = []; this._overviewRulerApproximateDecorations = []; - this._findScopeDecorationId = null; + this._findScopeDecorationIds = []; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; } @@ -54,9 +54,22 @@ export class FindDecorations implements IDisposable { return this._decorations.length; } + /** @deprecated use getFindScopes to support multiple selections */ public getFindScope(): Range | null { - if (this._findScopeDecorationId) { - return this._editor.getModel().getDecorationRange(this._findScopeDecorationId); + if (this._findScopeDecorationIds[0]) { + return this._editor.getModel().getDecorationRange(this._findScopeDecorationIds[0]); + } + return null; + } + + public getFindScopes(): Range[] | null { + if (this._findScopeDecorationIds.length) { + const scopes = this._findScopeDecorationIds.map(findScopeDecorationId => + this._editor.getModel().getDecorationRange(findScopeDecorationId) + ).filter(element => !!element); + if (scopes.length) { + return scopes as Range[]; + } } return null; } @@ -86,7 +99,8 @@ export class FindDecorations implements IDisposable { return this._getDecorationIndex(candidate.id); } } - return 1; + // We don't know the current match position, so returns zero to show '?' in find widget + return 0; } public setCurrentFindMatch(nextMatch: Range | null): number { @@ -132,7 +146,7 @@ export class FindDecorations implements IDisposable { return matchPosition; } - public set(findMatches: FindMatch[], findScope: Range | null): void { + public set(findMatches: FindMatch[], findScopes: Range[] | null): void { this._editor.changeDecorations((accessor) => { let findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; @@ -194,12 +208,12 @@ export class FindDecorations implements IDisposable { } // Find scope - if (this._findScopeDecorationId) { - accessor.removeDecoration(this._findScopeDecorationId); - this._findScopeDecorationId = null; + if (this._findScopeDecorationIds.length) { + this._findScopeDecorationIds.forEach(findScopeDecorationId => accessor.removeDecoration(findScopeDecorationId)); + this._findScopeDecorationIds = []; } - if (findScope) { - this._findScopeDecorationId = accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION); + if (findScopes?.length) { + this._findScopeDecorationIds = findScopes.map(findScope => accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION)); } }); } @@ -252,8 +266,8 @@ export class FindDecorations implements IDisposable { let result: string[] = []; result = result.concat(this._decorations); result = result.concat(this._overviewRulerApproximateDecorations); - if (this._findScopeDecorationId) { - result.push(this._findScopeDecorationId); + if (this._findScopeDecorationIds.length) { + result.push(...this._findScopeDecorationIds); } if (this._rangeHighlightDecorationId) { result.push(this._rangeHighlightDecorationId); @@ -261,7 +275,7 @@ export class FindDecorations implements IDisposable { return result; } - private static readonly _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({ + public static readonly _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, zIndex: 13, className: 'currentFindMatch', @@ -276,7 +290,7 @@ export class FindDecorations implements IDisposable { } }); - private static readonly _FIND_MATCH_DECORATION = ModelDecorationOptions.register({ + public static readonly _FIND_MATCH_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'findMatch', showIfCollapsed: true, @@ -290,7 +304,7 @@ export class FindDecorations implements IDisposable { } }); - private static readonly _FIND_MATCH_NO_OVERVIEW_DECORATION = ModelDecorationOptions.register({ + public static readonly _FIND_MATCH_NO_OVERVIEW_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'findMatch', showIfCollapsed: true diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index 0653ac4f5c4..11ec1c63505 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -20,12 +20,13 @@ import { FindDecorations } from 'vs/editor/contrib/find/findDecorations'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { ReplaceAllCommand } from 'vs/editor/contrib/find/replaceAllCommand'; import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { findFirstInSorted } from 'vs/base/common/arrays'; export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey('findWidgetVisible', false); -export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); +export const CONTEXT_FIND_WIDGET_NOT_VISIBLE = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); // Keep ContextKey use of 'Focussed' to not break when clauses export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey('findInputFocussed', false); export const CONTEXT_REPLACE_INPUT_FOCUSED = new RawContextKey('replaceInputFocussed', false); @@ -46,6 +47,10 @@ export const ToggleSearchScopeKeybinding: IKeybindings = { primary: KeyMod.Alt | KeyCode.KEY_L, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } }; +export const TogglePreserveCaseKeybinding: IKeybindings = { + primary: KeyMod.Alt | KeyCode.KEY_P, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P } +}; export const FIND_IDS = { StartFindAction: 'actions.find', @@ -168,34 +173,53 @@ export class FindModelBoundToEditorModel { return model.getFullModelRange(); } - private research(moveCursor: boolean, newFindScope?: Range | null): void { - let findScope: Range | null = null; + private research(moveCursor: boolean, newFindScope?: Range | Range[] | null): void { + let findScopes: Range[] | null = null; if (typeof newFindScope !== 'undefined') { - findScope = newFindScope; - } else { - findScope = this._decorations.getFindScope(); - } - if (findScope !== null) { - if (findScope.startLineNumber !== findScope.endLineNumber) { - if (findScope.endColumn === 1) { - findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber - 1)); + if (newFindScope !== null) { + if (!Array.isArray(newFindScope)) { + findScopes = [newFindScope as Range]; } else { - // multiline find scope => expand to line starts / ends - findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber)); + findScopes = newFindScope; } } + } else { + findScopes = this._decorations.getFindScopes(); + } + if (findScopes !== null) { + findScopes = findScopes.map(findScope => { + if (findScope.startLineNumber !== findScope.endLineNumber) { + let endLineNumber = findScope.endLineNumber; + + if (findScope.endColumn === 1) { + endLineNumber = endLineNumber - 1; + } + + return new Range(findScope.startLineNumber, 1, endLineNumber, this._editor.getModel().getLineMaxColumn(endLineNumber)); + } + return findScope; + }); } - let findMatches = this._findMatches(findScope, false, MATCHES_LIMIT); - this._decorations.set(findMatches, findScope); + let findMatches = this._findMatches(findScopes, false, MATCHES_LIMIT); + this._decorations.set(findMatches, findScopes); + + const editorSelection = this._editor.getSelection(); + let currentMatchesPosition = this._decorations.getCurrentMatchesPosition(editorSelection); + if (currentMatchesPosition === 0 && findMatches.length > 0) { + // current selection is not on top of a match + // try to find its nearest result from the top of the document + const matchAfterSelection = findFirstInSorted(findMatches.map(match => match.range), range => Range.compareRangesUsingStarts(range, editorSelection) >= 0); + currentMatchesPosition = matchAfterSelection > 0 ? matchAfterSelection - 1 + 1 /** match position is one based */ : currentMatchesPosition; + } this._state.changeMatchInfo( - this._decorations.getCurrentMatchesPosition(this._editor.getSelection()), + currentMatchesPosition, this._decorations.getCount(), undefined ); - if (moveCursor) { + if (moveCursor && this._editor.getOption(EditorOption.find).cursorMoveOnType) { this._moveToNextMatch(this._decorations.getStartPosition()); } } @@ -251,6 +275,16 @@ export class FindModelBoundToEditorModel { } private _moveToPrevMatch(before: Position, isRecursed: boolean = false): void { + if (!this._state.canNavigateBack()) { + // we are beyond the first matched find result + // instead of doing nothing, we should refocus the first item + const nextMatchRange = this._decorations.matchAfterPosition(before); + + if (nextMatchRange) { + this._setCurrentFindMatch(nextMatchRange); + } + return; + } if (this._decorations.getCount() < MATCHES_LIMIT) { let prevMatchRange = this._decorations.matchBeforePosition(before); @@ -336,6 +370,16 @@ export class FindModelBoundToEditorModel { } private _moveToNextMatch(after: Position): void { + if (!this._state.canNavigateForward()) { + // we are beyond the last matched find result + // instead of doing nothing, we should refocus the last item + const prevMatchRange = this._decorations.matchBeforePosition(after); + + if (prevMatchRange) { + this._setCurrentFindMatch(prevMatchRange); + } + return; + } if (this._decorations.getCount() < MATCHES_LIMIT) { let nextMatchRange = this._decorations.matchAfterPosition(after); @@ -437,9 +481,12 @@ export class FindModelBoundToEditorModel { } } - private _findMatches(findScope: Range | null, captureMatches: boolean, limitResultCount: number): FindMatch[] { - let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), findScope); - return this._editor.getModel().findMatches(this._state.searchString, searchRange, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, captureMatches, limitResultCount); + private _findMatches(findScopes: Range[] | null, captureMatches: boolean, limitResultCount: number): FindMatch[] { + const searchRanges = (findScopes as [] || [null]).map((scope: Range | null) => + FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), scope) + ); + + return this._editor.getModel().findMatches(this._state.searchString, searchRanges, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, captureMatches, limitResultCount); } public replaceAll(): void { @@ -447,13 +494,13 @@ export class FindModelBoundToEditorModel { return; } - const findScope = this._decorations.getFindScope(); + const findScopes = this._decorations.getFindScopes(); - if (findScope === null && this._state.matchesCount >= MATCHES_LIMIT) { + if (findScopes === null && this._state.matchesCount >= MATCHES_LIMIT) { // Doing a replace on the entire file that is over ${MATCHES_LIMIT} matches this._largeReplaceAll(); } else { - this._regularReplaceAll(findScope); + this._regularReplaceAll(findScopes); } this.research(false); @@ -498,10 +545,10 @@ export class FindModelBoundToEditorModel { this._executeEditorCommand('replaceAll', command); } - private _regularReplaceAll(findScope: Range | null): void { + private _regularReplaceAll(findScopes: Range[] | null): void { const replacePattern = this._getReplacePattern(); // Get all the ranges (even more than the highlighted ones) - let matches = this._findMatches(findScope, replacePattern.hasReplacementPatterns || this._state.preserveCase, Constants.MAX_SAFE_SMALL_INTEGER); + let matches = this._findMatches(findScopes, replacePattern.hasReplacementPatterns || this._state.preserveCase, Constants.MAX_SAFE_SMALL_INTEGER); let replaceStrings: string[] = []; for (let i = 0, len = matches.length; i < len; i++) { @@ -517,10 +564,10 @@ export class FindModelBoundToEditorModel { return; } - let findScope = this._decorations.getFindScope(); + let findScopes = this._decorations.getFindScopes(); // Get all the ranges (even more than the highlighted ones) - let matches = this._findMatches(findScope, false, Constants.MAX_SAFE_SMALL_INTEGER); + let matches = this._findMatches(findScopes, false, Constants.MAX_SAFE_SMALL_INTEGER); let selections = matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn)); // If one of the ranges is the editor selection, then maintain it as primary diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index b3d9b69e1f5..d541e0d0608 100644 --- a/src/vs/editor/contrib/find/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/findOptionsWidget.ts @@ -11,8 +11,8 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPosit import { FIND_IDS } from 'vs/editor/contrib/find/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, widgetShadow, editorWidgetForeground, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class FindOptionsWidget extends Widget implements IOverlayWidget { @@ -46,13 +46,15 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this._domNode.setAttribute('role', 'presentation'); this._domNode.setAttribute('aria-hidden', 'true'); - const inputActiveOptionBorderColor = themeService.getTheme().getColor(inputActiveOptionBorder); - const inputActiveOptionBackgroundColor = themeService.getTheme().getColor(inputActiveOptionBackground); + const inputActiveOptionBorderColor = themeService.getColorTheme().getColor(inputActiveOptionBorder); + const inputActiveOptionForegroundColor = themeService.getColorTheme().getColor(inputActiveOptionForeground); + const inputActiveOptionBackgroundColor = themeService.getColorTheme().getColor(inputActiveOptionBackground); this.caseSensitive = this._register(new CaseSensitiveCheckbox({ appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand), isChecked: this._state.matchCase, inputActiveOptionBorder: inputActiveOptionBorderColor, + inputActiveOptionForeground: inputActiveOptionForegroundColor, inputActiveOptionBackground: inputActiveOptionBackgroundColor })); this._domNode.appendChild(this.caseSensitive.domNode); @@ -66,6 +68,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand), isChecked: this._state.wholeWord, inputActiveOptionBorder: inputActiveOptionBorderColor, + inputActiveOptionForeground: inputActiveOptionForegroundColor, inputActiveOptionBackground: inputActiveOptionBackgroundColor })); this._domNode.appendChild(this.wholeWords.domNode); @@ -79,6 +82,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand), isChecked: this._state.isRegex, inputActiveOptionBorder: inputActiveOptionBorderColor, + inputActiveOptionForeground: inputActiveOptionForegroundColor, inputActiveOptionBackground: inputActiveOptionBackgroundColor })); this._domNode.appendChild(this.regex.domNode); @@ -112,8 +116,8 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this._register(dom.addDisposableNonBubblingMouseOutListener(this._domNode, (e) => this._onMouseOut())); this._register(dom.addDisposableListener(this._domNode, 'mouseover', (e) => this._onMouseOver())); - this._applyTheme(themeService.getTheme()); - this._register(themeService.onThemeChange(this._applyTheme.bind(this))); + this._applyTheme(themeService.getColorTheme()); + this._register(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); } private _keybindingLabelFor(actionId: string): string { @@ -182,9 +186,10 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this._domNode.style.display = 'none'; } - private _applyTheme(theme: ITheme) { + private _applyTheme(theme: IColorTheme) { let inputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputActiveOptionForeground: theme.getColor(inputActiveOptionForeground), inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground) }; this.caseSensitive.style(inputStyles); diff --git a/src/vs/editor/contrib/find/findState.ts b/src/vs/editor/contrib/find/findState.ts index 22451cf9bed..0313dd8bc46 100644 --- a/src/vs/editor/contrib/find/findState.ts +++ b/src/vs/editor/contrib/find/findState.ts @@ -6,6 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; +import { MATCHES_LIMIT } from './findModel'; export interface FindReplaceStateChangedEvent { moveCursor: boolean; @@ -23,6 +24,7 @@ export interface FindReplaceStateChangedEvent { matchesPosition: boolean; matchesCount: boolean; currentMatch: boolean; + loop: boolean; } export const enum FindOptionOverride { @@ -44,7 +46,8 @@ export interface INewFindReplaceState { matchCaseOverride?: FindOptionOverride; preserveCase?: boolean; preserveCaseOverride?: FindOptionOverride; - searchScope?: Range | null; + searchScope?: Range[] | null; + loop?: boolean; } function effectiveOptionValue(override: FindOptionOverride, value: boolean): boolean { @@ -70,10 +73,11 @@ export class FindReplaceState extends Disposable { private _matchCaseOverride: FindOptionOverride; private _preserveCase: boolean; private _preserveCaseOverride: FindOptionOverride; - private _searchScope: Range | null; + private _searchScope: Range[] | null; private _matchesPosition: number; private _matchesCount: number; private _currentMatch: Range | null; + private _loop: boolean; private readonly _onFindReplaceStateChange = this._register(new Emitter()); public get searchString(): string { return this._searchString; } @@ -90,7 +94,7 @@ export class FindReplaceState extends Disposable { public get actualMatchCase(): boolean { return this._matchCase; } public get actualPreserveCase(): boolean { return this._preserveCase; } - public get searchScope(): Range | null { return this._searchScope; } + public get searchScope(): Range[] | null { return this._searchScope; } public get matchesPosition(): number { return this._matchesPosition; } public get matchesCount(): number { return this._matchesCount; } public get currentMatch(): Range | null { return this._currentMatch; } @@ -114,6 +118,7 @@ export class FindReplaceState extends Disposable { this._matchesPosition = 0; this._matchesCount = 0; this._currentMatch = null; + this._loop = true; } public changeMatchInfo(matchesPosition: number, matchesCount: number, currentMatch: Range | undefined): void { @@ -131,7 +136,8 @@ export class FindReplaceState extends Disposable { searchScope: false, matchesPosition: false, matchesCount: false, - currentMatch: false + currentMatch: false, + loop: false }; let somethingChanged = false; @@ -181,7 +187,8 @@ export class FindReplaceState extends Disposable { searchScope: false, matchesPosition: false, matchesCount: false, - currentMatch: false + currentMatch: false, + loop: false }; let somethingChanged = false; @@ -231,13 +238,23 @@ export class FindReplaceState extends Disposable { this._preserveCase = newState.preserveCase; } if (typeof newState.searchScope !== 'undefined') { - if (!Range.equalsRange(this._searchScope, newState.searchScope)) { + if (!newState.searchScope?.every((newSearchScope) => { + return this._searchScope?.some(existingSearchScope => { + return !Range.equalsRange(existingSearchScope, newSearchScope); + }); + })) { this._searchScope = newState.searchScope; changeEvent.searchScope = true; somethingChanged = true; } } - + if (typeof newState.loop !== 'undefined') { + if (this._loop !== newState.loop) { + this._loop = newState.loop; + changeEvent.loop = true; + somethingChanged = true; + } + } // Overrides get set when they explicitly come in and get reset anytime something else changes this._isRegexOverride = (typeof newState.isRegexOverride !== 'undefined' ? newState.isRegexOverride : FindOptionOverride.NotSet); this._wholeWordOverride = (typeof newState.wholeWordOverride !== 'undefined' ? newState.wholeWordOverride : FindOptionOverride.NotSet); @@ -266,4 +283,17 @@ export class FindReplaceState extends Disposable { this._onFindReplaceStateChange.fire(changeEvent); } } + + public canNavigateBack(): boolean { + return this.canNavigateInLoop() || (this.matchesPosition !== 1); + } + + public canNavigateForward(): boolean { + return this.canNavigateInLoop() || (this.matchesPosition < this.matchesCount); + } + + private canNavigateInLoop(): boolean { + return this._loop || (this.matchesCount >= MATCHES_LIMIT); + } + } diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 8e85d700b4e..15ed9eb3f75 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -6,14 +6,14 @@ /* Find widget */ .monaco-editor .find-widget { position: absolute; - z-index: 10; + z-index: 50; height: 33px; overflow: hidden; line-height: 19px; transition: transform 200ms linear; padding: 0 4px; box-sizing: border-box; - transform: translateY(Calc(-100% - 10px)); /* shadow (10px) */ + transform: translateY(calc(-100% - 10px)); /* shadow (10px) */ } .monaco-editor .find-widget textarea { @@ -126,10 +126,6 @@ justify-content: center; } -.monaco-editor .find-widget .button:not(.disabled):hover { - background-color: rgba(0, 0, 0, 0.1); -} - .monaco-editor .find-widget .button.left { margin-left: 0; margin-right: 3px; @@ -207,13 +203,7 @@ } .monaco-editor .find-widget .monaco-sash { - width: 2px !important; - margin-left: -4px; -} - -.monaco-editor.hc-black .find-widget .button:not(.disabled):hover, -.monaco-editor.vs-dark .find-widget .button:not(.disabled):hover { - background-color: rgba(255, 255, 255, 0.1); + left: 0 !important; } .monaco-editor.hc-black .find-widget .button:before { diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 8cf2da8896c..5566fb24392 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -14,7 +14,7 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; -import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; +import { IVerticalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; import { Delayer } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; @@ -30,17 +30,28 @@ import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MA import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + +const findSelectionIcon = registerIcon('find-selection', Codicon.selection); +const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight); +const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown); + +export const findCloseIcon = registerIcon('find-close', Codicon.close); +export const findReplaceIcon = registerIcon('find-replace', Codicon.replace); +export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll); +export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp); +export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDown); export interface IFindController { replace(): void; replaceAll(): void; - getGlobalBufferTerm(): string; + getGlobalBufferTerm(): Promise; } const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); @@ -56,7 +67,7 @@ const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replac const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode"); const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first {0} results are highlighted, but all find operations work on the entire text.", MATCHES_LIMIT); const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}"); -const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results"); +const NLS_NO_RESULTS = nls.localize('label.noResults', "No results"); const FIND_WIDGET_INITIAL_WIDTH = 419; const PART_WIDTH = 275; @@ -101,7 +112,7 @@ function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: stri } } -export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider { +export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashLayoutProvider { private static readonly ID = 'editor.contrib.findWidget'; private readonly _codeEditor: ICodeEditor; private readonly _state: FindReplaceState; @@ -210,9 +221,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._updateToggleSelectionFindButton(); } })); - this._register(this._codeEditor.onDidFocusEditorWidget(() => { + this._register(this._codeEditor.onDidFocusEditorWidget(async () => { if (this._isVisible) { - let globalBufferTerm = this._controller.getGlobalBufferTerm(); + let globalBufferTerm = await this._controller.getGlobalBufferTerm(); if (globalBufferTerm && globalBufferTerm !== this._state.searchString) { this._state.change({ searchString: globalBufferTerm }, true); this._findInput.select(); @@ -244,8 +255,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._viewZone = new FindWidgetViewZone(0); // Put it before the first line then users can scroll beyond the first line. } - this._applyTheme(themeService.getTheme()); - this._register(themeService.onThemeChange(this._applyTheme.bind(this))); + this._applyTheme(themeService.getColorTheme()); + this._register(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this._register(this._codeEditor.onDidChangeModel(() => { if (!this._isVisible) { @@ -339,6 +350,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (e.matchCase) { this._findInput.setCaseSensitive(this._state.matchCase); } + if (e.preserveCase) { + this._replaceInput.setPreserveCase(this._state.preserveCase); + } if (e.searchScope) { if (this._state.searchScope) { this._toggleSelectionFind.checked = true; @@ -349,7 +363,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } if (e.searchString || e.matchesCount || e.matchesPosition) { let showRedOutline = (this._state.searchString.length > 0 && this._state.matchesCount === 0); - dom.toggleClass(this._domNode, 'no-results', showRedOutline); + this._domNode.classList.toggle('no-results', showRedOutline); this._updateMatchesCount(); this._updateButtons(); @@ -360,6 +374,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (e.updateHistory) { this._delayedUpdateHistory(); } + if (e.loop) { + this._updateButtons(); + } } private _delayedUpdateHistory() { @@ -405,7 +422,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._matchesCount.appendChild(document.createTextNode(label)); - alertFn(this._getAriaLabel(label, this._state.currentMatch, this._state.searchString), true); + alertFn(this._getAriaLabel(label, this._state.currentMatch, this._state.searchString)); MAX_MATCHES_COUNT_WIDTH = Math.max(MAX_MATCHES_COUNT_WIDTH, this._matchesCount.clientWidth); } @@ -415,11 +432,20 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (label === NLS_NO_RESULTS) { return searchString === '' ? nls.localize('ariaSearchNoResultEmpty', "{0} found", label) - : nls.localize('ariaSearchNoResult', "{0} found for {1}", label, searchString); + : nls.localize('ariaSearchNoResult', "{0} found for '{1}'", label, searchString); } - return currentMatch - ? nls.localize('ariaSearchNoResultWithLineNum', "{0} found for {1} at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn) - : nls.localize('ariaSearchNoResultWithLineNumNoCurrentMatch', "{0} found for {1}", label, searchString); + if (currentMatch) { + const ariaLabel = nls.localize('ariaSearchNoResultWithLineNum', "{0} found for '{1}', at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn); + const model = this._codeEditor.getModel(); + if (model && (currentMatch.startLineNumber <= model.getLineCount()) && (currentMatch.startLineNumber >= 1)) { + const lineContent = model.getLineContent(currentMatch.startLineNumber); + return `${lineContent}, ${ariaLabel}`; + } + + return ariaLabel; + } + + return nls.localize('ariaSearchNoResultWithLineNumNoCurrentMatch', "{0} found for '{1}'", label, searchString); } /** @@ -446,14 +472,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas let findInputIsNonEmpty = (this._state.searchString.length > 0); let matchesCount = this._state.matchesCount ? true : false; - this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount); - this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount); + this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount && this._state.canNavigateBack()); + this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount && this._state.canNavigateForward()); this._replaceBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); this._replaceAllBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); - dom.toggleClass(this._domNode, 'replaceToggled', this._isReplaceVisible); - this._toggleReplaceBtn.toggleClass('codicon-chevron-right', !this._isReplaceVisible); - this._toggleReplaceBtn.toggleClass('codicon-chevron-down', this._isReplaceVisible); + this._domNode.classList.toggle('replaceToggled', this._isReplaceVisible); this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); let canReplace = !this._codeEditor.getOption(EditorOption.readOnly); @@ -486,7 +510,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._updateButtons(); setTimeout(() => { - dom.addClass(this._domNode, 'visible'); + this._domNode.classList.add('visible'); this._domNode.setAttribute('aria-hidden', 'false'); }, 0); @@ -533,7 +557,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._updateButtons(); - dom.removeClass(this._domNode, 'visible'); + this._domNode.classList.remove('visible'); this._domNode.setAttribute('aria-hidden', 'true'); this._findInput.clearMessage(); if (focusTheEditor) { @@ -603,7 +627,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return; } else { - const scrollAdjustment = this._getHeight(); + let scrollAdjustment = this._getHeight(); + + // if the editor has top padding, factor that into the zone height + scrollAdjustment -= this._codeEditor.getOption(EditorOption.padding).top; + if (scrollAdjustment <= 0) { + return; + } + viewZone.heightInPx = scrollAdjustment; this._viewZoneId = accessor.addZone(viewZone); @@ -627,10 +658,11 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas }); } - private _applyTheme(theme: ITheme) { + private _applyTheme(theme: IColorTheme) { let inputStyles: IFindInputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground), + inputActiveOptionForeground: theme.getColor(inputActiveOptionForeground), inputBackground: theme.getColor(inputBackground), inputForeground: theme.getColor(inputForeground), inputBorder: theme.getColor(inputBorder), @@ -663,14 +695,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (editorContentWidth <= 0) { // for example, diff view original editor - dom.addClass(this._domNode, 'hiddenEditor'); + this._domNode.classList.add('hiddenEditor'); return; - } else if (dom.hasClass(this._domNode, 'hiddenEditor')) { - dom.removeClass(this._domNode, 'hiddenEditor'); + } else if (this._domNode.classList.contains('hiddenEditor')) { + this._domNode.classList.remove('hiddenEditor'); } const editorWidth = layoutInfo.width; - const minimapWidth = layoutInfo.minimapWidth; + const minimapWidth = layoutInfo.minimap.minimapWidth; let collapsedFindWidget = false; let reducedFindWidget = false; let narrowFindWidget = false; @@ -695,9 +727,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth + 50) { collapsedFindWidget = true; } - dom.toggleClass(this._domNode, 'collapsed-find-widget', collapsedFindWidget); - dom.toggleClass(this._domNode, 'narrow-find-widget', narrowFindWidget); - dom.toggleClass(this._domNode, 'reduced-find-widget', reducedFindWidget); + this._domNode.classList.toggle('collapsed-find-widget', collapsedFindWidget); + this._domNode.classList.toggle('narrow-find-widget', narrowFindWidget); + this._domNode.classList.toggle('reduced-find-widget', reducedFindWidget); if (!narrowFindWidget && !collapsedFindWidget) { // the minimal left offset of findwidget is 15px. @@ -772,16 +804,26 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } if (this._toggleSelectionFind.checked) { - let selection = this._codeEditor.getSelection(); - if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { - selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1)); - } - const currentMatch = this._state.currentMatch; - if (selection.startLineNumber !== selection.endLineNumber) { - if (!Range.equalsRange(selection, currentMatch)) { - // Reseed find scope - this._state.change({ searchScope: selection }, true); + let selections = this._codeEditor.getSelections(); + + selections.map(selection => { + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition( + selection.endLineNumber - 1, + this._codeEditor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1) + ); } + const currentMatch = this._state.currentMatch; + if (selection.startLineNumber !== selection.endLineNumber) { + if (!Range.equalsRange(selection, currentMatch)) { + return selection; + } + } + return null; + }).filter(element => !!element); + + if (selections.length) { + this._state.change({ searchScope: selections as Range[] }, true); } } } @@ -835,7 +877,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas ); this._ctrlEnterReplaceAllWarningPrompted = true; - this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL); + this._storageService.store2(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL, StorageTarget.USER); } @@ -872,16 +914,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } // ----- sash - public getHorizontalSashTop(_sash: Sash): number { + public getVerticalSashLeft(_sash: Sash): number { return 0; } - public getHorizontalSashLeft?(_sash: Sash): number { - return 0; - } - public getHorizontalSashWidth?(_sash: Sash): number { - return 500; - } - // ----- initialization private _keybindingLabelFor(actionId: string): string { @@ -908,7 +943,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return null; } try { - new RegExp(value); + // use `g` and `u` which are also used by the TextModel search + new RegExp(value, 'gu'); return null; } catch (e) { return { content: e.message }; @@ -967,7 +1003,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // Previous button this._prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), - className: 'codicon codicon-arrow-up', + className: findPreviousMatchIcon.classNames, onTrigger: () => { this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError); } @@ -976,7 +1012,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // Next button this._nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), - className: 'codicon codicon-arrow-down', + className: findNextMatchIcon.classNames, onTrigger: () => { this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError); } @@ -994,7 +1030,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // Toggle selection button this._toggleSelectionFind = this._register(new Checkbox({ - actionClassName: 'codicon codicon-selection', + icon: findSelectionIcon, title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand), isChecked: false })); @@ -1002,12 +1038,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._register(this._toggleSelectionFind.onChange(() => { if (this._toggleSelectionFind.checked) { if (this._codeEditor.hasModel()) { - let selection = this._codeEditor.getSelection(); - if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { - selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1)); - } - if (!selection.isEmpty()) { - this._state.change({ searchScope: selection }, true); + let selections = this._codeEditor.getSelections(); + selections.map(selection => { + if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) { + selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1)); + } + if (!selection.isEmpty()) { + return selection; + } + return null; + }).filter(element => !!element); + + if (selections.length) { + this._state.change({ searchScope: selections as Range[] }, true); } } } else { @@ -1020,7 +1063,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // Close button this._closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand), - className: 'codicon codicon-close', + className: findCloseIcon.classNames, onTrigger: () => { this._state.change({ isRevealed: false, searchScope: null }, false); }, @@ -1044,6 +1087,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, { label: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, + appendPreserveCaseLabel: this._keybindingLabelFor(FIND_IDS.TogglePreserveCaseCommand), history: [], flexibleHeight, flexibleWidth, @@ -1083,7 +1127,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // Replace one button this._replaceBtn = this._register(new SimpleButton({ label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction), - className: 'codicon codicon-replace', + className: findReplaceIcon.classNames, onTrigger: () => { this._controller.replace(); }, @@ -1098,7 +1142,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // Replace all button this._replaceAllBtn = this._register(new SimpleButton({ label: NLS_REPLACE_ALL_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceAllAction), - className: 'codicon codicon-replace-all', + className: findReplaceAllIcon.classNames, onTrigger: () => { this._controller.replaceAll(); } @@ -1128,8 +1172,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._showViewZone(); } })); - this._toggleReplaceBtn.toggleClass('codicon-chevron-down', this._isReplaceVisible); - this._toggleReplaceBtn.toggleClass('codicon-chevron-right', !this._isReplaceVisible); this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); // Widget @@ -1143,7 +1185,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._domNode.appendChild(findPart); this._domNode.appendChild(replacePart); - this._resizeSash = new Sash(this._domNode, this, { orientation: Orientation.VERTICAL }); + this._resizeSash = new Sash(this._domNode, this, { orientation: Orientation.VERTICAL, size: 2 }); this._resized = false; let originalWidth = FIND_WIDGET_INITIAL_WIDTH; @@ -1188,7 +1230,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // 1. never resized before, double click should maximizes it // 2. users resized it already but its width is the same as default const layoutInfo = this._codeEditor.getLayoutInfo(); - width = layoutInfo.width - 28 - layoutInfo.minimapWidth - 15; + width = layoutInfo.width - 28 - layoutInfo.minimap.minimapWidth - 15; this._resized = true; } else { /** @@ -1265,17 +1307,20 @@ export class SimpleButton extends Widget { } public setEnabled(enabled: boolean): void { - dom.toggleClass(this._domNode, 'disabled', !enabled); + this._domNode.classList.toggle('disabled', !enabled); this._domNode.setAttribute('aria-disabled', String(!enabled)); this._domNode.tabIndex = enabled ? 0 : -1; } public setExpanded(expanded: boolean): void { this._domNode.setAttribute('aria-expanded', String(!!expanded)); - } - - public toggleClass(className: string, shouldHaveIt: boolean): void { - dom.toggleClass(this._domNode, className, shouldHaveIt); + if (expanded) { + this._domNode.classList.remove(...findCollapsedIcon.classNames.split(' ')); + this._domNode.classList.add(...findExpandedIcon.classNames.split(' ')); + } else { + this._domNode.classList.remove(...findExpandedIcon.classNames.split(' ')); + this._domNode.classList.add(...findCollapsedIcon.classNames.split(' ')); + } } } @@ -1332,11 +1377,11 @@ registerThemingParticipant((theme, collector) => { const resizeBorderBackground = theme.getColor(editorWidgetResizeBorder); if (resizeBorderBackground) { - collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${resizeBorderBackground}; width: 3px !important; margin-left: -4px;}`); + collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${resizeBorderBackground}; }`); } else { const border = theme.getColor(editorWidgetBorder); if (border) { - collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`); + collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; }`); } } diff --git a/src/vs/editor/contrib/find/images/chevron-next-dark.svg b/src/vs/editor/contrib/find/images/chevron-next-dark.svg deleted file mode 100644 index dbe70d742de..00000000000 --- a/src/vs/editor/contrib/find/images/chevron-next-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/chevron-next-light.svg b/src/vs/editor/contrib/find/images/chevron-next-light.svg deleted file mode 100644 index ec824f41cc0..00000000000 --- a/src/vs/editor/contrib/find/images/chevron-next-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/chevron-previous-dark.svg b/src/vs/editor/contrib/find/images/chevron-previous-dark.svg deleted file mode 100644 index 5db4f79da85..00000000000 --- a/src/vs/editor/contrib/find/images/chevron-previous-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/chevron-previous-light.svg b/src/vs/editor/contrib/find/images/chevron-previous-light.svg deleted file mode 100644 index aac3a5020cd..00000000000 --- a/src/vs/editor/contrib/find/images/chevron-previous-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/close-dark.svg b/src/vs/editor/contrib/find/images/close-dark.svg deleted file mode 100644 index 75644595d19..00000000000 --- a/src/vs/editor/contrib/find/images/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/close-light.svg b/src/vs/editor/contrib/find/images/close-light.svg deleted file mode 100644 index cf5f28ca35c..00000000000 --- a/src/vs/editor/contrib/find/images/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/find-selection-dark.svg b/src/vs/editor/contrib/find/images/find-selection-dark.svg deleted file mode 100644 index 6fc07d81a55..00000000000 --- a/src/vs/editor/contrib/find/images/find-selection-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/find-selection-light.svg b/src/vs/editor/contrib/find/images/find-selection-light.svg deleted file mode 100644 index 3608b15d29e..00000000000 --- a/src/vs/editor/contrib/find/images/find-selection-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/replace-all-dark.svg b/src/vs/editor/contrib/find/images/replace-all-dark.svg deleted file mode 100644 index 07bd41a789f..00000000000 --- a/src/vs/editor/contrib/find/images/replace-all-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/replace-all-light.svg b/src/vs/editor/contrib/find/images/replace-all-light.svg deleted file mode 100644 index cd3974fae7e..00000000000 --- a/src/vs/editor/contrib/find/images/replace-all-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/replace-dark.svg b/src/vs/editor/contrib/find/images/replace-dark.svg deleted file mode 100644 index 5882b22c589..00000000000 --- a/src/vs/editor/contrib/find/images/replace-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/replace-light.svg b/src/vs/editor/contrib/find/images/replace-light.svg deleted file mode 100644 index 220f2aba40c..00000000000 --- a/src/vs/editor/contrib/find/images/replace-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/replacePattern.ts b/src/vs/editor/contrib/find/replacePattern.ts index 12a05d93536..e829fe6c108 100644 --- a/src/vs/editor/contrib/find/replacePattern.ts +++ b/src/vs/editor/contrib/find/replacePattern.ts @@ -20,7 +20,7 @@ class StaticValueReplacePattern { } /** - * Assigned when the replace pattern has replacemend patterns. + * Assigned when the replace pattern has replacement patterns. */ class DynamicPiecesReplacePattern { public readonly kind = ReplacePatternKind.DynamicPieces; @@ -68,7 +68,38 @@ export class ReplacePattern { } // match index ReplacePiece - result += ReplacePattern._substitute(piece.matchIndex, matches); + let match: string = ReplacePattern._substitute(piece.matchIndex, matches); + if (piece.caseOps !== null && piece.caseOps.length > 0) { + let repl: string[] = []; + let lenOps: number = piece.caseOps.length; + let opIdx: number = 0; + for (let idx: number = 0, len: number = match.length; idx < len; idx++) { + if (opIdx >= lenOps) { + repl.push(match.slice(idx)); + break; + } + switch (piece.caseOps[opIdx]) { + case 'U': + repl.push(match[idx].toUpperCase()); + break; + case 'u': + repl.push(match[idx].toUpperCase()); + opIdx++; + break; + case 'L': + repl.push(match[idx].toLowerCase()); + break; + case 'l': + repl.push(match[idx].toLowerCase()); + opIdx++; + break; + default: + repl.push(match[idx]); + } + } + match = repl.join(''); + } + result += match; } return result; @@ -102,19 +133,29 @@ export class ReplacePattern { export class ReplacePiece { public static staticValue(value: string): ReplacePiece { - return new ReplacePiece(value, -1); + return new ReplacePiece(value, -1, null); } public static matchIndex(index: number): ReplacePiece { - return new ReplacePiece(null, index); + return new ReplacePiece(null, index, null); + } + + public static caseOps(index: number, caseOps: string[]): ReplacePiece { + return new ReplacePiece(null, index, caseOps); } public readonly staticValue: string | null; public readonly matchIndex: number; + public readonly caseOps: string[] | null; - private constructor(staticValue: string | null, matchIndex: number) { + private constructor(staticValue: string | null, matchIndex: number, caseOps: string[] | null) { this.staticValue = staticValue; this.matchIndex = matchIndex; + if (!caseOps || caseOps.length === 0) { + this.caseOps = null; + } else { + this.caseOps = caseOps.slice(0); + } } } @@ -151,12 +192,12 @@ class ReplacePieceBuilder { this._currentStaticPiece += value; } - public emitMatchIndex(index: number, toCharIndex: number): void { + public emitMatchIndex(index: number, toCharIndex: number, caseOps: string[]): void { if (this._currentStaticPiece.length !== 0) { this._result[this._resultLen++] = ReplacePiece.staticValue(this._currentStaticPiece); this._currentStaticPiece = ''; } - this._result[this._resultLen++] = ReplacePiece.matchIndex(index); + this._result[this._resultLen++] = ReplacePiece.caseOps(index, caseOps); this._lastCharIndex = toCharIndex; } @@ -175,6 +216,10 @@ class ReplacePieceBuilder { * \n => inserts a LF * \t => inserts a TAB * \\ => inserts a "\". + * \u => upper-cases one character in a match. + * \U => upper-cases ALL remaining characters in a match. + * \l => lower-cases one character in a match. + * \L => lower-cases ALL remaining characters in a match. * $$ => inserts a "$". * $& and $0 => inserts the matched substring. * $n => Where n is a non-negative integer lesser than 100, inserts the nth parenthesized submatch string @@ -187,6 +232,7 @@ export function parseReplaceString(replaceString: string): ReplacePattern { return new ReplacePattern(null); } + let caseOps: string[] = []; let result = new ReplacePieceBuilder(replaceString); for (let i = 0, len = replaceString.length; i < len; i++) { @@ -221,6 +267,20 @@ export function parseReplaceString(replaceString: string): ReplacePattern { result.emitUnchanged(i - 1); result.emitStatic('\t', i + 1); break; + // Case modification of string replacements, patterned after Boost, but only applied + // to the replacement text, not subsequent content. + case CharCode.u: + // \u => upper-cases one character. + case CharCode.U: + // \U => upper-cases ALL following characters. + case CharCode.l: + // \l => lower-cases one character. + case CharCode.L: + // \L => lower-cases ALL following characters. + result.emitUnchanged(i - 1); + result.emitStatic('', i + 1); + caseOps.push(String.fromCharCode(nextChCode)); + break; } continue; @@ -248,7 +308,8 @@ export function parseReplaceString(replaceString: string): ReplacePattern { if (nextChCode === CharCode.Digit0 || nextChCode === CharCode.Ampersand) { // $& and $0 => inserts the matched substring. result.emitUnchanged(i - 1); - result.emitMatchIndex(0, i + 1); + result.emitMatchIndex(0, i + 1, caseOps); + caseOps.length = 0; continue; } @@ -268,13 +329,15 @@ export function parseReplaceString(replaceString: string): ReplacePattern { matchIndex = matchIndex * 10 + (nextNextChCode - CharCode.Digit0); result.emitUnchanged(i - 2); - result.emitMatchIndex(matchIndex, i + 1); + result.emitMatchIndex(matchIndex, i + 1, caseOps); + caseOps.length = 0; continue; } } result.emitUnchanged(i - 1); - result.emitMatchIndex(matchIndex, i + 1); + result.emitMatchIndex(matchIndex, i + 1, caseOps); + caseOps.length = 0; continue; } } diff --git a/src/vs/editor/contrib/find/test/find.test.ts b/src/vs/editor/contrib/find/test/find.test.ts index a0e8e987ef3..98f31f328cb 100644 --- a/src/vs/editor/contrib/find/test/find.test.ts +++ b/src/vs/editor/contrib/find/test/find.test.ts @@ -16,7 +16,7 @@ suite('Find', () => { withTestCodeEditor([ 'ABC DEF', '0123 456' - ], {}, (editor, cursor) => { + ], {}, (editor) => { // The cursor is at the very top, of the file, at the first ABC let searchStringAtTop = getSelectionSearchString(editor); @@ -39,7 +39,7 @@ suite('Find', () => { withTestCodeEditor([ 'ABC DEF', '0123 456' - ], {}, (editor, cursor) => { + ], {}, (editor) => { // Select A of ABC editor.setSelection(new Range(1, 1, 1, 2)); @@ -63,7 +63,7 @@ suite('Find', () => { withTestCodeEditor([ 'ABC DEF', '0123 456' - ], {}, (editor, cursor) => { + ], {}, (editor) => { // Select first line and newline editor.setSelection(new Range(1, 1, 2, 1)); diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 3065cb082f9..6a352efab67 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -12,9 +12,9 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController'; +import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction, StartFindWithSelectionAction } from 'vs/editor/contrib/find/findController'; import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; -import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { withAsyncTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -39,8 +39,8 @@ export class TestFindController extends CommonFindController { this.hasFocus = false; } - protected _start(opts: IFindStartOptions): void { - super._start(opts); + protected async _start(opts: IFindStartOptions): Promise { + await super._start(opts); if (opts.shouldFocus !== FindStartFocusAction.NoFocusChange) { this.hasFocus = true; @@ -55,7 +55,7 @@ function fromSelection(slc: Selection): number[] { return [slc.startLineNumber, slc.startColumn, slc.endLineNumber, slc.endColumn]; } -suite('FindController', () => { +suite('FindController', async () => { let queryState: { [key: string]: any; } = {}; let clipboardState = ''; let serviceCollection = new ServiceCollection(); @@ -77,19 +77,19 @@ suite('FindController', () => { }); } - /* test('stores to the global clipboard buffer on start find action', () => { - withTestCodeEditor([ + /* test('stores to the global clipboard buffer on start find action', async () => { + await withAsyncTestCodeEditor([ 'ABC', 'ABC', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; if (!platform.isMacintosh) { assert.ok(true); return; } - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let startFindAction = new StartFindAction(); // I select ABC on the first line editor.setSelection(new Selection(1, 1, 1, 4)); @@ -101,13 +101,13 @@ suite('FindController', () => { }); }); - test('reads from the global clipboard buffer on next find action if buffer exists', () => { - withTestCodeEditor([ + test('reads from the global clipboard buffer on next find action if buffer exists', async () => { + await withAsyncTestCodeEditor([ 'ABC', 'ABC', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = 'ABC'; if (!platform.isMacintosh) { @@ -115,7 +115,7 @@ suite('FindController', () => { return; } - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findState = findController.getState(); let nextMatchFindAction = new NextMatchFindAction(); @@ -128,20 +128,20 @@ suite('FindController', () => { }); }); - test('writes to the global clipboard buffer when text changes', () => { - withTestCodeEditor([ + test('writes to the global clipboard buffer when text changes', async () => { + await withAsyncTestCodeEditor([ 'ABC', 'ABC', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; if (!platform.isMacintosh) { assert.ok(true); return; } - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findState = findController.getState(); findState.change({ searchString: 'ABC' }, true); @@ -152,22 +152,22 @@ suite('FindController', () => { }); }); */ - test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => { - withTestCodeEditor([ + test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', async () => { + await withAsyncTestCodeEditor([ 'ABC', 'ABC', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; // The cursor is at the very top, of the file, at the first ABC - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findState = findController.getState(); let startFindAction = new StartFindAction(); let nextMatchFindAction = new NextMatchFindAction(); // I hit Ctrl+F to show the Find dialog - startFindAction.run(null, editor); + await startFindAction.run(null, editor); // I type ABC. findState.change({ searchString: 'A' }, true); @@ -201,7 +201,7 @@ suite('FindController', () => { assert.deepEqual(fromSelection(editor.getSelection()!), [1, 4, 1, 4]); // I hit F3 to "Find Next" to find the next occurrence of ABC, but instead it searches for XYZ. - nextMatchFindAction.run(null, editor); + await nextMatchFindAction.run(null, editor); assert.equal(findState.searchString, 'ABC'); assert.equal(findController.hasFocus, false); @@ -210,12 +210,12 @@ suite('FindController', () => { }); }); - test('issue #3090: F3 does not loop with two matches on a single line', () => { - withTestCodeEditor([ + test('issue #3090: F3 does not loop with two matches on a single line', async () => { + await withAsyncTestCodeEditor([ 'import nls = require(\'vs/nls\');' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let nextMatchFindAction = new NextMatchFindAction(); editor.setPosition({ @@ -223,63 +223,64 @@ suite('FindController', () => { column: 9 }); - nextMatchFindAction.run(null, editor); + await nextMatchFindAction.run(null, editor); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 26, 1, 29]); - nextMatchFindAction.run(null, editor); + await nextMatchFindAction.run(null, editor); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 8, 1, 11]); findController.dispose(); }); }); - test('issue #6149: Auto-escape highlighted text for search and replace regex mode', () => { - withTestCodeEditor([ + test('issue #6149: Auto-escape highlighted text for search and replace regex mode', async () => { + await withAsyncTestCodeEditor([ 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let startFindAction = new StartFindAction(); let nextMatchFindAction = new NextMatchFindAction(); editor.setSelection(new Selection(1, 9, 1, 13)); findController.toggleRegex(); - startFindAction.run(null, editor); + await startFindAction.run(null, editor); - nextMatchFindAction.run(null, editor); + await nextMatchFindAction.run(null, editor); assert.deepEqual(fromSelection(editor.getSelection()!), [2, 9, 2, 13]); - nextMatchFindAction.run(null, editor); + await nextMatchFindAction.run(null, editor); assert.deepEqual(fromSelection(editor.getSelection()!), [1, 9, 1, 13]); findController.dispose(); }); }); - test('issue #41027: Don\'t replace find input value on replace action if find input is active', () => { - withTestCodeEditor([ + test('issue #41027: Don\'t replace find input value on replace action if find input is active', async () => { + await withAsyncTestCodeEditor([ 'test', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { let testRegexString = 'tes.'; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let nextMatchFindAction = new NextMatchFindAction(); let startFindReplaceAction = new StartFindReplaceAction(); findController.toggleRegex(); findController.setSearchString(testRegexString); - findController.start({ + await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: false, - updateSearchScope: false + updateSearchScope: false, + loop: true }); - nextMatchFindAction.run(null, editor); - startFindReplaceAction.run(null, editor); + await nextMatchFindAction.run(null, editor); + await startFindReplaceAction.run(null, editor); assert.equal(findController.getState().searchString, testRegexString); @@ -287,45 +288,46 @@ suite('FindController', () => { }); }); - test('issue #9043: Clear search scope when find widget is hidden', () => { - withTestCodeEditor([ + test('issue #9043: Clear search scope when find widget is hidden', async () => { + await withAsyncTestCodeEditor([ 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - findController.start({ + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: false + updateSearchScope: false, + loop: true }); assert.equal(findController.getState().searchScope, null); findController.getState().change({ - searchScope: new Range(1, 1, 1, 5) + searchScope: [new Range(1, 1, 1, 5)] }, false); - assert.deepEqual(findController.getState().searchScope, new Range(1, 1, 1, 5)); + assert.deepEqual(findController.getState().searchScope, [new Range(1, 1, 1, 5)]); findController.closeFindWidget(); assert.equal(findController.getState().searchScope, null); }); }); - test('issue #18111: Regex replace with single space replaces with no space', () => { - withTestCodeEditor([ + test('issue #18111: Regex replace with single space replaces with no space', async () => { + await withAsyncTestCodeEditor([ 'HRESULT OnAmbientPropertyChange(DISPID dispid);' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let startFindAction = new StartFindAction(); - startFindAction.run(null, editor); + await startFindAction.run(null, editor); findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false); findController.moveToNextMatch(); @@ -342,17 +344,17 @@ suite('FindController', () => { }); }); - test('issue #24714: Regular expression with ^ in search & replace', () => { - withTestCodeEditor([ + test('issue #24714: Regular expression with ^ in search & replace', async () => { + await withAsyncTestCodeEditor([ '', 'line2', 'line3' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let startFindAction = new StartFindAction(); - startFindAction.run(null, editor); + await startFindAction.run(null, editor); findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false); findController.moveToNextMatch(); @@ -369,14 +371,14 @@ suite('FindController', () => { }); }); - test('issue #38232: Find Next Selection, regex enabled', () => { - withTestCodeEditor([ + test('issue #38232: Find Next Selection, regex enabled', async () => { + await withAsyncTestCodeEditor([ '([funny]', '', '([funny]' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let nextSelectionMatchFindAction = new NextSelectionMatchFindAction(); // toggle regex @@ -386,7 +388,7 @@ suite('FindController', () => { editor.setSelection(new Selection(1, 1, 1, 9)); // cmd+f3 - nextSelectionMatchFindAction.run(null, editor); + await nextSelectionMatchFindAction.run(null, editor); assert.deepEqual(editor.getSelections()!.map(fromSelection), [ [3, 1, 3, 9] @@ -396,19 +398,19 @@ suite('FindController', () => { }); }); - test('issue #38232: Find Next Selection, regex enabled, find widget open', () => { - withTestCodeEditor([ + test('issue #38232: Find Next Selection, regex enabled, find widget open', async () => { + await withAsyncTestCodeEditor([ '([funny]', '', '([funny]' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { clipboardState = ''; - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let startFindAction = new StartFindAction(); let nextSelectionMatchFindAction = new NextSelectionMatchFindAction(); // cmd+f - open find widget - startFindAction.run(null, editor); + await startFindAction.run(null, editor); // toggle regex findController.getState().change({ isRegex: true }, false); @@ -417,7 +419,7 @@ suite('FindController', () => { editor.setSelection(new Selection(1, 1, 1, 9)); // cmd+f3 - nextSelectionMatchFindAction.run(null, editor); + await nextSelectionMatchFindAction.run(null, editor); assert.deepEqual(editor.getSelections()!.map(fromSelection), [ [3, 1, 3, 9] @@ -426,9 +428,62 @@ suite('FindController', () => { findController.dispose(); }); }); + + test('issue #47400, CMD+E supports feeding multiple line of text into the find widget', async () => { + await withAsyncTestCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC', + 'ABC' + ], { serviceCollection: serviceCollection }, async (editor) => { + clipboardState = ''; + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let startFindAction = new StartFindAction(); + + // change selection + editor.setSelection(new Selection(1, 1, 1, 1)); + + // cmd+f - open find widget + await startFindAction.run(null, editor); + + editor.setSelection(new Selection(1, 1, 2, 4)); + let startFindWithSelectionAction = new StartFindWithSelectionAction(); + await startFindWithSelectionAction.run(null, editor); + let findState = findController.getState(); + + assert.deepEqual(findState.searchString.split(/\r\n|\r|\n/g), ['ABC', 'ABC']); + + editor.setSelection(new Selection(3, 1, 3, 1)); + await startFindWithSelectionAction.run(null, editor); + + findController.dispose(); + }); + }); + + test('issue #109756, CMD+E with empty cursor should always work', async () => { + await withAsyncTestCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC', + 'ABC' + ], { serviceCollection: serviceCollection }, async (editor) => { + clipboardState = ''; + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + editor.setSelection(new Selection(1, 2, 1, 2)); + + let startFindWithSelectionAction = new StartFindWithSelectionAction(); + startFindWithSelectionAction.run(null, editor); + + let findState = findController.getState(); + assert.deepEqual(findState.searchString, 'ABC'); + findController.dispose(); + }); + }); }); -suite('FindController query options persistence', () => { +suite('FindController query options persistence', async () => { let queryState: { [key: string]: any; } = {}; queryState['editor.isRegex'] = false; queryState['editor.matchCase'] = false; @@ -445,21 +500,21 @@ suite('FindController query options persistence', () => { remove: () => undefined } as any); - test('matchCase', () => { - withTestCodeEditor([ + test('matchCase', async () => { + await withAsyncTestCodeEditor([ 'abc', 'ABC', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false }; // The cursor is at the very top, of the file, at the first ABC - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findState = findController.getState(); let startFindAction = new StartFindAction(); // I hit Ctrl+F to show the Find dialog - startFindAction.run(null, editor); + await startFindAction.run(null, editor); // I type ABC. findState.change({ searchString: 'ABC' }, true); @@ -472,21 +527,21 @@ suite('FindController query options persistence', () => { queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; - test('wholeWord', () => { - withTestCodeEditor([ + test('wholeWord', async () => { + await withAsyncTestCodeEditor([ 'ABC', 'AB', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; // The cursor is at the very top, of the file, at the first ABC - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); let findState = findController.getState(); let startFindAction = new StartFindAction(); // I hit Ctrl+F to show the Find dialog - startFindAction.run(null, editor); + await startFindAction.run(null, editor); // I type AB. findState.change({ searchString: 'AB' }, true); @@ -497,16 +552,16 @@ suite('FindController query options persistence', () => { }); }); - test('toggling options is saved', () => { - withTestCodeEditor([ + test('toggling options is saved', async () => { + await withAsyncTestCodeEditor([ 'ABC', 'AB', 'XYZ', 'ABC' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, async (editor) => { queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; // The cursor is at the very top, of the file, at the first ABC - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); findController.toggleRegex(); assert.equal(queryState['editor.isRegex'], true); @@ -514,96 +569,106 @@ suite('FindController query options persistence', () => { }); }); - test('issue #27083: Update search scope once find widget becomes visible', () => { - withTestCodeEditor([ + test('issue #27083: Update search scope once find widget becomes visible', async () => { + await withAsyncTestCodeEditor([ 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor, cursor) => { + ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => { // clipboardState = ''; - editor.setSelection(new Range(1, 1, 2, 1)); - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - - findController.start({ + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + const findConfig: IFindStartOptions = { forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: true - }); + updateSearchScope: true, + loop: true + }; - assert.deepEqual(findController.getState().searchScope, new Selection(1, 1, 2, 1)); + editor.setSelection(new Range(1, 1, 2, 1)); + findController.start(findConfig); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1)]); + + findController.closeFindWidget(); + + editor.setSelections([new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]); + findController.start(findConfig); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]); }); }); - test('issue #58604: Do not update searchScope if it is empty', () => { - withTestCodeEditor([ + test('issue #58604: Do not update searchScope if it is empty', async () => { + await withAsyncTestCodeEditor([ 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor, cursor) => { + ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => { // clipboardState = ''; editor.setSelection(new Range(1, 2, 1, 2)); - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - findController.start({ + await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: true + updateSearchScope: true, + loop: true }); assert.deepEqual(findController.getState().searchScope, null); }); }); - test('issue #58604: Update searchScope if it is not empty', () => { - withTestCodeEditor([ + test('issue #58604: Update searchScope if it is not empty', async () => { + await withAsyncTestCodeEditor([ 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, (editor, cursor) => { + ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => { // clipboardState = ''; editor.setSelection(new Range(1, 2, 1, 3)); - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - findController.start({ + await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: true + updateSearchScope: true, + loop: true }); - assert.deepEqual(findController.getState().searchScope, new Selection(1, 2, 1, 3)); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 2, 1, 3)]); }); }); - test('issue #27083: Find in selection when multiple lines are selected', () => { - withTestCodeEditor([ + test('issue #27083: Find in selection when multiple lines are selected', async () => { + await withAsyncTestCodeEditor([ 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'multiline', globalFindClipboard: false } }, (editor, cursor) => { + ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'multiline', globalFindClipboard: false } }, async (editor) => { // clipboardState = ''; editor.setSelection(new Range(1, 6, 2, 1)); - let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - findController.start({ + await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: true + updateSearchScope: true, + loop: true }); - assert.deepEqual(findController.getState().searchScope, new Selection(1, 6, 2, 1)); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 6, 2, 1)]); }); }); }); diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index df936e17485..70c5494985a 100644 --- a/src/vs/editor/contrib/find/test/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/findModel.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -15,10 +14,13 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; suite('FindModel', () => { - function findTest(testName: string, callback: (editor: IActiveCodeEditor, cursor: Cursor) => void): void { + function findTest(testName: string, callback: (editor: IActiveCodeEditor) => void): void { test(testName, () => { const textArr = [ '// my cool header', @@ -34,7 +36,7 @@ suite('FindModel', () => { '// blablablaciao', '' ]; - withTestCodeEditor(textArr, {}, (editor, cursor) => callback(editor as unknown as IActiveCodeEditor, cursor)); + withTestCodeEditor(textArr, {}, (editor) => callback(editor as IActiveCodeEditor)); const text = textArr.join('\n'); const ptBuilder = new PieceTreeTextBufferBuilder(); @@ -44,9 +46,9 @@ suite('FindModel', () => { const factory = ptBuilder.finish(); withTestCodeEditor([], { - model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null) + model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null, new UndoRedoService(new TestDialogService(), new TestNotificationService())) }, - (editor, cursor) => callback(editor as unknown as IActiveCodeEditor, cursor) + (editor) => callback(editor as IActiveCodeEditor) ); }); } @@ -88,7 +90,7 @@ suite('FindModel', () => { assert.deepEqual(_getFindState(editor), expectedState, 'state'); } - findTest('incremental find from beginning of file', (editor, cursor) => { + findTest('incremental find from beginning of file', (editor) => { editor.setPosition({ lineNumber: 1, column: 1 }); let findState = new FindReplaceState(); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -208,7 +210,7 @@ suite('FindModel', () => { ); // simulate adding a search scope - findState.change({ searchScope: new Range(8, 1, 10, 1) }, true); + findState.change({ searchScope: [new Range(8, 1, 10, 1)] }, true); assertFindState( editor, [8, 14, 8, 19], @@ -238,7 +240,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model removes its decorations', (editor, cursor) => { + findTest('find model removes its decorations', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -268,7 +270,7 @@ suite('FindModel', () => { ); }); - findTest('find model updates state matchesCount', (editor, cursor) => { + findTest('find model updates state matchesCount', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -300,7 +302,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model reacts to position change', (editor, cursor) => { + findTest('find model reacts to position change', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -353,7 +355,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model next', (editor, cursor) => { + findTest('find model next', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -439,9 +441,9 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model next stays in scope', (editor, cursor) => { + findTest('find model next stays in scope', (editor) => { let findState = new FindReplaceState(); - findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 9, 1)] }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); assertFindState( @@ -491,7 +493,132 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model prev', (editor, cursor) => { + findTest('multi-selection find model next stays in scope (overlap)', (editor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 8, 2), new Range(8, 1, 9, 1)] }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [8, 14, 8, 19], + [8, 14, 8, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [7, 14, 7, 19], + [8, 14, 8, 19] + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('multi-selection find model next stays in scope', (editor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', matchCase: true, wholeWord: false, searchScope: [new Range(6, 1, 7, 38), new Range(9, 3, 9, 38)] }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assertFindState( + editor, + [1, 1, 1, 1], + null, + [ + [6, 14, 6, 19], + // `matchCase: false` would + // find this match as well: + // [6, 27, 6, 32], + [7, 14, 7, 19], + // `wholeWord: true` would + // exclude this match: + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [7, 14, 7, 19], + [7, 14, 7, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [9, 14, 9, 19], + [9, 14, 9, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.moveToNextMatch(); + assertFindState( + editor, + [6, 14, 6, 19], + [6, 14, 6, 19], + [ + [6, 14, 6, 19], + [7, 14, 7, 19], + [9, 14, 9, 19], + ] + ); + + findModel.dispose(); + findState.dispose(); + }); + + findTest('find model prev', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -577,9 +704,9 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model prev stays in scope', (editor, cursor) => { + findTest('find model prev stays in scope', (editor) => { let findState = new FindReplaceState(); - findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 9, 1)] }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); assertFindState( @@ -629,7 +756,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model next/prev with no matches', (editor, cursor) => { + findTest('find model next/prev with no matches', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'helloo', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -661,7 +788,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model next/prev respects cursor position', (editor, cursor) => { + findTest('find model next/prev respects cursor position', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -710,7 +837,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find ^', (editor, cursor) => { + findTest('find ^', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '^', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -781,7 +908,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find $', (editor, cursor) => { + findTest('find $', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '$', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -873,7 +1000,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find next ^$', (editor, cursor) => { + findTest('find next ^$', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '^$', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -925,7 +1052,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find .*', (editor, cursor) => { + findTest('find .*', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '.*', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -954,7 +1081,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find next ^.*$', (editor, cursor) => { + findTest('find next ^.*$', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '^.*$', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1025,7 +1152,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find prev ^.*$', (editor, cursor) => { + findTest('find prev ^.*$', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '^.*$', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1096,7 +1223,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find prev ^$', (editor, cursor) => { + findTest('find prev ^$', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: '^$', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1148,7 +1275,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replace hello', (editor, cursor) => { + findTest('replace hello', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1244,7 +1371,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replace bla', (editor, cursor) => { + findTest('replace bla', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'bla', replaceString: 'ciao' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1309,7 +1436,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll hello', (editor, cursor) => { + findTest('replaceAll hello', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1357,7 +1484,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll two spaces with one space', (editor, cursor) => { + findTest('replaceAll two spaces with one space', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: ' ', replaceString: ' ' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1399,7 +1526,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll bla', (editor, cursor) => { + findTest('replaceAll bla', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'bla', replaceString: 'ciao' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1428,7 +1555,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll bla with \\t\\n', (editor, cursor) => { + findTest('replaceAll bla with \\t\\n', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'bla', replaceString: '<\\n\\t>', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1460,7 +1587,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #3516: "replace all" moves page/cursor/focus/scroll to the place of the last replacement', (editor, cursor) => { + findTest('issue #3516: "replace all" moves page/cursor/focus/scroll to the place of the last replacement', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'include', replaceString: 'bar' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1490,7 +1617,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('listens to model content changes', (editor, cursor) => { + findTest('listens to model content changes', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1519,7 +1646,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('selectAllMatches', (editor, cursor) => { + findTest('selectAllMatches', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1561,7 +1688,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #14143 selectAllMatches should maintain primary cursor if feasible', (editor, cursor) => { + findTest('issue #14143 selectAllMatches should maintain primary cursor if feasible', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: 'hi', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1607,7 +1734,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #1914: NPE when there is only one find match', (editor, cursor) => { + findTest('issue #1914: NPE when there is only one find match', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'cool.h' }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1645,7 +1772,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replace when search string has look ahed regex', (editor, cursor) => { + findTest('replace when search string has look ahed regex', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello(?=\\sworld)', replaceString: 'hi', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1711,7 +1838,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replace when search string has look ahed regex and cursor is at the last find match', (editor, cursor) => { + findTest('replace when search string has look ahed regex and cursor is at the last find match', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello(?=\\sworld)', replaceString: 'hi', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1782,7 +1909,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll when search string has look ahed regex', (editor, cursor) => { + findTest('replaceAll when search string has look ahed regex', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello(?=\\sworld)', replaceString: 'hi', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1815,7 +1942,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replace when search string has look ahed regex and replace string has capturing groups', (editor, cursor) => { + findTest('replace when search string has look ahed regex and replace string has capturing groups', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hel(lo)(?=\\sworld)', replaceString: 'hi$1', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1881,7 +2008,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll when search string has look ahed regex and replace string has capturing groups', (editor, cursor) => { + findTest('replaceAll when search string has look ahed regex and replace string has capturing groups', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'wo(rl)d(?=.*;$)', replaceString: 'gi$1', isRegex: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1916,7 +2043,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll when search string is multiline and has look ahed regex and replace string has capturing groups', (editor, cursor) => { + findTest('replaceAll when search string is multiline and has look ahed regex and replace string has capturing groups', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'wo(rl)d(.*;\\n)(?=.*hello)', replaceString: 'gi$1$2', isRegex: true, matchCase: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1947,7 +2074,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('replaceAll preserving case', (editor, cursor) => { + findTest('replaceAll preserving case', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: 'goodbye', isRegex: false, matchCase: false, preserveCase: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -1983,7 +2110,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #18711 replaceAll with empty string', (editor, cursor) => { + findTest('issue #18711 replaceAll with empty string', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', replaceString: '', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -2015,7 +2142,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #32522 replaceAll with ^ on more than 1000 matches', (editor, cursor) => { + findTest('issue #32522 replaceAll with ^ on more than 1000 matches', (editor) => { let initialText = ''; for (let i = 0; i < 1100; i++) { initialText += 'line' + i + '\n'; @@ -2038,7 +2165,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #19740 Find and replace capture group/backreference inserts `undefined` instead of empty string', (editor, cursor) => { + findTest('issue #19740 Find and replace capture group/backreference inserts `undefined` instead of empty string', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello(z)?', replaceString: 'hi$1', isRegex: true, matchCase: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -2069,9 +2196,9 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #27083. search scope works even if it is a single line', (editor, cursor) => { + findTest('issue #27083. search scope works even if it is a single line', (editor) => { let findState = new FindReplaceState(); - findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 8, 1) }, false); + findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 8, 1)] }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); assertFindState( @@ -2086,4 +2213,165 @@ suite('FindModel', () => { findModel.dispose(); findState.dispose(); }); + + findTest('issue #3516: Control behavior of "Next" operations (not looping back to beginning)', (editor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello', loop: false }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assert.equal(findState.matchesCount, 5); + + // Test next operations + assert.equal(findState.matchesPosition, 0); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), false); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 2); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 3); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 4); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 5); + assert.equal(findState.canNavigateForward(), false); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 5); + assert.equal(findState.canNavigateForward(), false); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 5); + assert.equal(findState.canNavigateForward(), false); + assert.equal(findState.canNavigateBack(), true); + + // Test previous operations + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 4); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 3); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 2); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), false); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), false); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), false); + + }); + + findTest('issue #3516: Control behavior of "Next" operations (looping back to beginning)', (editor) => { + let findState = new FindReplaceState(); + findState.change({ searchString: 'hello' }, false); + let findModel = new FindModelBoundToEditorModel(editor, findState); + + assert.equal(findState.matchesCount, 5); + + // Test next operations + assert.equal(findState.matchesPosition, 0); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 2); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 3); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 4); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 5); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToNextMatch(); + assert.equal(findState.matchesPosition, 2); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + // Test previous operations + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 5); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 4); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 3); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 2); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + findModel.moveToPrevMatch(); + assert.equal(findState.matchesPosition, 1); + assert.equal(findState.canNavigateForward(), true); + assert.equal(findState.canNavigateBack(), true); + + }); + }); diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index 5a110c8160a..907292fd7ad 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -69,6 +69,38 @@ suite('Replace Pattern test', () => { testParse('hello$\'', [ReplacePiece.staticValue('hello$\'')]); }); + test('parse replace string with case modifiers', () => { + let testParse = (input: string, expectedPieces: ReplacePiece[]) => { + let actual = parseReplaceString(input); + let expected = new ReplacePattern(expectedPieces); + assert.deepEqual(actual, expected, 'Parsing ' + input); + }; + function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void { + let replacePattern = parseReplaceString(replaceString); + let m = search.exec(target); + let actual = replacePattern.buildReplaceString(m); + + assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`); + } + + // \U, \u => uppercase \L, \l => lowercase \E => cancel + + testParse('hello\\U$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['U'])]); + assertReplace('func privateFunc(', /func (\w+)\(/, 'func \\U$1(', 'func PRIVATEFUNC('); + + testParse('hello\\u$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['u'])]); + assertReplace('func privateFunc(', /func (\w+)\(/, 'func \\u$1(', 'func PrivateFunc('); + + testParse('hello\\L$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['L'])]); + assertReplace('func privateFunc(', /func (\w+)\(/, 'func \\L$1(', 'func privatefunc('); + + testParse('hello\\l$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['l'])]); + assertReplace('func PrivateFunc(', /func (\w+)\(/, 'func \\l$1(', 'func privateFunc('); + + testParse('hello$1\\u\\u\\U$4goodbye', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1), ReplacePiece.caseOps(4, ['u', 'u', 'U']), ReplacePiece.staticValue('goodbye')]); + assertReplace('hellogooDbye', /hello(\w+)/, 'hello\\u\\u\\l\\l\\U$1', 'helloGOodBYE'); + }); + test('replace has JavaScript semantics', () => { let testJSReplaceSemantics = (target: string, search: RegExp, replaceString: string, expected: string) => { let replacePattern = parseReplaceString(replaceString); diff --git a/src/vs/editor/contrib/folding/folding.css b/src/vs/editor/contrib/folding/folding.css index d19d4b73531..a68daff8155 100644 --- a/src/vs/editor/contrib/folding/folding.css +++ b/src/vs/editor/contrib/folding/folding.css @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .margin-view-overlays .codicon-chevron-right, -.monaco-editor .margin-view-overlays .codicon-chevron-down { +.monaco-editor .margin-view-overlays .codicon-folding-expanded, +.monaco-editor .margin-view-overlays .codicon-folding-collapsed { cursor: pointer; opacity: 0; transition: opacity 0.5s; @@ -16,7 +16,7 @@ } .monaco-editor .margin-view-overlays:hover .codicon, -.monaco-editor .margin-view-overlays .codicon.codicon-chevron-right, +.monaco-editor .margin-view-overlays .codicon.codicon-folding-collapsed, .monaco-editor .margin-view-overlays .codicon.alwaysShowFoldIcons { opacity: 1; } diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index b236c12ab2a..0bfa5f68166 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -15,11 +15,11 @@ import { ITextModel } from 'vs/editor/common/model'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, toggleCollapseState, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel'; -import { FoldingDecorationProvider } from './foldingDecorations'; +import { FoldingDecorationProvider, foldingCollapsedIcon, foldingExpandedIcon } from './foldingDecorations'; import { FoldingRegions, FoldingRegion } from './foldingRanges'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; +import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel'; import { IRange } from 'vs/editor/common/core/range'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; @@ -33,7 +33,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { onUnexpectedError } from 'vs/base/common/errors'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, editorSelectionBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorSelectionBackground, transparent, iconForeground } from 'vs/platform/theme/common/colorRegistry'; const CONTEXT_FOLDING_ENABLED = new RawContextKey('foldingEnabled', false); @@ -62,6 +62,8 @@ export class FoldingController extends Disposable implements IEditorContribution private readonly editor: ICodeEditor; private _isEnabled: boolean; private _useFoldingProviders: boolean; + private _unfoldOnClickAfterEndOfLine: boolean; + private _restoringViewState: boolean; private readonly foldingDecorationProvider: FoldingDecorationProvider; @@ -91,6 +93,8 @@ export class FoldingController extends Disposable implements IEditorContribution const options = this.editor.getOptions(); this._isEnabled = options.get(EditorOption.folding); this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; + this._unfoldOnClickAfterEndOfLine = options.get(EditorOption.unfoldOnClickAfterEndOfLine); + this._restoringViewState = false; this.foldingModel = null; this.hiddenRangeModel = null; @@ -112,8 +116,7 @@ export class FoldingController extends Disposable implements IEditorContribution this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => { if (e.hasChanged(EditorOption.folding)) { - const options = this.editor.getOptions(); - this._isEnabled = options.get(EditorOption.folding); + this._isEnabled = this.editor.getOptions().get(EditorOption.folding); this.foldingEnabled.set(this._isEnabled); this.onModelChanged(); } @@ -124,10 +127,12 @@ export class FoldingController extends Disposable implements IEditorContribution this.onModelContentChanged(); } if (e.hasChanged(EditorOption.foldingStrategy)) { - const options = this.editor.getOptions(); - this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; + this._useFoldingProviders = this.editor.getOptions().get(EditorOption.foldingStrategy) !== 'indentation'; this.onFoldingStrategyChanged(); } + if (e.hasChanged(EditorOption.unfoldOnClickAfterEndOfLine)) { + this._unfoldOnClickAfterEndOfLine = this.editor.getOptions().get(EditorOption.unfoldOnClickAfterEndOfLine); + } })); this.onModelChanged(); } @@ -172,7 +177,12 @@ export class FoldingController extends Disposable implements IEditorContribution if (foldingModel) { foldingModel.then(foldingModel => { if (foldingModel) { - foldingModel.applyMemento(collapsedRegions); + this._restoringViewState = true; + try { + foldingModel.applyMemento(collapsedRegions); + } finally { + this._restoringViewState = false; + } } }).then(undefined, onUnexpectedError); } @@ -254,7 +264,7 @@ export class FoldingController extends Disposable implements IEditorContribution }, 30000); return rangeProvider; // keep memento in case there are still no foldingProviders on the next request. } else if (foldingProviders.length > 0) { - this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders); + this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.onModelContentChanged()); } } this.foldingStateMemento = null; @@ -294,7 +304,7 @@ export class FoldingController extends Disposable implements IEditorContribution } private onHiddenRangesChanges(hiddenRanges: IRange[]) { - if (this.hiddenRangeModel && hiddenRanges.length) { + if (this.hiddenRangeModel && hiddenRanges.length && !this._restoringViewState) { let selections = this.editor.getSelections(); if (selections) { if (this.hiddenRangeModel.adjustSelections(selections)) { @@ -364,6 +374,15 @@ export class FoldingController extends Disposable implements IEditorContribution iconClicked = true; break; + case MouseTargetType.CONTENT_EMPTY: { + if (this._unfoldOnClickAfterEndOfLine && this.hiddenRangeModel.hasRanges()) { + const data = e.target.detail as IEmptyContentData; + if (!data.isAfterLines) { + break; + } + } + return; + } case MouseTargetType.CONTENT_TEXT: { if (this.hiddenRangeModel.hasRanges()) { let model = this.editor.getModel(); @@ -885,11 +904,22 @@ for (let i = 1; i <= 7; i++) { ); } -export const foldBackgroundBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hc: null }, nls.localize('editorSelectionBackground', "Color of the editor selection.")); +export const foldBackgroundBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hc: null }, nls.localize('foldBackgroundBackground', "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations."), true); +export const editorFoldForeground = registerColor('editorGutter.foldingControlForeground', { dark: iconForeground, light: iconForeground, hc: iconForeground }, nls.localize('editorGutter.foldingControlForeground', 'Color of the folding control in the editor gutter.')); registerThemingParticipant((theme, collector) => { const foldBackground = theme.getColor(foldBackgroundBackground); if (foldBackground) { collector.addRule(`.monaco-editor .folded-background { background-color: ${foldBackground}; }`); } + + const editorFoldColor = theme.getColor(editorFoldForeground); + if (editorFoldColor) { + collector.addRule(` + .monaco-editor .cldr${foldingExpandedIcon.cssSelector}, + .monaco-editor .cldr${foldingCollapsedIcon.cssSelector} { + color: ${editorFoldColor} !important; + } + `); + } }); diff --git a/src/vs/editor/contrib/folding/foldingDecorations.ts b/src/vs/editor/contrib/folding/foldingDecorations.ts index 0ec51779f72..c34e2c2121c 100644 --- a/src/vs/editor/contrib/folding/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/foldingDecorations.ts @@ -7,13 +7,18 @@ import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationsChangeA import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IDecorationProvider } from 'vs/editor/contrib/folding/foldingModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + +export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown); +export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight); export class FoldingDecorationProvider implements IDecorationProvider { private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, afterContentClassName: 'inline-folded', - linesDecorationsClassName: 'codicon codicon-chevron-right' + isWholeLine: true, + firstLineDecorationClassName: foldingCollapsedIcon.classNames }); private static readonly COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION = ModelDecorationOptions.register({ @@ -21,17 +26,23 @@ export class FoldingDecorationProvider implements IDecorationProvider { afterContentClassName: 'inline-folded', className: 'folded-background', isWholeLine: true, - linesDecorationsClassName: 'codicon codicon-chevron-right' + firstLineDecorationClassName: foldingCollapsedIcon.classNames }); private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - linesDecorationsClassName: 'codicon codicon-chevron-down' + isWholeLine: true, + firstLineDecorationClassName: foldingExpandedIcon.classNames }); private static readonly EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - linesDecorationsClassName: 'codicon codicon-chevron-down alwaysShowFoldIcons' + isWholeLine: true, + firstLineDecorationClassName: 'alwaysShowFoldIcons ' + foldingExpandedIcon.classNames + }); + + private static readonly HIDDEN_RANGE_DECORATION = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }); public autoHideFoldingControls: boolean = true; @@ -41,7 +52,10 @@ export class FoldingDecorationProvider implements IDecorationProvider { constructor(private readonly editor: ICodeEditor) { } - getDecorationOption(isCollapsed: boolean): ModelDecorationOptions { + getDecorationOption(isCollapsed: boolean, isHidden: boolean): ModelDecorationOptions { + if (isHidden) { + return FoldingDecorationProvider.HIDDEN_RANGE_DECORATION; + } if (isCollapsed) { return this.showFoldingHighlights ? FoldingDecorationProvider.COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION : FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION; } else if (this.autoHideFoldingControls) { diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index 917217b929b..570732ff878 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { FoldingRegions, ILineRange, FoldingRegion } from './foldingRanges'; export interface IDecorationProvider { - getDecorationOption(isCollapsed: boolean): IModelDecorationOptions; + getDecorationOption(isCollapsed: boolean, isHidden: boolean): IModelDecorationOptions; deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[]; changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null; } @@ -34,6 +34,7 @@ export class FoldingModel { public get regions(): FoldingRegions { return this._regions; } public get textModel() { return this._textModel; } public get isInitialized() { return this._isInitialized; } + public get decorationProvider() { return this._decorationProvider; } constructor(textModel: ITextModel, decorationProvider: IDecorationProvider) { this._textModel = textModel; @@ -43,24 +44,47 @@ export class FoldingModel { this._isInitialized = false; } - public toggleCollapseState(regions: FoldingRegion[]) { - if (!regions.length) { + public toggleCollapseState(toggledRegions: FoldingRegion[]) { + if (!toggledRegions.length) { return; } - let processed: { [key: string]: boolean | undefined } = {}; + toggledRegions = toggledRegions.sort((r1, r2) => r1.regionIndex - r2.regionIndex); + + const processed: { [key: string]: boolean | undefined } = {}; this._decorationProvider.changeDecorations(accessor => { - for (let region of regions) { + let k = 0; // index from [0 ... this.regions.length] + let dirtyRegionEndLine = -1; // end of the range where decorations need to be updated + let lastHiddenLine = -1; // the end of the last hidden lines + const updateDecorationsUntil = (index: number) => { + while (k < index) { + const endLineNumber = this._regions.getEndLineNumber(k); + const isCollapsed = this._regions.isCollapsed(k); + if (endLineNumber <= dirtyRegionEndLine) { + accessor.changeDecorationOptions(this._editorDecorationIds[k], this._decorationProvider.getDecorationOption(isCollapsed, endLineNumber <= lastHiddenLine)); + } + if (isCollapsed && endLineNumber > lastHiddenLine) { + lastHiddenLine = endLineNumber; + } + k++; + } + }; + for (let region of toggledRegions) { let index = region.regionIndex; let editorDecorationId = this._editorDecorationIds[index]; if (editorDecorationId && !processed[editorDecorationId]) { processed[editorDecorationId] = true; + + updateDecorationsUntil(index); // update all decorations up to current index using the old dirtyRegionEndLine + let newCollapseState = !this._regions.isCollapsed(index); this._regions.setCollapsed(index, newCollapseState); - accessor.changeDecorationOptions(editorDecorationId, this._decorationProvider.getDecorationOption(newCollapseState)); + + dirtyRegionEndLine = Math.max(dirtyRegionEndLine, this._regions.getEndLineNumber(index)); } } + updateDecorationsUntil(this._regions.length); }); - this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions }); + this._updateEventEmitter.fire({ model: this, collapseStateChanged: toggledRegions }); } public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void { @@ -75,20 +99,27 @@ export class FoldingModel { return false; }; + let lastHiddenLine = -1; + let initRange = (index: number, isCollapsed: boolean) => { - let startLineNumber = newRegions.getStartLineNumber(index); - if (isCollapsed && isBlocked(startLineNumber, newRegions.getEndLineNumber(index))) { + const startLineNumber = newRegions.getStartLineNumber(index); + const endLineNumber = newRegions.getEndLineNumber(index); + if (isCollapsed && isBlocked(startLineNumber, endLineNumber)) { isCollapsed = false; } newRegions.setCollapsed(index, isCollapsed); - let maxColumn = this._textModel.getLineMaxColumn(startLineNumber); - let decorationRange = { + + const maxColumn = this._textModel.getLineMaxColumn(startLineNumber); + const decorationRange = { startLineNumber: startLineNumber, - startColumn: maxColumn, + startColumn: Math.max(maxColumn - 1, 1), // make it length == 1 to detect deletions endLineNumber: startLineNumber, endColumn: maxColumn }; - newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(isCollapsed) }); + newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(isCollapsed, endLineNumber <= lastHiddenLine) }); + if (isCollapsed && endLineNumber > lastHiddenLine) { + lastHiddenLine = endLineNumber; + } }; let i = 0; let nextCollapsed = () => { @@ -109,7 +140,7 @@ export class FoldingModel { let decRange = this._textModel.getDecorationRange(this._editorDecorationIds[collapsedIndex]); if (decRange) { let collapsedStartLineNumber = decRange.startLineNumber; - if (this._textModel.getLineMaxColumn(collapsedStartLineNumber) === decRange.startColumn) { // test that the decoration is still at the end otherwise it got deleted + if (decRange.startColumn === Math.max(decRange.endColumn - 1, 1) && this._textModel.getLineMaxColumn(collapsedStartLineNumber) === decRange.endColumn) { // test that the decoration is still covering the full line else it got deleted while (k < newRegions.length) { let startLineNumber = newRegions.getStartLineNumber(k); if (collapsedStartLineNumber >= startLineNumber) { @@ -318,7 +349,7 @@ export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse: export function setCollapseStateUp(foldingModel: FoldingModel, doCollapse: boolean, lineNumbers: number[]): void { let toToggle: FoldingRegion[] = []; for (let lineNumber of lineNumbers) { - let regions = foldingModel.getAllRegionsAtLine(lineNumber, (region, ) => region.isCollapsed !== doCollapse); + let regions = foldingModel.getAllRegionsAtLine(lineNumber, (region,) => region.isCollapsed !== doCollapse); if (regions.length > 0) { toToggle.push(regions[0]); } diff --git a/src/vs/editor/contrib/folding/foldingRanges.ts b/src/vs/editor/contrib/folding/foldingRanges.ts index 1c2e0d1fc74..bf2a6730a0c 100644 --- a/src/vs/editor/contrib/folding/foldingRanges.ts +++ b/src/vs/editor/contrib/folding/foldingRanges.ts @@ -151,6 +151,26 @@ export class FoldingRegions { } return res.join(', '); } + + public equals(b: FoldingRegions) { + if (this.length !== b.length) { + return false; + } + + for (let i = 0; i < this.length; i++) { + if (this.getStartLineNumber(i) !== b.getStartLineNumber(i)) { + return false; + } + if (this.getEndLineNumber(i) !== b.getEndLineNumber(i)) { + return false; + } + if (this.getType(i) !== b.getType(i)) { + return false; + } + } + + return true; + } } export class FoldingRegion { diff --git a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts index 4e66801be36..898694bb2f8 100644 --- a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts +++ b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts @@ -9,6 +9,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { RangeProvider } from './folding'; import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const MAX_FOLDING_REGIONS = 5000; @@ -25,7 +26,17 @@ export class SyntaxRangeProvider implements RangeProvider { readonly id = ID_SYNTAX_PROVIDER; - constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) { + readonly disposables: DisposableStore | undefined; + + constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], handleFoldingRangesChange: () => void, private limit = MAX_FOLDING_REGIONS) { + for (const provider of providers) { + if (typeof provider.onDidChange === 'function') { + if (!this.disposables) { + this.disposables = new DisposableStore(); + } + this.disposables.add(provider.onDidChange(handleFoldingRangesChange)); + } + } } compute(cancellationToken: CancellationToken): Promise { @@ -39,8 +50,8 @@ export class SyntaxRangeProvider implements RangeProvider { } dispose() { + this.disposables?.dispose(); } - } function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextModel, cancellationToken: CancellationToken): Promise { diff --git a/src/vs/editor/contrib/folding/test/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/foldingModel.test.ts index 87470e12e09..a646fa78bd6 100644 --- a/src/vs/editor/contrib/folding/test/foldingModel.test.ts +++ b/src/vs/editor/contrib/folding/test/foldingModel.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel'; -import { TextModel, ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { TrackedRangeStickiness, IModelDeltaDecoration, ITextModel, IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -20,9 +21,24 @@ interface ExpectedRegion { isCollapsed: boolean; } +interface ExpectedDecoration { + line: number; + type: 'hidden' | 'collapsed' | 'expanded'; +} + export class TestDecorationProvider { - private testDecorator = ModelDecorationOptions.register({ + private static readonly collapsedDecoration = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + linesDecorationsClassName: 'folding' + }); + + private static readonly expandedDecoration = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + linesDecorationsClassName: 'folding' + }); + + private static readonly hiddenDecoration = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, linesDecorationsClassName: 'folding' }); @@ -30,8 +46,14 @@ export class TestDecorationProvider { constructor(private model: ITextModel) { } - getDecorationOption(isCollapsed: boolean): ModelDecorationOptions { - return this.testDecorator; + getDecorationOption(isCollapsed: boolean, isHidden: boolean): ModelDecorationOptions { + if (isHidden) { + return TestDecorationProvider.hiddenDecoration; + } + if (isCollapsed) { + return TestDecorationProvider.collapsedDecoration; + } + return TestDecorationProvider.expandedDecoration; } deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] { @@ -41,6 +63,21 @@ export class TestDecorationProvider { changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): (T | null) { return this.model.changeDecorations(callback); } + + getDecorations(): ExpectedDecoration[] { + const decorations = this.model.getAllDecorations(); + const res: ExpectedDecoration[] = []; + for (let decoration of decorations) { + if (decoration.options === TestDecorationProvider.hiddenDecoration) { + res.push({ line: decoration.range.startLineNumber, type: 'hidden' }); + } else if (decoration.options === TestDecorationProvider.collapsedDecoration) { + res.push({ line: decoration.range.startLineNumber, type: 'collapsed' }); + } else if (decoration.options === TestDecorationProvider.expandedDecoration) { + res.push({ line: decoration.range.startLineNumber, type: 'expanded' }); + } + } + return res; + } } suite('Folding Model', () => { @@ -48,6 +85,10 @@ suite('Folding Model', () => { return { startLineNumber, endLineNumber, isCollapsed }; } + function d(line: number, type: 'hidden' | 'collapsed' | 'expanded'): ExpectedDecoration { + return { line, type }; + } + function assertRegion(actual: FoldingRegion | null, expected: ExpectedRegion | null, message?: string) { assert.equal(!!actual, !!expected, message); if (actual && expected) { @@ -77,6 +118,11 @@ suite('Folding Model', () => { assert.deepEqual(actualRanges, expectedRegions, message); } + function assertDecorations(foldingModel: FoldingModel, expectedDecoration: ExpectedDecoration[], message?: string) { + const decorationProvider = foldingModel.decorationProvider as TestDecorationProvider; + assert.deepEqual(decorationProvider.getDecorations(), expectedDecoration, message); + } + function assertRegions(actual: FoldingRegion[], expectedRegions: ExpectedRegion[], message?: string) { assert.deepEqual(actual.map(r => ({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, isCollapsed: r.isCollapsed })), expectedRegions, message); } @@ -92,7 +138,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -131,7 +177,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -177,7 +223,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -217,7 +263,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -254,7 +300,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -295,7 +341,7 @@ suite('Folding Model', () => { /* 11*/ ' }', /* 12*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -346,7 +392,7 @@ suite('Folding Model', () => { /* 10*/ '//#endregion', /* 11*/ '']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -392,7 +438,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -448,7 +494,7 @@ suite('Folding Model', () => { /* 15*/ ' //#endregion', /* 16*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -504,7 +550,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -556,7 +602,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -603,7 +649,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -648,7 +694,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -671,4 +717,65 @@ suite('Folding Model', () => { }); + test('folding decoration', () => { + let lines = [ + /* 1*/ 'class A {', + /* 2*/ ' void foo() {', + /* 3*/ ' if (true) {', + /* 4*/ ' hoo();', + /* 5*/ ' }', + /* 6*/ ' }', + /* 7*/ '}']; + + let textModel = createTextModel(lines.join('\n')); + try { + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); + + let ranges = computeRanges(textModel, false, undefined); + foldingModel.update(ranges); + + let r1 = r(1, 6, false); + let r2 = r(2, 5, false); + let r3 = r(3, 4, false); + + assertRanges(foldingModel, [r1, r2, r3]); + assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'expanded'), d(3, 'expanded')]); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2)!]); + + assertRanges(foldingModel, [r1, r(2, 5, true), r3]); + assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]); + + foldingModel.update(ranges); + + assertRanges(foldingModel, [r1, r(2, 5, true), r3]); + assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!]); + + assertRanges(foldingModel, [r(1, 6, true), r(2, 5, true), r3]); + assertDecorations(foldingModel, [d(1, 'collapsed'), d(2, 'hidden'), d(3, 'hidden')]); + + foldingModel.update(ranges); + + assertRanges(foldingModel, [r(1, 6, true), r(2, 5, true), r3]); + assertDecorations(foldingModel, [d(1, 'collapsed'), d(2, 'hidden'), d(3, 'hidden')]); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!, foldingModel.getRegionAtLine(3)!]); + + assertRanges(foldingModel, [r1, r(2, 5, true), r(3, 4, true)]); + assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]); + + foldingModel.update(ranges); + + assertRanges(foldingModel, [r1, r(2, 5, true), r(3, 4, true)]); + assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]); + + textModel.dispose(); + } finally { + textModel.dispose(); + } + + }); + }); diff --git a/src/vs/editor/contrib/folding/test/foldingRanges.test.ts b/src/vs/editor/contrib/folding/test/foldingRanges.test.ts index dd572ec9850..214135a9c8c 100644 --- a/src/vs/editor/contrib/folding/test/foldingRanges.test.ts +++ b/src/vs/editor/contrib/folding/test/foldingRanges.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration'; import { MAX_FOLDING_REGIONS } from 'vs/editor/contrib/folding/foldingRanges'; @@ -26,7 +26,7 @@ suite('FoldingRanges', () => { for (let i = 0; i < nRegions; i++) { lines.push('#endregion'); } - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS); assert.equal(actual.length, nRegions, 'len'); for (let i = 0; i < nRegions; i++) { @@ -53,7 +53,7 @@ suite('FoldingRanges', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let actual = computeRanges(textModel, false, markers); // let r0 = r(1, 2); @@ -91,7 +91,7 @@ suite('FoldingRanges', () => { for (let i = 0; i < nRegions; i++) { lines.push('#endregion'); } - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS); assert.equal(actual.length, nRegions, 'len'); for (let i = 0; i < nRegions; i++) { diff --git a/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts b/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts index 18fa5acd3fe..9db5f45e4fb 100644 --- a/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts +++ b/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { FoldingModel } from 'vs/editor/contrib/folding/foldingModel'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { TestDecorationProvider } from './foldingModel.test'; import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel'; @@ -38,7 +38,7 @@ suite('Hidden Range Model', () => { /* 9*/ ' }', /* 10*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); let hiddenRangeModel = new HiddenRangeModel(foldingModel); diff --git a/src/vs/editor/contrib/folding/test/indentFold.test.ts b/src/vs/editor/contrib/folding/test/indentFold.test.ts index ed51ec95bac..ccdb26a756e 100644 --- a/src/vs/editor/contrib/folding/test/indentFold.test.ts +++ b/src/vs/editor/contrib/folding/test/indentFold.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; interface IndentRange { start: number; @@ -47,7 +47,7 @@ suite('Indentation Folding', () => { let r8 = r(13, 14);//4 let r9 = r(15, 16);//0 - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) { let indentRanges = computeRanges(model, true, undefined, maxEntries); diff --git a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts index 07e20256667..8a4dbe7f38a 100644 --- a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts +++ b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration'; @@ -15,7 +15,7 @@ interface ExpectedIndentRange { } function assertRanges(lines: string[], expected: ExpectedIndentRange[], offside: boolean, markers?: FoldingMarkers): void { - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let actual = computeRanges(model, offside, markers); let actualRanges: ExpectedIndentRange[] = []; diff --git a/src/vs/editor/contrib/folding/test/syntaxFold.test.ts b/src/vs/editor/contrib/folding/test/syntaxFold.test.ts index 480b84e5d7c..915ba173661 100644 --- a/src/vs/editor/contrib/folding/test/syntaxFold.test.ts +++ b/src/vs/editor/contrib/folding/test/syntaxFold.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/syntaxRangeProvider'; import { FoldingRangeProvider, FoldingRange, FoldingContext, ProviderResult } from 'vs/editor/common/modes'; import { ITextModel } from 'vs/editor/common/model'; @@ -69,12 +69,12 @@ suite('Syntax folding', () => { let r8 = r(14, 15); //6 let r9 = r(22, 23); //0 - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let ranges = [r1, r2, r3, r4, r5, r6, r7, r8, r9]; let providers = [new TestFoldingRangeProvider(model, ranges)]; async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) { - let indentRanges = await new SyntaxRangeProvider(model, providers, maxEntries).compute(CancellationToken.None); + let indentRanges = await new SyntaxRangeProvider(model, providers, () => { }, maxEntries).compute(CancellationToken.None); let actual: IndentRange[] = []; if (indentRanges) { for (let i = 0; i < indentRanges.length; i++) { diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 6d87263ad51..db2d59adaf6 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { alert } from 'vs/base/browser/ui/aria/aria'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { asArray, isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; @@ -27,6 +27,8 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { assertType } from 'vs/base/common/types'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { Iterable } from 'vs/base/common/iterator'; export function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -110,19 +112,20 @@ export abstract class FormattingConflicts { if (formatter.length === 0) { return undefined; } - const { value: selector } = FormattingConflicts._selectors.iterator().next(); + const selector = Iterable.first(FormattingConflicts._selectors); if (selector) { return await selector(formatter, document, mode); } - return formatter[0]; + return undefined; } } -export async function formatDocumentRangeWithSelectedProvider( +export async function formatDocumentRangesWithSelectedProvider( accessor: ServicesAccessor, editorOrModel: ITextModel | IActiveCodeEditor, - range: Range, + rangeOrRanges: Range | Range[], mode: FormattingMode, + progress: IProgress, token: CancellationToken ): Promise { @@ -131,15 +134,16 @@ export async function formatDocumentRangeWithSelectedProvider( const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model); const selected = await FormattingConflicts.select(provider, model, mode); if (selected) { - await instaService.invokeFunction(formatDocumentRangeWithProvider, selected, editorOrModel, range, token); + progress.report(selected); + await instaService.invokeFunction(formatDocumentRangesWithProvider, selected, editorOrModel, rangeOrRanges, token); } } -export async function formatDocumentRangeWithProvider( +export async function formatDocumentRangesWithProvider( accessor: ServicesAccessor, provider: DocumentRangeFormattingEditProvider, editorOrModel: ITextModel | IActiveCodeEditor, - range: Range, + rangeOrRanges: Range | Range[], token: CancellationToken ): Promise { const workerService = accessor.get(IEditorWorkerService); @@ -148,47 +152,59 @@ export async function formatDocumentRangeWithProvider( let cts: CancellationTokenSource; if (isCodeEditor(editorOrModel)) { model = editorOrModel.getModel(); - cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, token); + cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, undefined, token); } else { model = editorOrModel; cts = new TextModelCancellationTokenSource(editorOrModel, token); } - let edits: TextEdit[] | undefined; - try { - const rawEdits = await provider.provideDocumentRangeFormattingEdits( - model, - range, - model.getFormattingOptions(), - cts.token - ); - edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits); - - if (cts.token.isCancellationRequested) { - return true; + // make sure that ranges don't overlap nor touch each other + let ranges: Range[] = []; + let len = 0; + for (let range of asArray(rangeOrRanges).sort(Range.compareRangesUsingStarts)) { + if (len > 0 && Range.areIntersectingOrTouching(ranges[len - 1], range)) { + ranges[len - 1] = Range.fromPositions(ranges[len - 1].getStartPosition(), range.getEndPosition()); + } else { + len = ranges.push(range); } - - } finally { - cts.dispose(); } - if (!edits || edits.length === 0) { + const allEdits: TextEdit[] = []; + for (let range of ranges) { + try { + const rawEdits = await provider.provideDocumentRangeFormattingEdits( + model, + range, + model.getFormattingOptions(), + cts.token + ); + const minEdits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits); + if (minEdits) { + allEdits.push(...minEdits); + } + if (cts.token.isCancellationRequested) { + return true; + } + } finally { + cts.dispose(); + } + } + + if (allEdits.length === 0) { return false; } if (isCodeEditor(editorOrModel)) { // use editor to apply edits - FormattingEdit.execute(editorOrModel, edits); - alertFormattingEdits(edits); - editorOrModel.pushUndoStop(); - editorOrModel.focus(); + FormattingEdit.execute(editorOrModel, allEdits, true); + alertFormattingEdits(allEdits); editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate); } else { // use model to apply edits - const [{ range }] = edits; + const [{ range }] = allEdits; const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); - model.pushEditOperations([initialSelection], edits.map(edit => { + model.pushEditOperations([initialSelection], allEdits.map(edit => { return { text: edit.text, range: Range.lift(edit.range), @@ -211,6 +227,7 @@ export async function formatDocumentWithSelectedProvider( accessor: ServicesAccessor, editorOrModel: ITextModel | IActiveCodeEditor, mode: FormattingMode, + progress: IProgress, token: CancellationToken ): Promise { @@ -219,6 +236,7 @@ export async function formatDocumentWithSelectedProvider( const provider = getRealAndSyntheticDocumentFormattersOrdered(model); const selected = await FormattingConflicts.select(provider, model, mode); if (selected) { + progress.report(selected); await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token); } } @@ -236,7 +254,7 @@ export async function formatDocumentWithProvider( let cts: CancellationTokenSource; if (isCodeEditor(editorOrModel)) { model = editorOrModel.getModel(); - cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, token); + cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, undefined, token); } else { model = editorOrModel; cts = new TextModelCancellationTokenSource(editorOrModel, token); @@ -266,12 +284,10 @@ export async function formatDocumentWithProvider( if (isCodeEditor(editorOrModel)) { // use editor to apply edits - FormattingEdit.execute(editorOrModel, edits); + FormattingEdit.execute(editorOrModel, edits, mode !== FormattingMode.Silent); if (mode !== FormattingMode.Silent) { alertFormattingEdits(edits); - editorOrModel.pushUndoStop(); - editorOrModel.focus(); editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate); } diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 219308c8467..bedb20cdd97 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -16,7 +16,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { getOnTypeFormattingEdits, alertFormattingEdits, formatDocumentRangeWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; +import { getOnTypeFormattingEdits, alertFormattingEdits, formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; import * as nls from 'vs/nls'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; @@ -25,6 +25,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Progress, IEditorProgressService } from 'vs/platform/progress/common/progress'; class FormatOnType implements IEditorContribution { @@ -138,7 +139,7 @@ class FormatOnType implements IEditorContribution { } if (isNonEmptyArray(edits)) { - FormattingEdit.execute(this._editor, edits); + FormattingEdit.execute(this._editor, edits, true); alertFormattingEdits(edits); } @@ -201,7 +202,7 @@ class FormatOnPaste implements IEditorContribution { if (this.editor.getSelections().length > 1) { return; } - this._instantiationService.invokeFunction(formatDocumentRangeWithSelectedProvider, this.editor, range, FormattingMode.Silent, CancellationToken.None).catch(onUnexpectedError); + this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, FormattingMode.Silent, Progress.None, CancellationToken.None).catch(onUnexpectedError); } } @@ -212,7 +213,7 @@ class FormatDocumentAction extends EditorAction { id: 'editor.action.formatDocument', label: nls.localize('formatDocument.label', "Format Document"), alias: 'Format Document', - precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), + precondition: ContextKeyExpr.and(EditorContextKeys.notInCompositeEditor, EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider), primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, @@ -230,7 +231,11 @@ class FormatDocumentAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (editor.hasModel()) { const instaService = accessor.get(IInstantiationService); - await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, CancellationToken.None); + const progressService = accessor.get(IEditorProgressService); + await progressService.showWhile( + instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None), + 250 + ); } } } @@ -262,11 +267,18 @@ class FormatSelectionAction extends EditorAction { } const instaService = accessor.get(IInstantiationService); const model = editor.getModel(); - let range: Range = editor.getSelection(); - if (range.isEmpty()) { - range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)); - } - await instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None); + + const ranges = editor.getSelections().map(range => { + return range.isEmpty() + ? new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)) + : range; + }); + + const progressService = accessor.get(IEditorProgressService); + await progressService.showWhile( + instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, ranges, FormattingMode.Explicit, Progress.None, CancellationToken.None), + 250 + ); } } diff --git a/src/vs/editor/contrib/format/formattingEdit.ts b/src/vs/editor/contrib/format/formattingEdit.ts index 27dbbd06762..cabb0a73d7a 100644 --- a/src/vs/editor/contrib/format/formattingEdit.ts +++ b/src/vs/editor/contrib/format/formattingEdit.ts @@ -43,8 +43,10 @@ export class FormattingEdit { return fullModelRange.equalsRange(editRange); } - static execute(editor: ICodeEditor, _edits: TextEdit[]) { - editor.pushUndoStop(); + static execute(editor: ICodeEditor, _edits: TextEdit[], addUndoStops: boolean) { + if (addUndoStops) { + editor.pushUndoStop(); + } const edits = FormattingEdit._handleEolEdits(editor, _edits); if (edits.length === 1 && FormattingEdit._isFullModelReplaceEdit(editor, edits[0])) { // We use replace semantics and hope that markers stay put... @@ -52,6 +54,8 @@ export class FormattingEdit { } else { editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text))); } - editor.pushUndoStop(); + if (addUndoStops) { + editor.pushUndoStop(); + } } } diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 80e2342e747..001ed3f273b 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -4,418 +4,181 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Emitter } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { IMarker } from 'vs/platform/markers/common/markers'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { MarkerNavigationWidget } from './gotoErrorWidget'; -import { compare } from 'vs/base/common/strings'; -import { binarySearch, find } from 'vs/base/common/arrays'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { Action } from 'vs/base/common/actions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isEqual } from 'vs/base/common/resources'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; - -class MarkerModel { - - private readonly _editor: ICodeEditor; - private _markers: IMarker[]; - private _nextIdx: number; - private readonly _toUnbind = new DisposableStore(); - private _ignoreSelectionChange: boolean; - private readonly _onCurrentMarkerChanged: Emitter; - private readonly _onMarkerSetChanged: Emitter; - - constructor(editor: ICodeEditor, markers: IMarker[]) { - this._editor = editor; - this._markers = []; - this._nextIdx = -1; - this._ignoreSelectionChange = false; - this._onCurrentMarkerChanged = new Emitter(); - this._onMarkerSetChanged = new Emitter(); - this.setMarkers(markers); - - // listen on editor - this._toUnbind.add(this._editor.onDidDispose(() => this.dispose())); - this._toUnbind.add(this._editor.onDidChangeCursorPosition(() => { - if (this._ignoreSelectionChange) { - return; - } - if (this.currentMarker && this._editor.getPosition() && Range.containsPosition(this.currentMarker, this._editor.getPosition()!)) { - return; - } - this._nextIdx = -1; - })); - } - - public get onCurrentMarkerChanged() { - return this._onCurrentMarkerChanged.event; - } - - public get onMarkerSetChanged() { - return this._onMarkerSetChanged.event; - } - - public setMarkers(markers: IMarker[]): void { - - let oldMarker = this._nextIdx >= 0 ? this._markers[this._nextIdx] : undefined; - this._markers = markers || []; - this._markers.sort(MarkerNavigationAction.compareMarker); - if (!oldMarker) { - this._nextIdx = -1; - } else { - this._nextIdx = Math.max(-1, binarySearch(this._markers, oldMarker, MarkerNavigationAction.compareMarker)); - } - this._onMarkerSetChanged.fire(this); - } - - public withoutWatchingEditorPosition(callback: () => void): void { - this._ignoreSelectionChange = true; - try { - callback(); - } finally { - this._ignoreSelectionChange = false; - } - } - - private _initIdx(fwd: boolean): void { - let found = false; - const position = this._editor.getPosition(); - for (let i = 0; i < this._markers.length; i++) { - let range = Range.lift(this._markers[i]); - - if (range.isEmpty() && this._editor.getModel()) { - const word = this._editor.getModel()!.getWordAtPosition(range.getStartPosition()); - if (word) { - range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn); - } - } - - if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) { - this._nextIdx = i; - found = true; - break; - } - } - if (!found) { - // after the last change - this._nextIdx = fwd ? 0 : this._markers.length - 1; - } - if (this._nextIdx < 0) { - this._nextIdx = this._markers.length - 1; - } - } - - get currentMarker(): IMarker | undefined { - return this.canNavigate() ? this._markers[this._nextIdx] : undefined; - } - - set currentMarker(marker: IMarker | undefined) { - const idx = this._nextIdx; - this._nextIdx = -1; - if (marker) { - this._nextIdx = this.indexOf(marker); - } - if (this._nextIdx !== idx) { - this._onCurrentMarkerChanged.fire(marker); - } - } - - public move(fwd: boolean, inCircles: boolean): boolean { - if (!this.canNavigate()) { - this._onCurrentMarkerChanged.fire(undefined); - return !inCircles; - } - - let oldIdx = this._nextIdx; - let atEdge = false; - - if (this._nextIdx === -1) { - this._initIdx(fwd); - - } else if (fwd) { - if (inCircles || this._nextIdx + 1 < this._markers.length) { - this._nextIdx = (this._nextIdx + 1) % this._markers.length; - } else { - atEdge = true; - } - - } else if (!fwd) { - if (inCircles || this._nextIdx > 0) { - this._nextIdx = (this._nextIdx - 1 + this._markers.length) % this._markers.length; - } else { - atEdge = true; - } - } - - if (oldIdx !== this._nextIdx) { - const marker = this._markers[this._nextIdx]; - this._onCurrentMarkerChanged.fire(marker); - } - - return atEdge; - } - - public canNavigate(): boolean { - return this._markers.length > 0; - } - - public findMarkerAtPosition(pos: Position): IMarker | undefined { - return find(this._markers, marker => Range.containsPosition(marker, pos)); - } - - public get total() { - return this._markers.length; - } - - public indexOf(marker: IMarker): number { - return 1 + this._markers.indexOf(marker); - } - - public dispose(): void { - this._toUnbind.dispose(); - } -} +import { MenuId } from 'vs/platform/actions/common/actions'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMarkerNavigationService, MarkerList } from 'vs/editor/contrib/gotoError/markerNavigationService'; export class MarkerController implements IEditorContribution { - public static readonly ID = 'editor.contrib.markerController'; + static readonly ID = 'editor.contrib.markerController'; - public static get(editor: ICodeEditor): MarkerController { + static get(editor: ICodeEditor): MarkerController { return editor.getContribution(MarkerController.ID); } private readonly _editor: ICodeEditor; - private _model: MarkerModel | null = null; - private _widget: MarkerNavigationWidget | null = null; + private readonly _widgetVisible: IContextKey; - private readonly _disposeOnClose = new DisposableStore(); + private readonly _sessionDispoables = new DisposableStore(); + + private _model?: MarkerList; + private _widget?: MarkerNavigationWidget; constructor( editor: ICodeEditor, - @IMarkerService private readonly _markerService: IMarkerService, + @IMarkerNavigationService private readonly _markerNavigationService: IMarkerNavigationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IThemeService private readonly _themeService: IThemeService, @ICodeEditorService private readonly _editorService: ICodeEditorService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IOpenerService private readonly _openerService: IOpenerService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { this._editor = editor; this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService); } - public dispose(): void { + dispose(): void { this._cleanUp(); - this._disposeOnClose.dispose(); + this._sessionDispoables.dispose(); } private _cleanUp(): void { this._widgetVisible.reset(); - this._disposeOnClose.clear(); - this._widget = null; - this._model = null; + this._sessionDispoables.clear(); + this._widget = undefined; + this._model = undefined; } - public getOrCreateModel(): MarkerModel { + private _getOrCreateModel(uri: URI | undefined): MarkerList { - if (this._model) { + if (this._model && this._model.matches(uri)) { return this._model; } - - const markers = this._getMarkers(); - this._model = new MarkerModel(this._editor, markers); - this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._disposeOnClose); - - const prevMarkerKeybinding = this._keybindingService.lookupKeybinding(PrevMarkerAction.ID); - const nextMarkerKeybinding = this._keybindingService.lookupKeybinding(NextMarkerAction.ID); - const actions = [ - new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem codicon-chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }), - new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } }) - ]; - this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService, this._openerService, this._configurationService); - this._widgetVisible.set(true); - this._widget.onDidClose(() => this.closeMarkersNavigation(), this, this._disposeOnClose); - - this._disposeOnClose.add(this._model); - this._disposeOnClose.add(this._widget); - for (const action of actions) { - this._disposeOnClose.add(action); + let reusePosition = false; + if (this._model) { + reusePosition = true; + this._cleanUp(); } - this._disposeOnClose.add(this._widget.onDidSelectRelatedInformation(related => { - this._editorService.openCodeEditor({ - resource: related.resource, - options: { pinned: true, revealIfOpened: true, selection: Range.lift(related).collapseToStart() } - }, this._editor).then(undefined, onUnexpectedError); - this.closeMarkersNavigation(false); - })); - this._disposeOnClose.add(this._editor.onDidChangeModel(() => this._cleanUp())); - this._disposeOnClose.add(this._model.onCurrentMarkerChanged(marker => { - if (!marker || !this._model) { - this._cleanUp(); - } else { - this._model.withoutWatchingEditorPosition(() => { - if (!this._widget || !this._model) { - return; - } - this._widget.showAtMarker(marker, this._model.indexOf(marker), this._model.total); - }); + this._model = this._markerNavigationService.getMarkerList(uri); + if (reusePosition) { + this._model.move(true, this._editor.getModel()!, this._editor.getPosition()!); + } + + this._widget = this._instantiationService.createInstance(MarkerNavigationWidget, this._editor); + this._widget.onDidClose(() => this.close(), this, this._sessionDispoables); + this._widgetVisible.set(true); + + this._sessionDispoables.add(this._model); + this._sessionDispoables.add(this._widget); + + // follow cursor + this._sessionDispoables.add(this._editor.onDidChangeCursorPosition(e => { + if (!this._model?.selected || !Range.containsPosition(this._model?.selected.marker, e.position)) { + this._model?.resetIndex(); } })); - this._disposeOnClose.add(this._model.onMarkerSetChanged(() => { + + // update markers + this._sessionDispoables.add(this._model.onDidChange(() => { if (!this._widget || !this._widget.position || !this._model) { return; } - - const marker = this._model.findMarkerAtPosition(this._widget.position); - if (marker) { - this._widget.updateMarker(marker); + const info = this._model.find(this._editor.getModel()!.uri, this._widget!.position!); + if (info) { + this._widget.updateMarker(info.marker); } else { this._widget.showStale(); } })); + // open related + this._sessionDispoables.add(this._widget.onDidSelectRelatedInformation(related => { + this._editorService.openCodeEditor({ + resource: related.resource, + options: { pinned: true, revealIfOpened: true, selection: Range.lift(related).collapseToStart() } + }, this._editor); + this.close(false); + })); + this._sessionDispoables.add(this._editor.onDidChangeModel(() => this._cleanUp())); + return this._model; } - public closeMarkersNavigation(focusEditor: boolean = true): void { + close(focusEditor: boolean = true): void { this._cleanUp(); if (focusEditor) { this._editor.focus(); } } - public show(marker: IMarker): void { - const model = this.getOrCreateModel(); - model.currentMarker = marker; + showAtMarker(marker: IMarker): void { + if (this._editor.hasModel()) { + const model = this._getOrCreateModel(this._editor.getModel().uri); + model.resetIndex(); + model.move(true, this._editor.getModel(), new Position(marker.startLineNumber, marker.startColumn)); + if (model.selected) { + this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); + } + } } - private _onMarkerChanged(changedResources: URI[]): void { - const editorModel = this._editor.getModel(); - if (!editorModel) { - return; - } + async nagivate(next: boolean, multiFile: boolean) { + if (this._editor.hasModel()) { + const model = this._getOrCreateModel(multiFile ? undefined : this._editor.getModel().uri); + model.move(next, this._editor.getModel(), this._editor.getPosition()); + if (!model.selected) { + return; + } + if (model.selected.marker.resource.toString() !== this._editor.getModel().uri.toString()) { + // show in different editor + this._cleanUp(); + const otherEditor = await this._editorService.openCodeEditor({ + resource: model.selected.marker.resource, + options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.NearTop, selection: model.selected.marker } + }, this._editor); - if (!this._model) { - return; - } + if (otherEditor) { + MarkerController.get(otherEditor).close(); + MarkerController.get(otherEditor).nagivate(next, multiFile); + } - if (!changedResources.some(r => isEqual(editorModel.uri, r))) { - return; + } else { + // show in this editor + this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); + } } - this._model.setMarkers(this._getMarkers()); - } - - private _getMarkers(): IMarker[] { - let model = this._editor.getModel(); - if (!model) { - return []; - } - - return this._markerService.read({ - resource: model.uri, - severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info - }); } } class MarkerNavigationAction extends EditorAction { - private readonly _isNext: boolean; - - private readonly _multiFile: boolean; - - constructor(next: boolean, multiFile: boolean, opts: IActionOptions) { + constructor( + private readonly _next: boolean, + private readonly _multiFile: boolean, + opts: IActionOptions + ) { super(opts); - this._isNext = next; - this._multiFile = multiFile; } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - - const markerService = accessor.get(IMarkerService); - const editorService = accessor.get(ICodeEditorService); - const controller = MarkerController.get(editor); - if (!controller) { - return Promise.resolve(undefined); + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + if (editor.hasModel()) { + MarkerController.get(editor).nagivate(this._next, this._multiFile); } - - const model = controller.getOrCreateModel(); - const atEdge = model.move(this._isNext, !this._multiFile); - if (!atEdge || !this._multiFile) { - return Promise.resolve(undefined); - } - - // try with the next/prev file - let markers = markerService.read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }).sort(MarkerNavigationAction.compareMarker); - if (markers.length === 0) { - return Promise.resolve(undefined); - } - - const editorModel = editor.getModel(); - if (!editorModel) { - return Promise.resolve(undefined); - } - - let oldMarker = model.currentMarker || { resource: editorModel!.uri, severity: MarkerSeverity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; - let idx = binarySearch(markers, oldMarker, MarkerNavigationAction.compareMarker); - if (idx < 0) { - // find best match... - idx = ~idx; - idx %= markers.length; - } else if (this._isNext) { - idx = (idx + 1) % markers.length; - } else { - idx = (idx + markers.length - 1) % markers.length; - } - - let newMarker = markers[idx]; - if (isEqual(newMarker.resource, editorModel.uri)) { - // the next `resource` is this resource which - // means we cycle within this file - model.move(this._isNext, true); - return Promise.resolve(undefined); - } - - // close the widget for this editor-instance, open the resource - // for the next marker and re-start marker navigation in there - controller.closeMarkersNavigation(); - - return editorService.openCodeEditor({ - resource: newMarker.resource, - options: { pinned: false, revealIfOpened: true, revealInCenterIfOutsideViewport: true, selection: newMarker } - }, editor).then(editor => { - if (!editor) { - return undefined; - } - return editor.getAction(this.id).run(); - }); - } - - static compareMarker(a: IMarker, b: IMarker): number { - let res = compare(a.resource.toString(), b.resource.toString()); - if (res === 0) { - res = MarkerSeverity.compare(a.severity, b.severity); - } - if (res === 0) { - res = Range.compareRangesUsingStarts(a, b); - } - return res; } } @@ -427,8 +190,19 @@ export class NextMarkerAction extends MarkerNavigationAction { id: NextMarkerAction.ID, label: NextMarkerAction.LABEL, alias: 'Go to Next Problem (Error, Warning, Info)', - precondition: EditorContextKeys.writable, - kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib } + precondition: undefined, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Alt | KeyCode.F8, + weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + menuId: MarkerNavigationWidget.TitleMenu, + title: NextMarkerAction.LABEL, + icon: registerIcon('marker-navigation-next', Codicon.chevronDown), + group: 'navigation', + order: 1 + } }); } } @@ -441,8 +215,19 @@ class PrevMarkerAction extends MarkerNavigationAction { id: PrevMarkerAction.ID, label: PrevMarkerAction.LABEL, alias: 'Go to Previous Problem (Error, Warning, Info)', - precondition: EditorContextKeys.writable, - kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib } + precondition: undefined, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F8, + weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + menuId: MarkerNavigationWidget.TitleMenu, + title: NextMarkerAction.LABEL, + icon: registerIcon('marker-navigation-previous', Codicon.chevronUp), + group: 'navigation', + order: 2 + } }); } } @@ -453,11 +238,17 @@ class NextMarkerInFilesAction extends MarkerNavigationAction { id: 'editor.action.marker.nextInFiles', label: nls.localize('markerAction.nextInFiles.label', "Go to Next Problem in Files (Error, Warning, Info)"), alias: 'Go to Next Problem in Files (Error, Warning, Info)', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyCode.F8, weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + menuId: MenuId.MenubarGoMenu, + title: nls.localize({ key: 'miGotoNextProblem', comment: ['&& denotes a mnemonic'] }, "Next &&Problem"), + group: '6_problem_nav', + order: 1 } }); } @@ -469,11 +260,17 @@ class PrevMarkerInFilesAction extends MarkerNavigationAction { id: 'editor.action.marker.prevInFiles', label: nls.localize('markerAction.previousInFiles.label', "Go to Previous Problem in Files (Error, Warning, Info)"), alias: 'Go to Previous Problem in Files (Error, Warning, Info)', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Shift | KeyCode.F8, weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + menuId: MenuId.MenubarGoMenu, + title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem"), + group: '6_problem_nav', + order: 2 } }); } @@ -492,7 +289,7 @@ const MarkerCommand = EditorCommand.bindToContribution(MarkerC registerEditorCommand(new MarkerCommand({ id: 'closeMarkersNavigation', precondition: CONTEXT_MARKERS_NAVIGATION_VISIBLE, - handler: x => x.closeMarkersNavigation(), + handler: x => x.close(), kbOpts: { weight: KeybindingWeight.EditorContrib + 50, kbExpr: EditorContextKeys.focus, @@ -500,22 +297,3 @@ registerEditorCommand(new MarkerCommand({ secondary: [KeyMod.Shift | KeyCode.Escape] } })); - -// Go to menu -MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '6_problem_nav', - command: { - id: 'editor.action.marker.nextInFiles', - title: nls.localize({ key: 'miGotoNextProblem', comment: ['&& denotes a mnemonic'] }, "Next &&Problem") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '6_problem_nav', - command: { - id: 'editor.action.marker.prevInFiles', - title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem") - }, - order: 2 -}); diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 5190682a7c9..66bdc31f06c 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -8,11 +8,10 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; -import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerColor, oneOf, textLinkForeground, editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; @@ -27,10 +26,10 @@ import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/action import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { OperatingSystem, OS } from 'vs/base/common/platform'; - -type ModifierKey = 'meta' | 'ctrl' | 'alt'; +import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; class MessageWidget { @@ -44,7 +43,6 @@ class MessageWidget { private readonly _relatedDiagnostics = new WeakMap(); private readonly _disposables: DisposableStore = new DisposableStore(); - private _clickModifierKey: ModifierKey; private _codeLink?: HTMLElement; constructor( @@ -52,17 +50,16 @@ class MessageWidget { editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void, private readonly _openerService: IOpenerService, - private readonly _configurationService: IConfigurationService ) { this._editor = editor; const domNode = document.createElement('div'); domNode.className = 'descriptioncontainer'; - domNode.setAttribute('aria-live', 'assertive'); - domNode.setAttribute('role', 'alert'); this._messageBlock = document.createElement('div'); - dom.addClass(this._messageBlock, 'message'); + this._messageBlock.classList.add('message'); + this._messageBlock.setAttribute('aria-live', 'assertive'); + this._messageBlock.setAttribute('role', 'alert'); domNode.appendChild(this._messageBlock); this._relatedBlock = document.createElement('div'); @@ -88,23 +85,14 @@ class MessageWidget { domNode.style.top = `-${e.scrollTop}px`; })); this._disposables.add(this._scrollable); - - this._clickModifierKey = this._getClickModifierKey(); - this._disposables.add(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('editor.multiCursorModifier')) { - this._clickModifierKey = this._getClickModifierKey(); - if (this._codeLink) { - this._codeLink.setAttribute('title', this._getCodelinkTooltip()); - } - } - })); } dispose(): void { dispose(this._disposables); } - update({ source, message, relatedInformation, code }: IMarker): void { + update(marker: IMarker): void { + const { source, message, relatedInformation, code } = marker; let sourceAndCodeLength = (source?.length || 0) + '()'.length; if (code) { if (typeof code === 'string') { @@ -122,6 +110,7 @@ class MessageWidget { } dom.clearNode(this._messageBlock); + this._messageBlock.setAttribute('aria-label', this.getAriaLabel(marker)); this._editor.applyFontInfo(this._messageBlock); let lastLineElement = this._messageBlock; for (const line of lines) { @@ -134,31 +123,28 @@ class MessageWidget { } if (source || code) { const detailsElement = document.createElement('span'); - dom.addClass(detailsElement, 'details'); + detailsElement.classList.add('details'); lastLineElement.appendChild(detailsElement); if (source) { const sourceElement = document.createElement('span'); sourceElement.innerText = source; - dom.addClass(sourceElement, 'source'); + sourceElement.classList.add('source'); detailsElement.appendChild(sourceElement); } if (code) { if (typeof code === 'string') { const codeElement = document.createElement('span'); codeElement.innerText = `(${code})`; - dom.addClass(codeElement, 'code'); + codeElement.classList.add('code'); detailsElement.appendChild(codeElement); } else { this._codeLink = dom.$('a.code-link'); - this._codeLink.setAttribute('title', this._getCodelinkTooltip()); - this._codeLink.setAttribute('href', `${code.link.toString()}`); + this._codeLink.setAttribute('href', `${code.target.toString()}`); this._codeLink.onclick = (e) => { + this._openerService.open(code.target); e.preventDefault(); - if ((this._clickModifierKey === 'meta' && e.metaKey) || (this._clickModifierKey === 'ctrl' && e.ctrlKey) || (this._clickModifierKey === 'alt' && e.altKey)) { - this._openerService.open(code.link); - e.stopPropagation(); - } + e.stopPropagation(); }; const codeElement = dom.append(this._codeLink, dom.$('span')); @@ -180,8 +166,8 @@ class MessageWidget { let container = document.createElement('div'); let relatedResource = document.createElement('a'); - dom.addClass(relatedResource, 'filename'); - relatedResource.innerHTML = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `; + relatedResource.classList.add('filename'); + relatedResource.innerText = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `; relatedResource.title = getPathLabel(related.resource, undefined); this._relatedDiagnostics.set(relatedResource, related); @@ -212,34 +198,37 @@ class MessageWidget { return Math.min(17, this._lines); } - private _getClickModifierKey(): ModifierKey { - const value = this._configurationService.getValue<'ctrlCmd' | 'alt'>('editor.multiCursorModifier'); - if (value === 'ctrlCmd') { - return 'alt'; - } else { - if (OS === OperatingSystem.Macintosh) { - return 'meta'; - } else { - return 'ctrl'; - } + private getAriaLabel(marker: IMarker): string { + let severityLabel = ''; + switch (marker.severity) { + case MarkerSeverity.Error: + severityLabel = nls.localize('Error', "Error"); + break; + case MarkerSeverity.Warning: + severityLabel = nls.localize('Warning', "Warning"); + break; + case MarkerSeverity.Info: + severityLabel = nls.localize('Info', "Info"); + break; + case MarkerSeverity.Hint: + severityLabel = nls.localize('Hint', "Hint"); + break; } - } - private _getCodelinkTooltip(): string { - const tooltipLabel = nls.localize('links.navigate.follow', 'Follow link'); - const tooltipKeybinding = this._clickModifierKey === 'ctrl' - ? nls.localize('links.navigate.kb.meta', 'ctrl + click') - : - this._clickModifierKey === 'meta' - ? OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.meta.mac', 'cmd + click') : nls.localize('links.navigate.kb.meta', 'ctrl + click') - : OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.alt.mac', 'option + click') : nls.localize('links.navigate.kb.alt', 'alt + click'); - - return `${tooltipLabel} (${tooltipKeybinding})`; + let ariaLabel = nls.localize('marker aria', "{0} at {1}. ", severityLabel, marker.startLineNumber + ':' + marker.startColumn); + const model = this._editor.getModel(); + if (model && (marker.startLineNumber <= model.getLineCount()) && (marker.startLineNumber >= 1)) { + const lineContent = model.getLineContent(marker.startLineNumber); + ariaLabel = `${lineContent}, ${ariaLabel}`; + } + return ariaLabel; } } export class MarkerNavigationWidget extends PeekViewWidget { + static readonly TitleMenu = new MenuId('gotoErrorTitleMenu'); + private _parentContainer!: HTMLElement; private _container!: HTMLElement; private _icon!: HTMLElement; @@ -254,22 +243,23 @@ export class MarkerNavigationWidget extends PeekViewWidget { constructor( editor: ICodeEditor, - private readonly actions: ReadonlyArray, - private readonly _themeService: IThemeService, - private readonly _openerService: IOpenerService, - private readonly _configurationService: IConfigurationService + @IThemeService private readonly _themeService: IThemeService, + @IOpenerService private readonly _openerService: IOpenerService, + @IMenuService private readonly _menuService: IMenuService, + @IInstantiationService instantiationService: IInstantiationService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { - super(editor, { showArrow: true, showFrame: true, isAccessible: true }); + super(editor, { showArrow: true, showFrame: true, isAccessible: true }, instantiationService); this._severity = MarkerSeverity.Warning; this._backgroundColor = Color.white; - this._applyTheme(_themeService.getTheme()); - this._callOnDispose.add(_themeService.onThemeChange(this._applyTheme.bind(this))); + this._applyTheme(_themeService.getColorTheme()); + this._callOnDispose.add(_themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this.create(); } - private _applyTheme(theme: ITheme) { + private _applyTheme(theme: IColorTheme) { this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); let colorId = editorMarkerNavigationError; if (this._severity === MarkerSeverity.Warning) { @@ -305,7 +295,14 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _fillHead(container: HTMLElement): void { super._fillHead(container); - this._actionbarWidget!.push(this.actions, { label: false, icon: true, index: 0 }); + + this._disposables.add(this._actionbarWidget!.actionRunner.onDidBeforeRun(e => this.editor.focus())); + + const actions: IAction[] = []; + const menu = this._menuService.createMenu(MarkerNavigationWidget.TitleMenu, this._contextKeyService); + createAndFillInActionBarActions(menu, undefined, actions); + this._actionbarWidget!.push(actions, { label: false, icon: true, index: 0 }); + menu.dispose(); } protected _fillTitleIcon(container: HTMLElement): void { @@ -314,24 +311,25 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _getActionBarOptions(): IActionBarOptions { return { + ...super._getActionBarOptions(), orientation: ActionsOrientation.HORIZONTAL }; } protected _fillBody(container: HTMLElement): void { this._parentContainer = container; - dom.addClass(container, 'marker-widget'); + container.classList.add('marker-widget'); this._parentContainer.tabIndex = 0; this._parentContainer.setAttribute('role', 'tooltip'); this._container = document.createElement('div'); container.appendChild(this._container); - this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService, this._configurationService); + this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService); this._disposables.add(this._message); } - show(where: Position, heightInLines: number): void { + show(): void { throw new Error('call showAtMarker'); } @@ -344,7 +342,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { // update frame color (only applied on 'show') this._severity = marker.severity; - this._applyTheme(this._themeService.getTheme()); + this._applyTheme(this._themeService.getColorTheme()); // show let range = Range.lift(marker); @@ -361,7 +359,8 @@ export class MarkerNavigationWidget extends PeekViewWidget { } this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`; - this.editor.revealPositionInCenter(position, ScrollType.Smooth); + this.editor.revealPositionNearTop(position, ScrollType.Smooth); + this.editor.focus(); } updateMarker(marker: IMarker): void { diff --git a/src/vs/editor/contrib/gotoError/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/markerNavigationService.ts new file mode 100644 index 00000000000..abf0fe7a4e0 --- /dev/null +++ b/src/vs/editor/contrib/gotoError/markerNavigationService.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IMarkerService, MarkerSeverity, IMarker } from 'vs/platform/markers/common/markers'; +import { URI } from 'vs/base/common/uri'; +import { Emitter, Event } from 'vs/base/common/event'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { compare } from 'vs/base/common/strings'; +import { binarySearch } from 'vs/base/common/arrays'; +import { ITextModel } from 'vs/editor/common/model'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { LinkedList } from 'vs/base/common/linkedList'; + +export class MarkerCoordinate { + constructor( + readonly marker: IMarker, + readonly index: number, + readonly total: number + ) { } +} + +export class MarkerList { + + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + + private readonly _resourceFilter?: (uri: URI) => boolean; + private readonly _dispoables = new DisposableStore(); + + private _markers: IMarker[] = []; + private _nextIdx: number = -1; + + constructor( + resourceFilter: URI | ((uri: URI) => boolean) | undefined, + @IMarkerService private readonly _markerService: IMarkerService, + ) { + if (URI.isUri(resourceFilter)) { + this._resourceFilter = uri => uri.toString() === resourceFilter.toString(); + } else if (resourceFilter) { + this._resourceFilter = resourceFilter; + } + + const updateMarker = () => { + this._markers = this._markerService.read({ + resource: URI.isUri(resourceFilter) ? resourceFilter : undefined, + severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info + }); + if (typeof resourceFilter === 'function') { + this._markers = this._markers.filter(m => this._resourceFilter!(m.resource)); + } + this._markers.sort(MarkerList._compareMarker); + }; + + updateMarker(); + + this._dispoables.add(_markerService.onMarkerChanged(uris => { + if (!this._resourceFilter || uris.some(uri => this._resourceFilter!(uri))) { + updateMarker(); + this._nextIdx = -1; + this._onDidChange.fire(); + } + })); + } + + dispose(): void { + this._dispoables.dispose(); + this._onDidChange.dispose(); + } + + matches(uri: URI | undefined) { + if (!this._resourceFilter && !uri) { + return true; + } + if (!this._resourceFilter || !uri) { + return false; + } + return this._resourceFilter(uri); + } + + get selected(): MarkerCoordinate | undefined { + const marker = this._markers[this._nextIdx]; + return marker && new MarkerCoordinate(marker, this._nextIdx + 1, this._markers.length); + } + + private _initIdx(model: ITextModel, position: Position, fwd: boolean): void { + let found = false; + + let idx = this._markers.findIndex(marker => marker.resource.toString() === model.uri.toString()); + if (idx < 0) { + idx = binarySearch(this._markers, { resource: model.uri }, (a, b) => compare(a.resource.toString(), b.resource.toString())); + if (idx < 0) { + idx = ~idx; + } + } + + for (let i = idx; i < this._markers.length; i++) { + let range = Range.lift(this._markers[i]); + + if (range.isEmpty()) { + const word = model.getWordAtPosition(range.getStartPosition()); + if (word) { + range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn); + } + } + + if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) { + this._nextIdx = i; + found = true; + break; + } + + if (this._markers[i].resource.toString() !== model.uri.toString()) { + break; + } + } + + if (!found) { + // after the last change + this._nextIdx = fwd ? 0 : this._markers.length - 1; + } + if (this._nextIdx < 0) { + this._nextIdx = this._markers.length - 1; + } + } + + resetIndex() { + this._nextIdx = -1; + } + + move(fwd: boolean, model: ITextModel, position: Position): boolean { + if (this._markers.length === 0) { + return false; + } + + let oldIdx = this._nextIdx; + if (this._nextIdx === -1) { + this._initIdx(model, position, fwd); + } else if (fwd) { + this._nextIdx = (this._nextIdx + 1) % this._markers.length; + } else if (!fwd) { + this._nextIdx = (this._nextIdx - 1 + this._markers.length) % this._markers.length; + } + + if (oldIdx !== this._nextIdx) { + return true; + } + return false; + } + + find(uri: URI, position: Position): MarkerCoordinate | undefined { + let idx = this._markers.findIndex(marker => marker.resource.toString() === uri.toString()); + if (idx < 0) { + return undefined; + } + for (; idx < this._markers.length; idx++) { + if (Range.containsPosition(this._markers[idx], position)) { + return new MarkerCoordinate(this._markers[idx], idx + 1, this._markers.length); + } + } + return undefined; + } + + private static _compareMarker(a: IMarker, b: IMarker): number { + let res = compare(a.resource.toString(), b.resource.toString()); + if (res === 0) { + res = MarkerSeverity.compare(a.severity, b.severity); + } + if (res === 0) { + res = Range.compareRangesUsingStarts(a, b); + } + return res; + } +} + +export const IMarkerNavigationService = createDecorator('IMarkerNavigationService'); + +export interface IMarkerNavigationService { + readonly _serviceBrand: undefined; + registerProvider(provider: IMarkerListProvider): IDisposable; + getMarkerList(resource: URI | undefined): MarkerList; +} + +export interface IMarkerListProvider { + getMarkerList(resource: URI | undefined): MarkerList | undefined; +} + +class MarkerNavigationService implements IMarkerNavigationService, IMarkerListProvider { + + readonly _serviceBrand: undefined; + + private readonly _provider = new LinkedList(); + + constructor(@IMarkerService private readonly _markerService: IMarkerService) { } + + registerProvider(provider: IMarkerListProvider): IDisposable { + const remove = this._provider.unshift(provider); + return toDisposable(() => remove()); + } + + getMarkerList(resource: URI | undefined): MarkerList { + for (let provider of this._provider) { + const result = provider.getMarkerList(resource); + if (result) { + return result; + } + } + // default + return new MarkerList(resource, this._markerService); + } +} + +registerSingleton(IMarkerNavigationService, MarkerNavigationService, true); diff --git a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css index c748c365bdc..bf9d507df0a 100644 --- a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css +++ b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css @@ -32,7 +32,7 @@ user-select: text; -webkit-user-select: text; -ms-user-select: text; - padding: 8px 12px 0px 20px; + padding: 8px 12px 0 20px; } .monaco-editor .marker-widget .descriptioncontainer .message { diff --git a/src/vs/editor/contrib/quickOpen/quickOpen.ts b/src/vs/editor/contrib/gotoSymbol/documentSymbols.ts similarity index 93% rename from src/vs/editor/contrib/quickOpen/quickOpen.ts rename to src/vs/editor/contrib/gotoSymbol/documentSymbols.ts index 69c379413e2..51702b59e92 100644 --- a/src/vs/editor/contrib/quickOpen/quickOpen.ts +++ b/src/vs/editor/contrib/gotoSymbol/documentSymbols.ts @@ -11,19 +11,19 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { values } from 'vs/base/common/collections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { assertType } from 'vs/base/common/types'; +import { Iterable } from 'vs/base/common/iterator'; export async function getDocumentSymbols(document: ITextModel, flat: boolean, token: CancellationToken): Promise { const model = await OutlineModel.create(document, token); const roots: DocumentSymbol[] = []; - for (const child of values(model.children)) { + for (const child of model.children.values()) { if (child instanceof OutlineElement) { roots.push(child.symbol); } else { - roots.push(...values(child.children).map(child => child.symbol)); + roots.push(...Iterable.map(child.children.values(), child => child.symbol)); } } @@ -62,7 +62,6 @@ function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideCo } } - CommandsRegistry.registerCommand('_executeDocumentSymbolProvider', async function (accessor, ...args) { const [resource] = args; assertType(URI.isUri(resource)); diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index b0610d605b1..232aab8d0f8 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -36,6 +36,8 @@ import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ScrollType, IEditorAction } from 'vs/editor/common/editorCommon'; import { assertType } from 'vs/base/common/types'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; MenuRegistry.appendMenuItem(MenuId.EditorContext, { @@ -129,7 +131,7 @@ abstract class SymbolNavigationAction extends EditorAction { private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel): Promise { const gotoLocation = this._getGoToPreference(editor); - if (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) { + if (!(editor instanceof EmbeddedCodeEditorWidget) && (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1))) { this._openInPeek(editor, model); } else { @@ -165,7 +167,7 @@ abstract class SymbolNavigationAction extends EditorAction { resource: reference.uri, options: { selection: Range.collapseToStart(range), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport } }, editor, sideBySide); @@ -238,7 +240,7 @@ registerEditorAction(class GoToDefinitionAction extends DefinitionAction { alias: 'Go to Definition', precondition: ContextKeyExpr.and( EditorContextKeys.hasDefinitionProvider, - EditorContextKeys.isInEmbeddedEditor.toNegated()), + EditorContextKeys.isInWalkThroughSnippet.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: goToDefinitionKb, @@ -274,7 +276,7 @@ registerEditorAction(class OpenDefinitionToSideAction extends DefinitionAction { alias: 'Open Definition to the Side', precondition: ContextKeyExpr.and( EditorContextKeys.hasDefinitionProvider, - EditorContextKeys.isInEmbeddedEditor.toNegated()), + EditorContextKeys.isInWalkThroughSnippet.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDefinitionKb), @@ -301,7 +303,7 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasDefinitionProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -359,7 +361,7 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { alias: 'Go to Declaration', precondition: ContextKeyExpr.and( EditorContextKeys.hasDeclarationProvider, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), contextMenuOpts: { group: 'navigation', @@ -394,7 +396,7 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasDeclarationProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), contextMenuOpts: { menuId: MenuId.EditorContextPeek, @@ -445,7 +447,7 @@ registerEditorAction(class GoToTypeDefinitionAction extends TypeDefinitionAction alias: 'Go to Type Definition', precondition: ContextKeyExpr.and( EditorContextKeys.hasTypeDefinitionProvider, - EditorContextKeys.isInEmbeddedEditor.toNegated()), + EditorContextKeys.isInWalkThroughSnippet.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: 0, @@ -481,7 +483,7 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction precondition: ContextKeyExpr.and( EditorContextKeys.hasTypeDefinitionProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), contextMenuOpts: { menuId: MenuId.EditorContextPeek, @@ -532,7 +534,7 @@ registerEditorAction(class GoToImplementationAction extends ImplementationAction alias: 'Go to Implementations', precondition: ContextKeyExpr.and( EditorContextKeys.hasImplementationProvider, - EditorContextKeys.isInEmbeddedEditor.toNegated()), + EditorContextKeys.isInWalkThroughSnippet.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyCode.F12, @@ -568,7 +570,7 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction precondition: ContextKeyExpr.and( EditorContextKeys.hasImplementationProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -619,7 +621,7 @@ registerEditorAction(class GoToReferencesAction extends ReferencesAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -658,7 +660,7 @@ registerEditorAction(class PeekReferencesAction extends ReferencesAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), contextMenuOpts: { menuId: MenuId.EditorContextPeek, @@ -691,7 +693,7 @@ class GenericGoToLocationAction extends SymbolNavigationAction { alias: 'Go To Any Symbol', precondition: ContextKeyExpr.and( PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), }); } @@ -720,9 +722,10 @@ CommandsRegistry.registerCommand({ { name: 'position', description: 'The position at which to start', constraint: corePosition.Position.isIPosition }, { name: 'locations', description: 'An array of locations.', constraint: Array }, { name: 'multiple', description: 'Define what to do when having multiple results, either `peek`, `gotoAndPeek`, or `goto' }, + { name: 'noResultsMessage', description: 'Human readable message that shows when locations is empty.' }, ] }, - handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any, openInPeek?: boolean) => { + handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any, noResultsMessage?: string, openInPeek?: boolean) => { assertType(URI.isUri(resource)); assertType(corePosition.Position.isIPosition(position)); assertType(Array.isArray(references)); @@ -737,7 +740,16 @@ CommandsRegistry.registerCommand({ editor.revealPositionInCenterIfOutsideViewport(position, ScrollType.Smooth); return editor.invokeWithinContext(accessor => { - const command = new GenericGoToLocationAction({ muteMessage: true, openInPeek: Boolean(openInPeek), openToSide: false }, references, multiple as GoToLocationValues); + const command = new class extends GenericGoToLocationAction { + _getNoResultFoundMessage(info: IWordAtPosition | null) { + return noResultsMessage || super._getNoResultFoundMessage(info); + } + }({ + muteMessage: !Boolean(noResultsMessage), + openInPeek: Boolean(openInPeek), + openToSide: false + }, references, multiple as GoToLocationValues); + accessor.get(IInstantiationService).invokeFunction(command.run.bind(command), editor); }); } @@ -756,7 +768,7 @@ CommandsRegistry.registerCommand({ ] }, handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any) => { - accessor.get(ICommandService).executeCommand('editor.action.goToLocations', resource, position, references, multiple, true); + accessor.get(ICommandService).executeCommand('editor.action.goToLocations', resource, position, references, multiple, undefined, true); } }); diff --git a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts index d478c783204..e3e7ec687d5 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; @@ -28,9 +27,18 @@ function getLocationLinks( return undefined; }); }); - return Promise.all(promises) - .then(flatten) - .then(coalesce); + + return Promise.all(promises).then(values => { + const result: LocationLink[] = []; + for (let value of values) { + if (Array.isArray(value)) { + result.push(...value); + } else if (value) { + result.push(value); + } + } + return result; + }); } diff --git a/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture.ts b/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture.ts index 003ed988f84..986023ffac2 100644 --- a/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture.ts +++ b/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode } from 'vs/base/common/keyCodes'; -import * as browser from 'vs/base/browser/browser'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ICodeEditor, IEditorMouseEvent, IMouseTarget } from 'vs/editor/browser/editorBrowser'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -31,7 +30,7 @@ export class ClickLinkMouseEvent { this.target = source.target; this.hasTriggerModifier = hasModifier(source.event, opts.triggerModifier); this.hasSideBySideModifier = hasModifier(source.event, opts.triggerSideBySideModifier); - this.isNoneOrSingleMouseDown = (browser.isIE || source.event.detail <= 1); // IE does not support event.detail properly + this.isNoneOrSingleMouseDown = (source.event.detail <= 1); } } @@ -109,8 +108,9 @@ export class ClickLinkGesture extends Disposable { private readonly _editor: ICodeEditor; private _opts: ClickLinkOptions; - private lastMouseMoveEvent: ClickLinkMouseEvent | null; - private hasTriggerKeyOnMouseDown: boolean; + private _lastMouseMoveEvent: ClickLinkMouseEvent | null; + private _hasTriggerKeyOnMouseDown: boolean; + private _lineNumberOnMouseDown: number; constructor(editor: ICodeEditor) { super(); @@ -118,8 +118,9 @@ export class ClickLinkGesture extends Disposable { this._editor = editor; this._opts = createOptions(this._editor.getOption(EditorOption.multiCursorModifier)); - this.lastMouseMoveEvent = null; - this.hasTriggerKeyOnMouseDown = false; + this._lastMouseMoveEvent = null; + this._hasTriggerKeyOnMouseDown = false; + this._lineNumberOnMouseDown = 0; this._register(this._editor.onDidChangeConfiguration((e) => { if (e.hasChanged(EditorOption.multiCursorModifier)) { @@ -128,77 +129,80 @@ export class ClickLinkGesture extends Disposable { return; } this._opts = newOpts; - this.lastMouseMoveEvent = null; - this.hasTriggerKeyOnMouseDown = false; + this._lastMouseMoveEvent = null; + this._hasTriggerKeyOnMouseDown = false; + this._lineNumberOnMouseDown = 0; this._onCancel.fire(); } })); - this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts)))); - this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts)))); - this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this.onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts)))); - this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts)))); - this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts)))); - this._register(this._editor.onMouseDrag(() => this.resetHandler())); + this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts)))); + this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts)))); + this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts)))); + this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this._onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts)))); + this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this._onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts)))); + this._register(this._editor.onMouseDrag(() => this._resetHandler())); - this._register(this._editor.onDidChangeCursorSelection((e) => this.onDidChangeCursorSelection(e))); - this._register(this._editor.onDidChangeModel((e) => this.resetHandler())); - this._register(this._editor.onDidChangeModelContent(() => this.resetHandler())); + this._register(this._editor.onDidChangeCursorSelection((e) => this._onDidChangeCursorSelection(e))); + this._register(this._editor.onDidChangeModel((e) => this._resetHandler())); + this._register(this._editor.onDidChangeModelContent(() => this._resetHandler())); this._register(this._editor.onDidScrollChange((e) => { if (e.scrollTopChanged || e.scrollLeftChanged) { - this.resetHandler(); + this._resetHandler(); } })); } - private onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void { + private _onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void { if (e.selection && e.selection.startColumn !== e.selection.endColumn) { - this.resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/Microsoft/vscode/issues/7827) + this._resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/microsoft/vscode/issues/7827) } } - private onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void { - this.lastMouseMoveEvent = mouseEvent; + private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void { + this._lastMouseMoveEvent = mouseEvent; this._onMouseMoveOrRelevantKeyDown.fire([mouseEvent, null]); } - private onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void { + private _onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void { // We need to record if we had the trigger key on mouse down because someone might select something in the editor // holding the mouse down and then while mouse is down start to press Ctrl/Cmd to start a copy operation and then // release the mouse button without wanting to do the navigation. // With this flag we prevent goto definition if the mouse was down before the trigger key was pressed. - this.hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier; + this._hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier; + this._lineNumberOnMouseDown = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0; } - private onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void { - if (this.hasTriggerKeyOnMouseDown) { + private _onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void { + const currentLineNumber = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0; + if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber) { this._onExecute.fire(mouseEvent); } } - private onEditorKeyDown(e: ClickLinkKeyboardEvent): void { + private _onEditorKeyDown(e: ClickLinkKeyboardEvent): void { if ( - this.lastMouseMoveEvent + this._lastMouseMoveEvent && ( e.keyCodeIsTriggerKey // User just pressed Ctrl/Cmd (normal goto definition) || (e.keyCodeIsSideBySideKey && e.hasTriggerModifier) // User pressed Ctrl/Cmd+Alt (goto definition to the side) ) ) { - this._onMouseMoveOrRelevantKeyDown.fire([this.lastMouseMoveEvent, e]); + this._onMouseMoveOrRelevantKeyDown.fire([this._lastMouseMoveEvent, e]); } else if (e.hasTriggerModifier) { this._onCancel.fire(); // remove decorations if user holds another key with ctrl/cmd to prevent accident goto declaration } } - private onEditorKeyUp(e: ClickLinkKeyboardEvent): void { + private _onEditorKeyUp(e: ClickLinkKeyboardEvent): void { if (e.keyCodeIsTriggerKey) { this._onCancel.fire(); } } - private resetHandler(): void { - this.lastMouseMoveEvent = null; - this.hasTriggerKeyOnMouseDown = false; + private _resetHandler(): void { + this._lastMouseMoveEvent = null; + this._hasTriggerKeyOnMouseDown = false; this._onCancel.fire(); } } diff --git a/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts index d3678e527d9..68c3275a7a4 100644 --- a/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts @@ -27,6 +27,10 @@ import { IWordAtPosition, IModelDeltaDecoration, ITextModel, IFoundBracket } fro import { Position } from 'vs/editor/common/core/position'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { PeekContext } from 'vs/editor/contrib/peekView/peekView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class GotoDefinitionAtPositionEditorContribution implements IEditorContribution { @@ -334,8 +338,16 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri private gotoDefinition(position: Position, openToSide: boolean): Promise { this.editor.setPosition(position); - const action = new DefinitionAction({ openToSide, openInPeek: false, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined }); - return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor)); + return this.editor.invokeWithinContext((accessor) => { + const canPeek = !openToSide && this.editor.getOption(EditorOption.definitionLinkOpensInPeek) && !this.isInPeekEditor(accessor); + const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined }); + return action.run(accessor, this.editor); + }); + } + + private isInPeekEditor(accessor: ServicesAccessor): boolean | undefined { + const contextKeyService = accessor.get(IContextKeyService); + return PeekContext.inPeekEditor.getValue(contextKeyService); } public dispose(): void { diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index 7678aab4664..0470ced7334 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -64,8 +64,8 @@ export abstract class ReferencesController implements IEditorContribution { dispose(): void { this._referenceSearchVisible.reset(); this._disposables.dispose(); - dispose(this._widget); - dispose(this._model); + this._widget?.dispose(); + this._model?.dispose(); this._widget = undefined; this._model = undefined; } @@ -117,17 +117,17 @@ export abstract class ReferencesController implements IEditorContribution { if (event.source !== 'editor' || !this._configurationService.getValue('editor.stablePeek')) { // when stable peek is configured we don't close // the peek window on selecting the editor - this.openReference(element, false); + this.openReference(element, false, false); } break; case 'side': - this.openReference(element, true); + this.openReference(element, true, false); break; case 'goto': if (peekMode) { this._gotoReference(element); } else { - this.openReference(element, false); + this.openReference(element, false, true); } break; } @@ -216,14 +216,25 @@ export abstract class ReferencesController implements IEditorContribution { } } - closeWidget(): void { + async revealReference(reference: OneReference): Promise { + if (!this._editor.hasModel() || !this._model || !this._widget) { + // can be called while still resolving... + return; + } + + await this._widget.revealReference(reference); + } + + closeWidget(focusEditor = true): void { + this._widget?.dispose(); + this._model?.dispose(); this._referenceSearchVisible.reset(); this._disposables.clear(); - dispose(this._widget); - dispose(this._model); this._widget = undefined; this._model = undefined; - this._editor.focus(); + if (focusEditor) { + this._editor.focus(); + } this._requestIdPool += 1; // Cancel pending requests } @@ -274,7 +285,7 @@ export abstract class ReferencesController implements IEditorContribution { }); } - openReference(ref: Location, sideBySide: boolean): void { + openReference(ref: Location, sideBySide: boolean, pinned: boolean): void { // clear stage if (!sideBySide) { this.closeWidget(); @@ -283,7 +294,7 @@ export abstract class ReferencesController implements IEditorContribution { const { uri, range } = ref; this._editorService.openCodeEditor({ resource: uri, - options: { selection: range } + options: { selection: range, pinned } }, this._editor, sideBySide); } } @@ -300,7 +311,7 @@ function withController(accessor: ServicesAccessor, fn: (controller: ReferencesC } KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'changePeekFocus', + id: 'togglePeekWidgetFocus', weight: KeybindingWeight.EditorContrib, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.F2), when: ContextKeyExpr.or(ctxReferenceSearchVisible, PeekContext.inPeekEditor), @@ -356,13 +367,31 @@ KeybindingsRegistry.registerKeybindingRule({ }); KeybindingsRegistry.registerKeybindingRule({ id: 'closeReferenceSearch', - weight: KeybindingWeight.WorkbenchContrib + 50, + weight: KeybindingWeight.EditorContrib + 50, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')) }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'revealReference', + weight: KeybindingWeight.EditorContrib, + primary: KeyCode.Enter, + mac: { + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] + }, + when: ContextKeyExpr.and(ctxReferenceSearchVisible, WorkbenchListFocusContextKey), + handler(accessor: ServicesAccessor) { + const listService = accessor.get(IListService); + const focus = listService.lastFocusedList?.getFocus(); + if (Array.isArray(focus) && focus[0] instanceof OneReference) { + withController(accessor, controller => controller.revealReference(focus[0])); + } + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'openReferenceToSide', weight: KeybindingWeight.EditorContrib, @@ -375,7 +404,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const listService = accessor.get(IListService); const focus = listService.lastFocusedList?.getFocus(); if (Array.isArray(focus) && focus[0] instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus[0], true)); + withController(accessor, controller => controller.openReference(focus[0], true, true)); } } }); @@ -384,6 +413,6 @@ CommandsRegistry.registerCommand('openReference', (accessor) => { const listService = accessor.get(IListService); const focus = listService.lastFocusedList?.getFocus(); if (Array.isArray(focus) && focus[0] instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus[0], false)); + withController(accessor, controller => controller.openReference(focus[0], false, true)); } }); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts index 1b93c77febb..f448805d288 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts @@ -17,7 +17,7 @@ import { getBaseLabel } from 'vs/base/common/labels'; import { dirname, basename } from 'vs/base/common/resources'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -36,7 +36,7 @@ export class DataSource implements IAsyncDataSource 1) { + if (len > 1) { this.badge.setTitleFormat(localize('referencesCount', "{0} references", len)); } else { this.badge.setTitleFormat(localize('referenceCount', "{0} reference", len)); @@ -174,20 +171,19 @@ class OneReferenceTemplate { } set(element: OneReference, score?: FuzzyScore): void { - const filePreview = element.parent.preview; - const preview = filePreview && filePreview.preview(element.range); - if (!preview) { - // this means we FAILED to resolve the document... + const preview = element.parent.getPreview(element)?.preview(element.range); + if (!preview || !preview.value) { + // this means we FAILED to resolve the document or the value is the empty string this.label.set(`${basename(element.uri)}:${element.range.startLineNumber + 1}:${element.range.startColumn + 1}`); } else { // render search match as highlight unless // we have score, then render the score const { value, highlight } = preview; if (score && !FuzzyScore.isDefault(score)) { - dom.toggleClass(this.label.element, 'referenceMatch', false); + this.label.element.classList.toggle('referenceMatch', false); this.label.set(value, createMatches(score)); } else { - dom.toggleClass(this.label.element, 'referenceMatch', true); + this.label.element.classList.toggle('referenceMatch', true); this.label.set(value, [highlight]); } } @@ -213,7 +209,11 @@ export class OneReferenceRenderer implements ITreeRenderer { +export class AccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return localize('treeAriaLabel', "References"); + } getAriaLabel(element: FileReferences | OneReference): string | null { return element.ariaMessage; diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index f4d1f08555b..ad343d031b4 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -6,13 +6,12 @@ import 'vs/css!./referencesWidget'; import * as dom from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { GestureEvent } from 'vs/base/browser/touch'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { basenameOrAuthority, dirname, isEqual } from 'vs/base/common/resources'; +import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -22,17 +21,20 @@ import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/ import { ModelDecorationOptions, TextModel } from 'vs/editor/common/model/textModel'; import { Location } from 'vs/editor/common/modes'; import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; -import { AriaProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement, StringRepresentationProvider, IdentityProvider } from 'vs/editor/contrib/gotoSymbol/peek/referencesTree'; +import { AccessibilityProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement, StringRepresentationProvider, IdentityProvider } from 'vs/editor/contrib/gotoSymbol/peek/referencesTree'; import * as nls from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchAsyncDataTree, IWorkbenchAsyncDataTreeOptions } from 'vs/platform/list/browser/listService'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import * as peekView from 'vs/editor/contrib/peekView/peekView'; import { FileReferences, OneReference, ReferencesModel } from '../referencesModel'; import { FuzzyScore } from 'vs/base/common/filters'; import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { KeyCode } from 'vs/base/common/keyCodes'; class DecorationsManager implements IDisposable { @@ -61,12 +63,13 @@ class DecorationsManager implements IDisposable { private _onModelChanged(): void { this._callOnModelChange.clear(); const model = this._editor.getModel(); - if (model) { - for (const ref of this._model.groups) { - if (isEqual(ref.uri, model.uri)) { - this._addDecorations(ref); - return; - } + if (!model) { + return; + } + for (let ref of this._model.references) { + if (ref.uri.toString() === model.uri.toString()) { + this._addDecorations(ref.parent); + return; } } } @@ -75,7 +78,7 @@ class DecorationsManager implements IDisposable { if (!this._editor.hasModel()) { return; } - this._callOnModelChange.add(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged())); + this._callOnModelChange.add(this._editor.getModel().onDidChangeDecorations(() => this._onDecorationChanged())); const newDecorations: IModelDeltaDecoration[] = []; const newDecorationsActualIndex: number[] = []; @@ -85,6 +88,9 @@ class DecorationsManager implements IDisposable { if (this._decorationIgnoreSet.has(oneReference.id)) { continue; } + if (oneReference.uri.toString() !== this._editor.getModel().uri.toString()) { + continue; + } newDecorations.push({ range: oneReference.range, options: DecorationsManager.DecorationOptions @@ -106,19 +112,21 @@ class DecorationsManager implements IDisposable { return; } - this._decorations.forEach((reference, decorationId) => { + for (let [decorationId, reference] of this._decorations) { + const newRange = model.getDecorationRange(decorationId); if (!newRange) { - return; + continue; } let ignore = false; - if (Range.equalsRange(newRange, reference.range)) { - return; + continue; - } else if (Range.spansMultipleLines(newRange)) { + } + + if (Range.spansMultipleLines(newRange)) { ignore = true; } else { @@ -136,7 +144,7 @@ class DecorationsManager implements IDisposable { } else { reference.range = newRange; } - }); + } for (let i = 0, len = toRemove.length; i < len; i++) { this._decorations.delete(toRemove[i]); @@ -145,11 +153,7 @@ class DecorationsManager implements IDisposable { } removeDecorations(): void { - let toRemove: string[] = []; - this._decorations.forEach((value, key) => { - toRemove.push(key); - }); - this._editor.deltaDecorations(toRemove, []); + this._editor.deltaDecorations([...this._decorations.keys()], []); this._decorations.clear(); } } @@ -181,6 +185,8 @@ export interface SelectionEvent { readonly element?: Location; } +class ReferencesTree extends WorkbenchAsyncDataTree { } + /** * ZoneWidget that is shown inside the editor */ @@ -195,7 +201,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { private readonly _onDidSelectReference = new Emitter(); readonly onDidSelectReference = this._onDidSelectReference.event; - private _tree!: WorkbenchAsyncDataTree; + private _tree!: ReferencesTree; private _treeContainer!: HTMLElement; private _splitView!: SplitView; private _preview!: ICodeEditor; @@ -203,7 +209,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { private _previewNotAvailableMessage!: TextModel; private _previewContainer!: HTMLElement; private _messageContainer!: HTMLElement; - private _dim: dom.Dimension = { height: 0, width: 0 }; + private _dim = new dom.Dimension(0, 0); constructor( editor: ICodeEditor, @@ -213,12 +219,14 @@ export class ReferenceWidget extends peekView.PeekViewWidget { @ITextModelService private readonly _textModelResolverService: ITextModelService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @peekView.IPeekViewService private readonly _peekViewService: peekView.IPeekViewService, - @ILabelService private readonly _uriLabel: ILabelService + @ILabelService private readonly _uriLabel: ILabelService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { - super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }); + super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }, _instantiationService); - this._applyTheme(themeService.getTheme()); - this._callOnDispose.add(themeService.onThemeChange(this._applyTheme.bind(this))); + this._applyTheme(themeService.getColorTheme()); + this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this._peekViewService.addExclusiveWidget(editor, this); this.create(); } @@ -235,7 +243,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { super.dispose(); } - private _applyTheme(theme: ITheme) { + private _applyTheme(theme: IColorTheme) { const borderColor = theme.getColor(peekView.peekViewBorder) || Color.transparent; this.style({ arrowColor: borderColor, @@ -302,22 +310,32 @@ export class ReferenceWidget extends peekView.PeekViewWidget { }; this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, this._previewContainer, options, this.editor); dom.hide(this._previewContainer); - this._previewNotAvailableMessage = TextModel.createFromString(nls.localize('missingPreviewMessage', "no preview available")); + this._previewNotAvailableMessage = new TextModel(nls.localize('missingPreviewMessage', "no preview available"), TextModel.DEFAULT_CREATION_OPTIONS, null, null, this._undoRedoService); // tree this._treeContainer = dom.append(containerElement, dom.$('div.ref-tree.inline')); const treeOptions: IWorkbenchAsyncDataTreeOptions = { - ariaLabel: nls.localize('treeAriaLabel', "References"), keyboardSupport: this._defaultTreeKeyboardSupport, - accessibilityProvider: new AriaProvider(), + accessibilityProvider: new AccessibilityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider), identityProvider: new IdentityProvider(), + openOnSingleClick: true, + openOnFocus: true, overrideStyles: { listBackground: peekView.peekViewResultsBackground } }; - this._tree = this._instantiationService.createInstance>( - WorkbenchAsyncDataTree, + if (this._defaultTreeKeyboardSupport) { + // the tree will consume `Escape` and prevent the widget from closing + this._callOnDispose.add(dom.addStandardDisposableListener(this._treeContainer, 'keydown', (e) => { + if (e.equals(KeyCode.Escape)) { + this._keybindingService.dispatchEvent(e, e.target); + e.stopPropagation(); + } + }, true)); + } + this._tree = this._instantiationService.createInstance( + ReferencesTree, 'ReferencesWidget', this._treeContainer, new Delegate(), @@ -367,22 +385,13 @@ export class ReferenceWidget extends peekView.PeekViewWidget { this._onDidSelectReference.fire({ element, kind, source: 'tree' }); } }; - this._tree.onDidChangeFocus(e => { - onEvent(e.elements[0], 'show'); - }); this._tree.onDidOpen(e => { - if (e.browserEvent instanceof MouseEvent && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey)) { - // modifier-click -> open to the side - onEvent(e.elements[0], 'side'); - } else if (e.browserEvent instanceof KeyboardEvent || (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2) || (e.browserEvent).tapCount === 2) { - // keybinding (list service command) - // OR double click - // OR double tap - // -> close widget and goto target - onEvent(e.elements[0], 'goto'); + if (e.sideBySide) { + onEvent(e.element, 'side'); + } else if (e.editorOptions.pinned) { + onEvent(e.element, 'goto'); } else { - // preview location - onEvent(e.elements[0], 'show'); + onEvent(e.element, 'show'); } }); @@ -397,7 +406,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void { super._doLayoutBody(heightInPixel, widthInPixel); - this._dim = { height: heightInPixel, width: widthInPixel }; + this._dim = new dom.Dimension(widthInPixel, heightInPixel); this.layoutData.heightInLines = this._viewZone ? this._viewZone.heightInLines : this.layoutData.heightInLines; this._splitView.layout(widthInPixel); this._splitView.resizeView(0, widthInPixel * this.layoutData.ratio); @@ -432,7 +441,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { if (this._model.isEmpty) { this.setTitle(''); - this._messageContainer.innerHTML = nls.localize('noResults', "No results"); + this._messageContainer.innerText = nls.localize('noResults', "No results"); dom.show(this._messageContainer); return Promise.resolve(undefined); } @@ -462,7 +471,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { })); // make sure things are rendered - dom.addClass(this.container!, 'results-loaded'); + this.container!.classList.add('results-loaded'); dom.show(this._treeContainer); dom.show(this._previewContainer); this._splitView.layout(this._dim.width); @@ -484,6 +493,11 @@ export class ReferenceWidget extends peekView.PeekViewWidget { return undefined; } + async revealReference(reference: OneReference): Promise { + await this._revealReference(reference, false); + this._onDidSelectReference.fire({ element: reference, kind: 'goto', source: 'tree' }); + } + private _revealedReference?: OneReference; private async _revealReference(reference: OneReference, revealParent: boolean): Promise { diff --git a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts index fa0f2c14db2..5c4bbe4d021 100644 --- a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts +++ b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; -import { basename } from 'vs/base/common/resources'; +import { basename, extUri } from 'vs/base/common/resources'; import { IDisposable, dispose, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -16,6 +16,8 @@ import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/r import { Position } from 'vs/editor/common/core/position'; import { IMatch } from 'vs/base/common/filters'; import { Constants } from 'vs/base/common/uint'; +import { ResourceMap } from 'vs/base/common/map'; +import { onUnexpectedError } from 'vs/base/common/errors'; export class OneReference { @@ -24,14 +26,11 @@ export class OneReference { constructor( readonly isProviderFirst: boolean, readonly parent: FileReferences, + readonly uri: URI, private _range: IRange, private _rangeCallback: (ref: OneReference) => void ) { } - get uri(): URI { - return this.parent.uri; - } - get range(): IRange { return this._range; } @@ -86,9 +85,7 @@ export class FileReferences implements IDisposable { readonly children: OneReference[] = []; - private _preview?: FilePreview; - private _resolved?: boolean; - private _loadFailure?: any; + private _previews = new ResourceMap(); constructor( readonly parent: ReferencesModel, @@ -96,16 +93,12 @@ export class FileReferences implements IDisposable { ) { } dispose(): void { - dispose(this._preview); - this._preview = undefined; + dispose(this._previews.values()); + this._previews.clear(); } - get preview(): FilePreview | undefined { - return this._preview; - } - - get failure(): any { - return this._loadFailure; + getPreview(child: OneReference): FilePreview | undefined { + return this._previews.get(child.uri); } get ariaMessage(): string { @@ -117,31 +110,22 @@ export class FileReferences implements IDisposable { } } - resolve(textModelResolverService: ITextModelService): Promise { - - if (this._resolved) { - return Promise.resolve(this); + async resolve(textModelResolverService: ITextModelService): Promise { + if (this._previews.size !== 0) { + return this; } - - return Promise.resolve(textModelResolverService.createModelReference(this.uri).then(modelReference => { - const model = modelReference.object; - - if (!model) { - modelReference.dispose(); - throw new Error(); + for (let child of this.children) { + if (this._previews.has(child.uri)) { + continue; } - - this._preview = new FilePreview(modelReference); - this._resolved = true; - return this; - - }, err => { - // something wrong here - this.children.length = 0; - this._resolved = true; - this._loadFailure = err; - return this; - })); + try { + const ref = await textModelResolverService.createModelReference(child.uri); + this._previews.set(child.uri, new FilePreview(ref)); + } catch (err) { + onUnexpectedError(err); + } + } + return this; } } @@ -167,17 +151,20 @@ export class ReferencesModel implements IDisposable { let current: FileReferences | undefined; for (let link of links) { - if (!current || current.uri.toString() !== link.uri.toString()) { + if (!current || !extUri.isEqual(current.uri, link.uri, true)) { // new group current = new FileReferences(this, link.uri); this.groups.push(current); } // append, check for equality first! - if (current.children.length === 0 || !Range.equalsRange(link.range, current.children[current.children.length - 1].range)) { + if (current.children.length === 0 || ReferencesModel._compareReferences(link, current.children[current.children.length - 1]) !== 0) { const oneRef = new OneReference( - providersFirst === link, current, link.targetSelectionRange || link.range, + providersFirst === link, + current, + link.uri, + link.targetSelectionRange || link.range, ref => this._onDidChangeReferenceRange.fire(ref) ); this.references.push(oneRef); @@ -294,6 +281,6 @@ export class ReferencesModel implements IDisposable { } private static _compareReferences(a: Location, b: Location): number { - return strings.compare(a.uri.toString(), b.uri.toString()) || Range.compareRangesUsingStarts(a.range, b.range); + return extUri.compare(a.uri, b.uri) || Range.compareRangesUsingStarts(a.range, b.range); } } diff --git a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts index 879f3b9aaf9..915aae90522 100644 --- a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts @@ -19,13 +19,14 @@ import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isEqual } from 'vs/base/common/resources'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; export const ctxHasSymbols = new RawContextKey('hasSymbols', false); export const ISymbolNavigationService = createDecorator('ISymbolNavigationService'); export interface ISymbolNavigationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; reset(): void; put(anchor: OneReference): void; revealNext(source: ICodeEditor): Promise; @@ -33,7 +34,7 @@ export interface ISymbolNavigationService { class SymbolNavigationService implements ISymbolNavigationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _ctxHasSymbols: IContextKey; @@ -54,8 +55,8 @@ class SymbolNavigationService implements ISymbolNavigationService { reset(): void { this._ctxHasSymbols.reset(); - dispose(this._currentState); - dispose(this._currentMessage); + this._currentState?.dispose(); + this._currentMessage?.dispose(); this._currentModel = undefined; this._currentIdx = -1; } @@ -127,7 +128,7 @@ class SymbolNavigationService implements ISymbolNavigationService { resource: reference.uri, options: { selection: Range.collapseToStart(reference.range), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport } }, source).finally(() => { this._ignoreEditorChange = false; @@ -137,7 +138,7 @@ class SymbolNavigationService implements ISymbolNavigationService { private _showMessage(): void { - dispose(this._currentMessage); + this._currentMessage?.dispose(); const kb = this._keybindingService.lookupKeybinding('editor.gotoNextSymbolFromResult'); const message = kb @@ -197,7 +198,7 @@ class EditorState { dispose(): void { this._disposables.dispose(); this._onDidChange.dispose(); - this._listener.forEach(dispose); + dispose(this._listener.values()); } private _onDidAddEditor(editor: ICodeEditor): void { @@ -208,7 +209,7 @@ class EditorState { } private _onDidRemoveEditor(editor: ICodeEditor): void { - dispose(this._listener.get(editor)); + this._listener.get(editor)?.dispose(); this._listener.delete(editor); } } diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css deleted file mode 100644 index 8f35b9aec75..00000000000 --- a/src/vs/editor/contrib/hover/hover.css +++ /dev/null @@ -1,129 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-editor-hover { - cursor: default; - position: absolute; - overflow: hidden; - z-index: 50; - user-select: text; - -webkit-user-select: text; - -ms-user-select: text; - box-sizing: initial; - animation: fadein 100ms linear; - line-height: 1.5em; -} - -.monaco-editor-hover.hidden { - display: none; -} - -.monaco-editor-hover .hover-contents { - padding: 4px 8px; -} - -.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { - max-width: 500px; - word-wrap: break-word; -} - -.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr { - min-width: 100vw; -} - -.monaco-editor-hover p, -.monaco-editor-hover ul { - margin: 8px 0; -} - -.monaco-editor-hover code { - font-family: var(--monaco-monospace-font); -} - -.monaco-editor-hover hr { - margin-top: 4px; - margin-bottom: -6px; - margin-left: -10px; - margin-right: -10px; - height: 1px; -} - -.monaco-editor-hover p:first-child, -.monaco-editor-hover ul:first-child { - margin-top: 0; -} - -.monaco-editor-hover p:last-child, -.monaco-editor-hover ul:last-child { - margin-bottom: 0; -} - -/* MarkupContent Layout */ -.monaco-editor-hover ul { - padding-left: 20px; -} -.monaco-editor-hover ol { - padding-left: 20px; -} - -.monaco-editor-hover li > p { - margin-bottom: 0; -} - -.monaco-editor-hover li > ul { - margin-top: 0; -} - -.monaco-editor-hover code { - border-radius: 3px; - padding: 0 0.4em; -} - -.monaco-editor-hover .monaco-tokenized-source { - white-space: pre-wrap; - word-break: break-all; -} - -.monaco-editor-hover .hover-row.status-bar { - font-size: 12px; - line-height: 22px; -} - -.monaco-editor-hover .hover-row.status-bar .actions { - display: flex; - padding: 0px 8px; -} - -.monaco-editor-hover .hover-row.status-bar .actions .action-container { - margin-right: 16px; - cursor: pointer; -} - -.monaco-editor-hover .hover-row.status-bar .actions .action-container .action .icon { - padding-right: 4px; -} - -.monaco-editor-hover .markdown-hover .hover-contents .codicon { - color: inherit; - font-size: inherit; - vertical-align: middle; -} - -.monaco-editor-hover .hover-contents a.code-link:before { - content: '('; -} -.monaco-editor-hover .hover-contents a.code-link:after { - content: ')'; -} - -.monaco-editor-hover .hover-contents a.code-link { - color: inherit; -} -.monaco-editor-hover .hover-contents a.code-link > span { - text-decoration: underline; - /** Hack to force underline to show **/ - border-bottom: 1px solid transparent; - text-underline-position: under; -} diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index b77e2b0132a..92af0fba8a9 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./hover'; import * as nls from 'vs/nls'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -27,7 +26,7 @@ import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDeco import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { GotoDefinitionAtPositionEditorContribution } from 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; export class ModesHoverController implements IEditorContribution { @@ -58,6 +57,8 @@ export class ModesHoverController implements IEditorContribution { private _isHoverEnabled!: boolean; private _isHoverSticky!: boolean; + private _hoverVisibleKey: IContextKey; + static get(editor: ICodeEditor): ModesHoverController { return editor.getContribution(ModesHoverController.ID); } @@ -68,7 +69,7 @@ export class ModesHoverController implements IEditorContribution { @IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @IThemeService private readonly _themeService: IThemeService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IContextKeyService _contextKeyService: IContextKeyService ) { this._isMouseDown = false; this._hoverClicked = false; @@ -77,11 +78,12 @@ export class ModesHoverController implements IEditorContribution { this._didChangeConfigurationHandler = this._editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => { if (e.hasChanged(EditorOption.hover)) { - this._hideWidgets(); this._unhookEvents(); this._hookEvents(); } }); + + this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(_contextKeyService); } private _hookEvents(): void { @@ -97,7 +99,8 @@ export class ModesHoverController implements IEditorContribution { this._toUnhook.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); this._toUnhook.add(this._editor.onDidChangeModelDecorations(() => this._onModelDecorationsChanged())); } else { - this._toUnhook.add(this._editor.onMouseMove(hideWidgetsEventHandler)); + this._toUnhook.add(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); + this._toUnhook.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); } this._toUnhook.add(this._editor.onMouseLeave(hideWidgetsEventHandler)); @@ -177,7 +180,17 @@ export class ModesHoverController implements IEditorContribution { this.glyphWidget.hide(); if (this._isHoverEnabled && mouseEvent.target.range) { - this.contentWidget.startShowingAt(mouseEvent.target.range, HoverStartMode.Delayed, false); + // TODO@rebornix. This should be removed if we move Color Picker out of Hover component. + // Check if mouse is hovering on color decorator + const hoverOnColorDecorator = [...mouseEvent.target.element?.classList.values() || []].find(className => className.startsWith('ced-colorBox')) + && mouseEvent.target.range.endColumn - mouseEvent.target.range.startColumn === 1; + if (hoverOnColorDecorator) { + // shift the mouse focus by one as color decorator is a `before` decoration of next character. + this.contentWidget.startShowingAt(new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1), HoverStartMode.Delayed, false); + } else { + this.contentWidget.startShowingAt(mouseEvent.target.range, HoverStartMode.Delayed, false); + } + } } else if (targetType === MouseTargetType.GUTTER_GLYPH_MARGIN) { this.contentWidget.hide(); @@ -207,7 +220,7 @@ export class ModesHoverController implements IEditorContribution { } private _createHoverWidgets() { - this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService, this._configurationService); + this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._hoverVisibleKey, this._markerDecorationsService, this._keybindingService, this._themeService, this._modeService, this._openerService); this._glyphWidget.value = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService); } @@ -314,29 +327,29 @@ registerThemingParticipant((theme, collector) => { } const hoverBackground = theme.getColor(editorHoverBackground); if (hoverBackground) { - collector.addRule(`.monaco-editor .monaco-editor-hover { background-color: ${hoverBackground}; }`); + collector.addRule(`.monaco-editor .monaco-hover { background-color: ${hoverBackground}; }`); } const hoverBorder = theme.getColor(editorHoverBorder); if (hoverBorder) { - collector.addRule(`.monaco-editor .monaco-editor-hover { border: 1px solid ${hoverBorder}; }`); - collector.addRule(`.monaco-editor .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); - collector.addRule(`.monaco-editor .monaco-editor-hover hr { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); - collector.addRule(`.monaco-editor .monaco-editor-hover hr { border-bottom: 0px solid ${hoverBorder.transparent(0.5)}; }`); + collector.addRule(`.monaco-editor .monaco-hover { border: 1px solid ${hoverBorder}; }`); + collector.addRule(`.monaco-editor .monaco-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); + collector.addRule(`.monaco-editor .monaco-hover hr { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); + collector.addRule(`.monaco-editor .monaco-hover hr { border-bottom: 0px solid ${hoverBorder.transparent(0.5)}; }`); } const link = theme.getColor(textLinkForeground); if (link) { - collector.addRule(`.monaco-editor .monaco-editor-hover a { color: ${link}; }`); + collector.addRule(`.monaco-editor .monaco-hover a { color: ${link}; }`); } const hoverForeground = theme.getColor(editorHoverForeground); if (hoverForeground) { - collector.addRule(`.monaco-editor .monaco-editor-hover { color: ${hoverForeground}; }`); + collector.addRule(`.monaco-editor .monaco-hover { color: ${hoverForeground}; }`); } const actionsBackground = theme.getColor(editorHoverStatusBarBackground); if (actionsBackground) { - collector.addRule(`.monaco-editor .monaco-editor-hover .hover-row .actions { background-color: ${actionsBackground}; }`); + collector.addRule(`.monaco-editor .monaco-hover .hover-row .actions { background-color: ${actionsBackground}; }`); } const codeBackground = theme.getColor(textCodeBlockBackground); if (codeBackground) { - collector.addRule(`.monaco-editor .monaco-editor-hover code { background-color: ${codeBackground}; }`); + collector.addRule(`.monaco-editor .monaco-hover code { background-color: ${codeBackground}; }`); } }); diff --git a/src/vs/editor/contrib/hover/hoverOperation.ts b/src/vs/editor/contrib/hover/hoverOperation.ts index 46195dbeb5b..b1c73fad4a3 100644 --- a/src/vs/editor/contrib/hover/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/hoverOperation.ts @@ -143,9 +143,7 @@ export class HoverOperation { } private _onComplete(value: Result): void { - if (this._completeCallback) { - this._completeCallback(value); - } + this._completeCallback(value); } private _onError(error: any): void { @@ -157,9 +155,7 @@ export class HoverOperation { } private _onProgress(value: Result): void { - if (this._progressCallback) { - this._progressCallback(value); - } + this._progressCallback(value); } public start(mode: HoverStartMode): void { diff --git a/src/vs/editor/contrib/hover/hoverWidgets.ts b/src/vs/editor/contrib/hover/hoverWidgets.ts index 9d55f8340a6..cb8c02807c3 100644 --- a/src/vs/editor/contrib/hover/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/hoverWidgets.ts @@ -3,27 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toggleClass } from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import { renderHoverAction, HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; export class ContentHoverWidget extends Widget implements IContentWidget { + protected readonly _hover: HoverWidget; private readonly _id: string; protected _editor: ICodeEditor; private _isVisible: boolean; - private readonly _containerDomNode: HTMLElement; - protected readonly _domNode: HTMLElement; protected _showAtPosition: Position | null; protected _showAtRange: Range | null; private _stoleFocus: boolean; - private readonly scrollbar: DomScrollableElement; // Editor.IContentWidget.allowEditorOverflow public allowEditorOverflow = true; @@ -34,28 +34,24 @@ export class ContentHoverWidget extends Widget implements IContentWidget { protected set isVisible(value: boolean) { this._isVisible = value; - toggleClass(this._containerDomNode, 'hidden', !this._isVisible); + this._hover.containerDomNode.classList.toggle('hidden', !this._isVisible); } - constructor(id: string, editor: ICodeEditor) { + constructor( + id: string, + editor: ICodeEditor, + private readonly _hoverVisibleKey: IContextKey, + private readonly _keybindingService: IKeybindingService + ) { super(); + + this._hover = this._register(new HoverWidget()); this._id = id; this._editor = editor; this._isVisible = false; this._stoleFocus = false; - this._containerDomNode = document.createElement('div'); - this._containerDomNode.className = 'monaco-editor-hover hidden'; - this._containerDomNode.tabIndex = 0; - - this._domNode = document.createElement('div'); - this._domNode.className = 'monaco-editor-hover-content'; - - this.scrollbar = new DomScrollableElement(this._domNode, {}); - this._register(this.scrollbar); - this._containerDomNode.appendChild(this.scrollbar.getDomNode()); - - this.onkeydown(this._containerDomNode, (e: IKeyboardEvent) => { + this.onkeydown(this._hover.containerDomNode, (e: IKeyboardEvent) => { if (e.equals(KeyCode.Escape)) { this.hide(); } @@ -81,13 +77,14 @@ export class ContentHoverWidget extends Widget implements IContentWidget { } public getDomNode(): HTMLElement { - return this._containerDomNode; + return this._hover.containerDomNode; } public showAt(position: Position, range: Range | null, focus: boolean): void { // Position has changed this._showAtPosition = position; this._showAtRange = range; + this._hoverVisibleKey.set(true); this.isVisible = true; this._editor.layoutContentWidget(this); @@ -96,7 +93,7 @@ export class ContentHoverWidget extends Widget implements IContentWidget { this._editor.render(); this._stoleFocus = focus; if (focus) { - this._containerDomNode.focus(); + this._hover.containerDomNode.focus(); } } @@ -105,6 +102,12 @@ export class ContentHoverWidget extends Widget implements IContentWidget { return; } + setTimeout(() => { + // Give commands a chance to see the key + if (!this.isVisible) { + this._hoverVisibleKey.set(false); + } + }, 0); this.isVisible = false; this._editor.layoutContentWidget(this); @@ -133,31 +136,33 @@ export class ContentHoverWidget extends Widget implements IContentWidget { } private updateFont(): void { - const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._domNode.getElementsByClassName('code')); + const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._hover.contentsDomNode.getElementsByClassName('code')); codeClasses.forEach(node => this._editor.applyFontInfo(node)); } protected updateContents(node: Node): void { - this._domNode.textContent = ''; - this._domNode.appendChild(node); + this._hover.contentsDomNode.textContent = ''; + this._hover.contentsDomNode.appendChild(node); this.updateFont(); this._editor.layoutContentWidget(this); - this.onContentsChange(); + this._hover.onContentsChanged(); } - protected onContentsChange(): void { - this.scrollbar.scanDomNode(); + protected _renderAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }): IDisposable { + const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId); + const keybindingLabel = keybinding ? keybinding.getLabel() : null; + return renderHoverAction(parent, actionOptions, keybindingLabel); } private layout(): void { const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); const { fontSize, lineHeight } = this._editor.getOption(EditorOption.fontInfo); - this._domNode.style.fontSize = `${fontSize}px`; - this._domNode.style.lineHeight = `${lineHeight}px`; - this._domNode.style.maxHeight = `${height}px`; - this._domNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`; + this._hover.contentsDomNode.style.fontSize = `${fontSize}px`; + this._hover.contentsDomNode.style.lineHeight = `${lineHeight}px`; + this._hover.contentsDomNode.style.maxHeight = `${height}px`; + this._hover.contentsDomNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`; } } @@ -176,9 +181,9 @@ export class GlyphHoverWidget extends Widget implements IOverlayWidget { this._isVisible = false; this._domNode = document.createElement('div'); - this._domNode.className = 'monaco-editor-hover hidden'; + this._domNode.className = 'monaco-hover hidden'; this._domNode.setAttribute('aria-hidden', 'true'); - this._domNode.setAttribute('role', 'presentation'); + this._domNode.setAttribute('role', 'tooltip'); this._showAtLineNumber = -1; @@ -197,7 +202,7 @@ export class GlyphHoverWidget extends Widget implements IOverlayWidget { protected set isVisible(value: boolean) { this._isVisible = value; - toggleClass(this._domNode, 'hidden', !this._isVisible); + this._domNode.classList.toggle('hidden', !this._isVisible); } public getId(): string { diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index c52183db542..2cb7e7938c5 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -21,7 +21,7 @@ import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/colorPickerWidg import { getHover } from 'vs/editor/contrib/hover/getHover'; import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation'; import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { coalesce, isNonEmptyArray, asArray } from 'vs/base/common/arrays'; import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; @@ -39,9 +39,9 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Constants } from 'vs/base/common/uint'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { OperatingSystem, OS } from 'vs/base/common/platform'; import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { Progress } from 'vs/platform/progress/common/progress'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; const $ = dom.$; @@ -194,8 +194,6 @@ const markerCodeActionTrigger: CodeActionTrigger = { filter: { include: CodeActionKind.QuickFix } }; -type ModifierKey = 'meta' | 'ctrl' | 'alt'; - export class ModesContentHoverWidget extends ContentHoverWidget { static readonly ID = 'editor.contrib.modesContentHoverWidget'; @@ -209,21 +207,20 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private _shouldFocus: boolean; private _colorPicker: ColorPickerWidget | null; - private _clickModifierKey: ModifierKey; private _codeLink?: HTMLElement; private readonly renderDisposable = this._register(new MutableDisposable()); constructor( editor: ICodeEditor, + _hoverVisibleKey: IContextKey, markerDecorationsService: IMarkerDecorationsService, + keybindingService: IKeybindingService, private readonly _themeService: IThemeService, - private readonly _keybindingService: IKeybindingService, private readonly _modeService: IModeService, private readonly _openerService: IOpenerService = NullOpenerService, - private readonly _configurationService: IConfigurationService ) { - super(ModesContentHoverWidget.ID, editor); + super(ModesContentHoverWidget.ID, editor, _hoverVisibleKey, keybindingService); this._messages = []; this._lastRange = null; @@ -243,31 +240,21 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.FOCUS, () => { if (this._colorPicker) { - dom.addClass(this.getDomNode(), 'colorpicker-hover'); + this.getDomNode().classList.add('colorpicker-hover'); } })); this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.BLUR, () => { - dom.removeClass(this.getDomNode(), 'colorpicker-hover'); + this.getDomNode().classList.remove('colorpicker-hover'); })); this._register(editor.onDidChangeConfiguration((e) => { this._hoverOperation.setHoverTime(this._editor.getOption(EditorOption.hover).delay); })); this._register(TokenizationRegistry.onDidChange((e) => { if (this.isVisible && this._lastRange && this._messages.length > 0) { - this._domNode.textContent = ''; + this._hover.contentsDomNode.textContent = ''; this._renderMessages(this._lastRange, this._messages); } })); - - this._clickModifierKey = this._getClickModifierKey(); - this._register((this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('editor.multiCursorModifier')) { - this._clickModifierKey = this._getClickModifierKey(); - if (this._codeLink) { - this._codeLink.setAttribute('title', this._getCodelinkTooltip()); - } - } - }))); } dispose(): void { @@ -473,10 +460,10 @@ export class ModesContentHoverWidget extends ContentHoverWidget { .forEach(contents => { const markdownHoverElement = $('div.hover-row.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); - const renderer = markdownDisposeables.add(new MarkdownRenderer(this._editor, this._modeService, this._openerService)); + const renderer = markdownDisposeables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService)); markdownDisposeables.add(renderer.onDidRenderCodeBlock(() => { hoverContentsElement.className = 'hover-contents code-hover-contents'; - this.onContentsChange(); + this._hover.onContentsChanged(); })); const renderedContents = markdownDisposeables.add(renderer.render(contents)); hoverContentsElement.appendChild(renderedContents.element); @@ -519,37 +506,33 @@ export class ModesContentHoverWidget extends ContentHoverWidget { messageElement.innerText = message; if (source || code) { - if (typeof code === 'string') { + // Code has link + if (code && typeof code !== 'string') { + const sourceAndCodeElement = $('span'); + if (source) { + const sourceElement = dom.append(sourceAndCodeElement, $('span')); + sourceElement.innerText = source; + } + this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link')); + this._codeLink.setAttribute('href', code.target.toString()); + + this._codeLink.onclick = (e) => { + this._openerService.open(code.target); + e.preventDefault(); + e.stopPropagation(); + }; + + const codeElement = dom.append(this._codeLink, $('span')); + codeElement.innerText = code.value; + + const detailsElement = dom.append(markerElement, sourceAndCodeElement); + detailsElement.style.opacity = '0.6'; + detailsElement.style.paddingLeft = '6px'; + } else { const detailsElement = dom.append(markerElement, $('span')); detailsElement.style.opacity = '0.6'; detailsElement.style.paddingLeft = '6px'; detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`; - } else { - if (code) { - const sourceAndCodeElement = $('span'); - if (source) { - const sourceElement = dom.append(sourceAndCodeElement, $('span')); - sourceElement.innerText = source; - } - this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link')); - this._codeLink.setAttribute('title', this._getCodelinkTooltip()); - this._codeLink.setAttribute('href', code.link.toString()); - - this._codeLink.onclick = (e) => { - e.preventDefault(); - if ((this._clickModifierKey === 'meta' && e.metaKey) || (this._clickModifierKey === 'ctrl' && e.ctrlKey) || (this._clickModifierKey === 'alt' && e.altKey)) { - this._openerService.open(code.link); - e.stopPropagation(); - } - }; - - const codeElement = dom.append(this._codeLink, $('span')); - codeElement.innerText = code.value; - - const detailsElement = dom.append(markerElement, sourceAndCodeElement); - detailsElement.style.opacity = '0.6'; - detailsElement.style.paddingLeft = '6px'; - } } } @@ -581,59 +564,63 @@ export class ModesContentHoverWidget extends ContentHoverWidget { const disposables = new DisposableStore(); const actionsElement = dom.append(hoverElement, $('div.actions')); if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) { - disposables.add(this.renderAction(actionsElement, { + disposables.add(this._renderAction(actionsElement, { label: nls.localize('peek problem', "Peek Problem"), commandId: NextMarkerAction.ID, run: () => { this.hide(); - MarkerController.get(this._editor).show(markerHover.marker); + MarkerController.get(this._editor).showAtMarker(markerHover.marker); this._editor.focus(); } })); } - const quickfixPlaceholderElement = dom.append(actionsElement, $('div')); - quickfixPlaceholderElement.style.opacity = '0'; - quickfixPlaceholderElement.style.transition = 'opacity 0.2s'; - setTimeout(() => quickfixPlaceholderElement.style.opacity = '1', 200); - quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."); - disposables.add(toDisposable(() => quickfixPlaceholderElement.remove())); + if (!this._editor.getOption(EditorOption.readOnly)) { + const quickfixPlaceholderElement = dom.append(actionsElement, $('div')); + quickfixPlaceholderElement.style.opacity = '0'; + quickfixPlaceholderElement.style.transition = 'opacity 0.2s'; + setTimeout(() => quickfixPlaceholderElement.style.opacity = '1', 200); + quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."); + disposables.add(toDisposable(() => quickfixPlaceholderElement.remove())); + const codeActionsPromise = this.getCodeActions(markerHover.marker); + disposables.add(toDisposable(() => codeActionsPromise.cancel())); + codeActionsPromise.then(actions => { + quickfixPlaceholderElement.style.transition = ''; + quickfixPlaceholderElement.style.opacity = '1'; - const codeActionsPromise = this.getCodeActions(markerHover.marker); - disposables.add(toDisposable(() => codeActionsPromise.cancel())); - codeActionsPromise.then(actions => { - quickfixPlaceholderElement.style.transition = ''; - quickfixPlaceholderElement.style.opacity = '1'; - - if (!actions.validActions.length) { - actions.dispose(); - quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); - return; - } - quickfixPlaceholderElement.remove(); - - let showing = false; - disposables.add(toDisposable(() => { - if (!showing) { + if (!actions.validActions.length) { actions.dispose(); + quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); + return; } - })); + quickfixPlaceholderElement.remove(); - disposables.add(this.renderAction(actionsElement, { - label: nls.localize('quick fixes', "Quick Fix..."), - commandId: QuickFixAction.Id, - run: (target) => { - showing = true; - const controller = QuickFixController.get(this._editor); - const elementPosition = dom.getDomNodePagePosition(target); - controller.showCodeActions(markerCodeActionTrigger, actions, { - x: elementPosition.left + 6, - y: elementPosition.top + elementPosition.height + 6 - }); - } - })); - }); + let showing = false; + disposables.add(toDisposable(() => { + if (!showing) { + actions.dispose(); + } + })); + + disposables.add(this._renderAction(actionsElement, { + label: nls.localize('quick fixes', "Quick Fix..."), + commandId: QuickFixAction.Id, + run: (target) => { + showing = true; + const controller = QuickFixController.get(this._editor); + const elementPosition = dom.getDomNodePagePosition(target); + // Hide the hover pre-emptively, otherwise the editor can close the code actions + // context menu as well when using keyboard navigation + this.hide(); + controller.showCodeActions(markerCodeActionTrigger, actions, { + x: elementPosition.left + 6, + y: elementPosition.top + elementPosition.height + 6 + }); + } + })); + }); + } this.renderDisposable.value = disposables; return hoverElement; @@ -645,57 +632,14 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), markerCodeActionTrigger, + Progress.None, cancellationToken); }); } - private renderAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }): IDisposable { - const actionContainer = dom.append(parent, $('div.action-container')); - const action = dom.append(actionContainer, $('a.action')); - if (actionOptions.iconClass) { - dom.append(action, $(`span.icon.${actionOptions.iconClass}`)); - } - const label = dom.append(action, $('span')); - label.textContent = actionOptions.label; - const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId); - if (keybinding) { - label.title = `${actionOptions.label} (${keybinding.getLabel()})`; - } - return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => { - e.stopPropagation(); - e.preventDefault(); - actionOptions.run(actionContainer); - }); - } - private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({ className: 'hoverHighlight' }); - - private _getClickModifierKey(): ModifierKey { - const value = this._configurationService.getValue<'ctrlCmd' | 'alt'>('editor.multiCursorModifier'); - if (value === 'ctrlCmd') { - return 'alt'; - } else { - if (OS === OperatingSystem.Macintosh) { - return 'meta'; - } else { - return 'ctrl'; - } - } - } - - private _getCodelinkTooltip(): string { - const tooltipLabel = nls.localize('links.navigate.follow', 'Follow link'); - const tooltipKeybinding = this._clickModifierKey === 'ctrl' - ? nls.localize('links.navigate.kb.meta', 'ctrl + click') - : - this._clickModifierKey === 'meta' - ? OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.meta.mac', 'cmd + click') : nls.localize('links.navigate.kb.meta', 'ctrl + click') - : OS === OperatingSystem.Macintosh ? nls.localize('links.navigate.kb.alt.mac', 'option + click') : nls.localize('links.navigate.kb.alt', 'alt + click'); - - return `${tooltipLabel} (${tooltipKeybinding})`; - } } function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean { @@ -724,7 +668,6 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean { registerThemingParticipant((theme, collector) => { const linkFg = theme.getColor(textLinkForeground); if (linkFg) { - collector.addRule(`.monaco-editor-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`); + collector.addRule(`.monaco-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`); } }); - diff --git a/src/vs/editor/contrib/hover/modesGlyphHover.ts b/src/vs/editor/contrib/hover/modesGlyphHover.ts index 32c6cf14656..54d0c58c732 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -9,7 +9,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation'; import { GlyphHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { asArray } from 'vs/base/common/arrays'; @@ -104,7 +104,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { this._messages = []; this._lastLineNumber = -1; - this._markdownRenderer = this._register(new MarkdownRenderer(this._editor, modeService, openerService)); + this._markdownRenderer = this._register(new MarkdownRenderer({ editor: this._editor }, modeService, openerService)); this._computer = new MarginComputer(this._editor); this._hoverOperation = new HoverOperation( diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 20280edd604..11c8b67f99a 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -241,7 +241,7 @@ export class ChangeIndentationSizeAction extends EditorAction { } } }); - }, 50/* quick open is sensitive to being opened so soon after another */); + }, 50/* quick input is sensitive to being opened so soon after another */); } } diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index d351f6af863..bc7fd660d84 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -37,12 +37,36 @@ abstract class AbstractCopyLinesAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { + if (!editor.hasModel()) { + return; + } + + const selections = editor.getSelections().map((selection, index) => ({ selection, index, ignore: false })); + selections.sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection)); + + // Remove selections that would result in copying the same line + let prev = selections[0]; + for (let i = 1; i < selections.length; i++) { + const curr = selections[i]; + if (prev.selection.endLineNumber === curr.selection.startLineNumber) { + // these two selections would copy the same line + if (prev.index < curr.index) { + // prev wins + curr.ignore = true; + } else { + // curr wins + prev.ignore = true; + prev = curr; + } + } + } const commands: ICommand[] = []; - const selections = editor.getSelections() || []; - for (const selection of selections) { - commands.push(new CopyLinesCommand(selection, this.down)); + if (selection.ignore) { + continue; + } + commands.push(new CopyLinesCommand(selection.selection, this.down)); } editor.pushUndoStop(); @@ -430,12 +454,12 @@ export class IndentLinesAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - const cursors = editor._getCursors(); - if (!cursors) { + const viewModel = editor._getViewModel(); + if (!viewModel) { return; } editor.pushUndoStop(); - editor.executeCommands(this.id, TypeOperations.indent(cursors.context.config, editor.getModel(), editor.getSelections())); + editor.executeCommands(this.id, TypeOperations.indent(viewModel.cursorConfig, editor.getModel(), editor.getSelections())); editor.pushUndoStop(); } } @@ -476,12 +500,12 @@ export class InsertLineBeforeAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - const cursors = editor._getCursors(); - if (!cursors) { + const viewModel = editor._getViewModel(); + if (!viewModel) { return; } editor.pushUndoStop(); - editor.executeCommands(this.id, TypeOperations.lineInsertBefore(cursors.context.config, editor.getModel(), editor.getSelections())); + editor.executeCommands(this.id, TypeOperations.lineInsertBefore(viewModel.cursorConfig, editor.getModel(), editor.getSelections())); } } @@ -501,12 +525,12 @@ export class InsertLineAfterAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - const cursors = editor._getCursors(); - if (!cursors) { + const viewModel = editor._getViewModel(); + if (!viewModel) { return; } editor.pushUndoStop(); - editor.executeCommands(this.id, TypeOperations.lineInsertAfter(cursors.context.config, editor.getModel(), editor.getSelections())); + editor.executeCommands(this.id, TypeOperations.lineInsertAfter(viewModel.cursorConfig, editor.getModel(), editor.getSelections())); } } @@ -936,7 +960,7 @@ export abstract class AbstractCaseAction extends EditorAction { let selection = selections[i]; if (selection.isEmpty()) { let cursor = selection.getStartPosition(); - let word = model.getWordAtPosition(cursor); + const word = editor.getConfiguredWordAtPosition(cursor); if (!word) { continue; diff --git a/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts index 446c947cf8d..6777693d4ea 100644 --- a/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts @@ -204,7 +204,7 @@ suite('Editor Contrib - Duplicate Selection', () => { const duplicateSelectionAction = new DuplicateSelectionAction(); function testDuplicateSelectionAction(lines: string[], selections: Selection[], expectedLines: string[], expectedSelections: Selection[]): void { - withTestCodeEditor(lines.join('\n'), {}, (editor, cursor) => { + withTestCodeEditor(lines.join('\n'), {}, (editor) => { editor.setSelections(selections); duplicateSelectionAction.run(null!, editor, {}); assert.deepEqual(editor.getValue(), expectedLines.join('\n')); diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index 64b26b23b76..cdcb7f49128 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; @@ -14,6 +13,7 @@ import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import type { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction } from 'vs/editor/browser/editorExtensions'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; function assertSelection(editor: ICodeEditor, expected: Selection | Selection[]): void { if (!Array.isArray(expected)) { @@ -317,7 +317,7 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(1), 'one'); assert.deepEqual(editor.getSelection(), new Selection(1, 1, 1, 1)); - editor.trigger('keyboard', Handler.Undo, {}); + CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Typing some text here on line one'); assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31)); }); @@ -447,7 +447,7 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(1), 'hello my dear world'); assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); - editor.trigger('keyboard', Handler.Undo, {}); + CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'hello my dear'); assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); }); @@ -815,13 +815,13 @@ suite('Editor Contrib - Line Operations', () => { new Selection(2, 4, 2, 4) ]); - editor.trigger('tests', Handler.Undo, {}); + CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(1, 6, 1, 6), new Selection(3, 4, 3, 4) ]); - editor.trigger('tests', Handler.Redo, {}); + CoreEditingCommands.Redo.runEditorCommand(null, editor, null); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(2, 4, 2, 4) @@ -831,39 +831,39 @@ suite('Editor Contrib - Line Operations', () => { }); test('InsertLineBeforeAction', () => { - function testInsertLineBefore(lineNumber: number, column: number, callback: (model: ITextModel, cursor: Cursor) => void): void { + function testInsertLineBefore(lineNumber: number, column: number, callback: (model: ITextModel, viewModel: ViewModel) => void): void { const TEXT = [ 'First line', 'Second line', 'Third line' ]; - withTestCodeEditor(TEXT, {}, (editor, cursor) => { + withTestCodeEditor(TEXT, {}, (editor, viewModel) => { editor.setPosition(new Position(lineNumber, column)); let insertLineBeforeAction = new InsertLineBeforeAction(); executeAction(insertLineBeforeAction, editor); - callback(editor.getModel()!, cursor); + callback(editor.getModel()!, viewModel); }); } - testInsertLineBefore(1, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(1, 1, 1, 1)); + testInsertLineBefore(1, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(1, 1, 1, 1)); assert.equal(model.getLineContent(1), ''); assert.equal(model.getLineContent(2), 'First line'); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineBefore(2, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(2, 1, 2, 1)); + testInsertLineBefore(2, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineBefore(3, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(3, 1, 3, 1)); + testInsertLineBefore(3, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), ''); @@ -872,39 +872,39 @@ suite('Editor Contrib - Line Operations', () => { }); test('InsertLineAfterAction', () => { - function testInsertLineAfter(lineNumber: number, column: number, callback: (model: ITextModel, cursor: Cursor) => void): void { + function testInsertLineAfter(lineNumber: number, column: number, callback: (model: ITextModel, viewModel: ViewModel) => void): void { const TEXT = [ 'First line', 'Second line', 'Third line' ]; - withTestCodeEditor(TEXT, {}, (editor, cursor) => { + withTestCodeEditor(TEXT, {}, (editor, viewModel) => { editor.setPosition(new Position(lineNumber, column)); let insertLineAfterAction = new InsertLineAfterAction(); executeAction(insertLineAfterAction, editor); - callback(editor.getModel()!, cursor); + callback(editor.getModel()!, viewModel); }); } - testInsertLineAfter(1, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(2, 1, 2, 1)); + testInsertLineAfter(1, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineAfter(2, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(3, 1, 3, 1)); + testInsertLineAfter(2, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), ''); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineAfter(3, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(4, 1, 4, 1)); + testInsertLineAfter(3, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(4, 1, 4, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), 'Third line'); @@ -960,6 +960,25 @@ suite('Editor Contrib - Line Operations', () => { model.dispose(); }); + test('Indenting on empty line should move cursor', () => { + const model = createTextModel( + [ + '' + ].join('\n') + ); + + withTestCodeEditor(null, { model: model, useTabStops: false }, (editor) => { + const indentLinesAction = new IndentLinesAction(); + editor.setPosition(new Position(1, 1)); + + executeAction(indentLinesAction, editor); + assert.equal(model.getLineContent(1), ' '); + assert.deepEqual(editor.getSelection(), new Selection(1, 5, 1, 5)); + }); + + model.dispose(); + }); + test('issue #62112: Delete line does not work properly when multiple cursors are on line', () => { const TEXT = [ 'a', diff --git a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts index b803557c63e..dd0a86ac066 100644 --- a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts @@ -277,7 +277,7 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { unIndentedLinePattern: /^(?!.*([;{}]|\S:)\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!.*(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$))/ }; - // https://github.com/Microsoft/vscode/issues/28552#issuecomment-307862797 + // https://github.com/microsoft/vscode/issues/28552#issuecomment-307862797 test('first line indentation adjust to 0', () => { let mode = new IndentRulesMode(indentRules); @@ -300,7 +300,7 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { mode.dispose(); }); - // https://github.com/Microsoft/vscode/issues/28552#issuecomment-307867717 + // https://github.com/microsoft/vscode/issues/28552#issuecomment-307867717 test('move lines across block', () => { let mode = new IndentRulesMode(indentRules); diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index a85e7f8c942..b1acfcf4fca 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -11,8 +11,9 @@ import { ITextModel } from 'vs/editor/common/model'; import { ILink, LinkProvider, LinkProviderRegistry, ILinksList } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { isDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { isDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; +import { assertType } from 'vs/base/common/types'; export class Link implements ILink { @@ -65,25 +66,32 @@ export class Link implements ILink { } } -export class LinksList extends Disposable { +export class LinksList { readonly links: Link[]; + private readonly _disposables = new DisposableStore(); + constructor(tuples: [ILinksList, LinkProvider][]) { - super(); + let links: Link[] = []; for (const [list, provider] of tuples) { // merge all links const newLinks = list.links.map(link => new Link(link, provider)); links = LinksList._union(links, newLinks); // register disposables - if (isDisposable(provider)) { - this._register(provider); + if (isDisposable(list)) { + this._disposables.add(list); } } this.links = links; } + dispose(): void { + this._disposables.dispose(); + this.links.length = 0; + } + private static _union(oldLinks: Link[], newLinks: Link[]): Link[] { // reunite oldLinks with newLinks and remove duplicates let result: Link[] = []; @@ -152,10 +160,13 @@ export function getLinks(model: ITextModel, token: CancellationToken): Promise => { - const [uri] = args; - if (!(uri instanceof URI)) { - return []; + let [uri, resolveCount] = args; + assertType(uri instanceof URI); + + if (typeof resolveCount !== 'number') { + resolveCount = 0; } + const model = accessor.get(IModelService).getModel(uri); if (!model) { return []; @@ -164,6 +175,12 @@ CommandsRegistry.registerCommand('_executeLinkProvider', async (accessor, ...arg if (!list) { return []; } + + // resolve links + for (let i = 0; i < Math.min(resolveCount, list.links.length); i++) { + await list.links[i].resolve(CancellationToken.None); + } + const result = list.links.slice(0); list.dispose(); return result; diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index bf5399f274a..283e8ef31a3 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -25,6 +25,9 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import * as resources from 'vs/base/common/resources'; function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString { const executeCmd = link.url && /^command:/i.test(link.url.toString()); @@ -97,7 +100,7 @@ class LinkOccurrence { } } -class LinkDetector implements IEditorContribution { +export class LinkDetector implements IEditorContribution { public static readonly ID: string = 'editor.linkDetector'; @@ -291,7 +294,29 @@ class LinkDetector implements IEditorContribution { const { link } = occurrence; link.resolve(CancellationToken.None).then(uri => { - // open the uri + + // Support for relative file URIs of the shape file://./relativeFile.txt or file:///./relativeFile.txt + if (typeof uri === 'string' && this.editor.hasModel()) { + const modelUri = this.editor.getModel().uri; + if (modelUri.scheme === Schemas.file && uri.startsWith(`${Schemas.file}:`)) { + const parsedUri = URI.parse(uri); + if (parsedUri.scheme === Schemas.file) { + const fsPath = resources.originalFSPath(parsedUri); + + let relativePath: string | null = null; + if (fsPath.startsWith('/./')) { + relativePath = `.${fsPath.substr(1)}`; + } else if (fsPath.startsWith('//./')) { + relativePath = `.${fsPath.substr(2)}`; + } + + if (relativePath) { + uri = resources.joinPath(modelUri, relativePath); + } + } + } + } + return this.openerService.open(uri, { openToSide, fromUserGesture }); }, err => { @@ -339,7 +364,8 @@ class LinkDetector implements IEditorContribution { private stop(): void { this.timeout.cancel(); if (this.activeLinksList) { - this.activeLinksList.dispose(); + this.activeLinksList?.dispose(); + this.activeLinksList = null; } if (this.computePromise) { this.computePromise.cancel(); diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts deleted file mode 100644 index b4bfe8ddd9e..00000000000 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ /dev/null @@ -1,88 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; -import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { optional } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; -import { TokenizationRegistry } from 'vs/editor/common/modes'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; - -export interface IMarkdownRenderResult extends IDisposable { - element: HTMLElement; -} - -export class MarkdownRenderer extends Disposable { - - private _onDidRenderCodeBlock = this._register(new Emitter()); - readonly onDidRenderCodeBlock: Event = this._onDidRenderCodeBlock.event; - - constructor( - private readonly _editor: ICodeEditor, - @IModeService private readonly _modeService: IModeService, - @optional(IOpenerService) private readonly _openerService: IOpenerService = NullOpenerService, - ) { - super(); - } - - private getOptions(disposeables: DisposableStore): MarkdownRenderOptions { - return { - codeBlockRenderer: (languageAlias, value) => { - // In markdown, - // it is possible that we stumble upon language aliases (e.g.js instead of javascript) - // it is possible no alias is given in which case we fall back to the current editor lang - let modeId: string | null = null; - if (languageAlias) { - modeId = this._modeService.getModeIdForLanguageName(languageAlias); - } else { - const model = this._editor.getModel(); - if (model) { - modeId = model.getLanguageIdentifier().language; - } - } - - this._modeService.triggerMode(modeId || ''); - return Promise.resolve(true).then(_ => { - const promise = TokenizationRegistry.getPromise(modeId || ''); - if (promise) { - return promise.then(support => tokenizeToString(value, support)); - } - return tokenizeToString(value, undefined); - }).then(code => { - return `${code}`; - }); - }, - codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), - actionHandler: { - callback: (content) => { - this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError); - }, - disposeables - } - }; - } - - render(markdown: IMarkdownString | undefined): IMarkdownRenderResult { - const disposeables = new DisposableStore(); - - let element: HTMLElement; - if (!markdown) { - element = document.createElement('span'); - } else { - element = renderMarkdown(markdown, this.getOptions(disposeables)); - } - - return { - element, - dispose: () => disposeables.dispose() - }; - } -} diff --git a/src/vs/editor/contrib/message/messageController.css b/src/vs/editor/contrib/message/messageController.css index 36c68465b2c..a3910415dd4 100644 --- a/src/vs/editor/contrib/message/messageController.css +++ b/src/vs/editor/contrib/message/messageController.css @@ -5,6 +5,7 @@ .monaco-editor .monaco-editor-overlaymessage { padding-bottom: 8px; + z-index: 10000; } @keyframes fadeIn { diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index a4fcb5f2d1a..6bec24fd00f 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -15,9 +15,10 @@ import { registerEditorContribution, EditorCommand, registerEditorCommand } from import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IPosition } from 'vs/editor/common/core/position'; -import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidationInfoForeground } from 'vs/platform/theme/common/colorRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export class MessageController extends Disposable implements IEditorContribution { @@ -29,8 +30,6 @@ export class MessageController extends Disposable implements IEditorContribution return editor.getContribution(MessageController.ID); } - private readonly closeTimeout = 3000; // close after 3s - private readonly _editor: ICodeEditor; private readonly _visible: IContextKey; private readonly _messageWidget = this._register(new MutableDisposable()); @@ -70,7 +69,8 @@ export class MessageController extends Disposable implements IEditorContribution this._messageListeners.add(this._editor.onDidDispose(() => this.closeMessage())); this._messageListeners.add(this._editor.onDidChangeModel(() => this.closeMessage())); - this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), this.closeTimeout)); + // 3sec + this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), 3000)); // close on mouse move let bounds: Range; @@ -185,7 +185,7 @@ registerEditorContribution(MessageController.ID, MessageController); registerThemingParticipant((theme, collector) => { const border = theme.getColor(inputValidationInfoBorder); if (border) { - let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`); collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`); } diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index b23cc0c09f8..6a7c295e827 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -9,7 +9,6 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { RevealTarget } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; import { Range } from 'vs/editor/common/core/range'; @@ -61,20 +60,19 @@ export class InsertCursorAbove extends EditorAction { } const useLogicalLine = (args && args.logicalLine === true); - const cursors = editor._getCursors(); - const context = cursors.context; + const viewModel = editor._getViewModel(); - if (context.config.readOnly) { + if (viewModel.cursorConfig.readOnly) { return; } - context.model.pushStackElement(); - cursors.setStates( + viewModel.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.addCursorUp(context, cursors.getAll(), useLogicalLine) + CursorMoveCommands.addCursorUp(viewModel, viewModel.getCursorStates(), useLogicalLine) ); - cursors.reveal(args.source, true, RevealTarget.TopMost, ScrollType.Smooth); + viewModel.revealTopMostCursor(args.source); } } @@ -110,20 +108,19 @@ export class InsertCursorBelow extends EditorAction { } const useLogicalLine = (args && args.logicalLine === true); - const cursors = editor._getCursors(); - const context = cursors.context; + const viewModel = editor._getViewModel(); - if (context.config.readOnly) { + if (viewModel.cursorConfig.readOnly) { return; } - context.model.pushStackElement(); - cursors.setStates( + viewModel.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.addCursorDown(context, cursors.getAll(), useLogicalLine) + CursorMoveCommands.addCursorDown(viewModel, viewModel.getCursorStates(), useLogicalLine) ); - cursors.reveal(args.source, true, RevealTarget.BottomMost, ScrollType.Smooth); + viewModel.revealBottomMostCursor(args.source); } } @@ -286,7 +283,7 @@ export class MultiCursorSession { if (s.isEmpty()) { // selection is empty => expand to current word - const word = editor.getModel().getWordAtPosition(s.getStartPosition()); + const word = editor.getConfiguredWordAtPosition(s.getStartPosition()); if (!word) { return null; } @@ -505,7 +502,7 @@ export class MultiCursorSelectionController extends Disposable implements IEdito if (!selection.isEmpty()) { return selection; } - const word = model.getWordAtPosition(selection.getStartPosition()); + const word = this._editor.getConfiguredWordAtPosition(selection.getStartPosition()); if (!word) { return selection; } @@ -604,13 +601,15 @@ export class MultiCursorSelectionController extends Disposable implements IEdito } if (findState.searchScope) { - const state = findState.searchScope; + const states = findState.searchScope; let inSelection: FindMatch[] | null = []; - for (let i = 0; i < matches.length; i++) { - if (matches[i].range.endLineNumber <= state.endLineNumber && matches[i].range.startLineNumber >= state.startLineNumber) { - inSelection.push(matches[i]); - } - } + matches.forEach((match) => { + states.forEach((state) => { + if (match.range.endLineNumber <= state.endLineNumber && match.range.startLineNumber >= state.startLineNumber) { + inSelection!.push(match); + } + }); + }); matches = inSelection; } @@ -786,11 +785,13 @@ class SelectionHighlighterState { public readonly searchText: string; public readonly matchCase: boolean; public readonly wordSeparators: string | null; + public readonly modelVersionId: number; - constructor(searchText: string, matchCase: boolean, wordSeparators: string | null) { + constructor(searchText: string, matchCase: boolean, wordSeparators: string | null, modelVersionId: number) { this.searchText = searchText; this.matchCase = matchCase; this.wordSeparators = wordSeparators; + this.modelVersionId = modelVersionId; } /** @@ -807,6 +808,7 @@ class SelectionHighlighterState { a.searchText === b.searchText && a.matchCase === b.matchCase && a.wordSeparators === b.wordSeparators + && a.modelVersionId === b.modelVersionId ); } } @@ -857,6 +859,11 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut this._register(editor.onDidChangeModel((e) => { this._setState(null); })); + this._register(editor.onDidChangeModelContent((e) => { + if (this._isEnabled) { + this.updateSoon.schedule(); + } + })); this._register(CommonFindController.get(editor).getState().onFindReplaceStateChange((e) => { this._update(); })); @@ -939,7 +946,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut } } - return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null); + return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null, editor.getModel().getVersionId()); } private _setState(state: SelectionHighlighterState | null): void { @@ -964,7 +971,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut return; } - const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model) && this.editor.getOption(EditorOption.occurrencesHighlight); let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range); allMatches.sort(Range.compareRangesUsingStarts); diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index 157a018ac0d..56ec69d45eb 100644 --- a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts @@ -10,7 +10,7 @@ import { Handler } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence } from 'vs/editor/common/model'; import { CommonFindController } from 'vs/editor/contrib/find/findController'; import { AddSelectionToNextFindMatchAction, InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction } from 'vs/editor/contrib/multicursor/multicursor'; -import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { ITestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -20,12 +20,12 @@ suite('Multicursor', () => { withTestCodeEditor([ 'abc', 'def' - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { let addCursorUpAction = new InsertCursorAbove(); editor.setSelection(new Selection(2, 1, 2, 1)); addCursorUpAction.run(null!, editor, {}); - assert.equal(cursor.getSelections().length, 2); + assert.equal(viewModel.getSelections().length, 2); editor.trigger('test', Handler.Paste, { text: '1\n2', @@ -34,7 +34,7 @@ suite('Multicursor', () => { '2' ] }); - // cursorCommand(cursor, H.Paste, { text: '1\n2' }); + assert.equal(editor.getModel()!.getLineContent(1), '1abc'); assert.equal(editor.getModel()!.getLineContent(2), '2def'); }); @@ -43,10 +43,10 @@ suite('Multicursor', () => { test('issue #1336: Insert cursor below on last line adds a cursor to the end of the current line', () => { withTestCodeEditor([ 'abc' - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { let addCursorDownAction = new InsertCursorBelow(); addCursorDownAction.run(null!, editor, {}); - assert.equal(cursor.getSelections().length, 1); + assert.equal(viewModel.getSelections().length, 1); }); }); @@ -61,16 +61,20 @@ suite('Multicursor selection', () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeValue: Event.None, + onDidChangeTarget: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], getNumber: (key: string) => undefined!, + store2: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined, logStorage: () => undefined, migrate: (toWorkspace) => Promise.resolve(undefined), - flush: () => undefined + flush: () => Promise.resolve(undefined), + isNew: () => true, + keys: () => [] } as IStorageService); test('issue #8817: Cursor position changes when you cancel multicursor', () => { @@ -78,10 +82,10 @@ suite('Multicursor selection', () => { 'var x = (3 * 5)', 'var y = (3 * 5)', 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, (editor) => { - let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); - let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); + let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); let selectHighlightsAction = new SelectHighlightsAction(); editor.setSelection(new Selection(2, 9, 2, 16)); @@ -108,10 +112,10 @@ suite('Multicursor selection', () => { 'someething', 'someeething', 'nothing' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, (editor) => { - let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); - let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); + let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); let selectHighlightsAction = new SelectHighlightsAction(); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -142,10 +146,10 @@ suite('Multicursor selection', () => { 'rty', 'qwe', 'rty' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, (editor) => { - let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); - let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); + let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); editor.setSelection(new Selection(2, 1, 3, 4)); @@ -170,10 +174,10 @@ suite('Multicursor selection', () => { 'abcabc', 'abc', 'abcabc', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, (editor) => { - let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); - let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); + let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); editor.setSelection(new Selection(1, 1, 1, 4)); @@ -225,12 +229,12 @@ suite('Multicursor selection', () => { 'rty', 'qwe', 'rty' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, (editor) => { editor.getModel()!.setEOL(EndOfLineSequence.CRLF); - let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); - let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); + let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); editor.setSelection(new Selection(2, 1, 3, 4)); @@ -250,10 +254,10 @@ suite('Multicursor selection', () => { }); }); - function testMulticursor(text: string[], callback: (editor: TestCodeEditor, findController: CommonFindController) => void): void { - withTestCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => { - let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); - let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); + function testMulticursor(text: string[], callback: (editor: ITestCodeEditor, findController: CommonFindController) => void): void { + withTestCodeEditor(text, { serviceCollection: serviceCollection }, (editor) => { + let findController = editor.registerAndInstantiateContribution(CommonFindController.ID, CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController.ID, MultiCursorSelectionController); callback(editor, findController); @@ -262,7 +266,7 @@ suite('Multicursor selection', () => { }); } - function testAddSelectionToNextFindMatchAction(text: string[], callback: (editor: TestCodeEditor, action: AddSelectionToNextFindMatchAction, findController: CommonFindController) => void): void { + function testAddSelectionToNextFindMatchAction(text: string[], callback: (editor: ITestCodeEditor, action: AddSelectionToNextFindMatchAction, findController: CommonFindController) => void): void { testMulticursor(text, (editor, findController) => { let action = new AddSelectionToNextFindMatchAction(); callback(editor, action, findController); diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.css b/src/vs/editor/contrib/parameterHints/parameterHints.css index 89bf6462e7f..03c4e2640ee 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/parameterHints.css @@ -33,6 +33,7 @@ .monaco-editor .parameter-hints-widget .monaco-scrollable-element, .monaco-editor .parameter-hints-widget .body { display: flex; + flex: 1; flex-direction: column; min-height: 100%; } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts index b285d1f7665..d7ea89b532b 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts @@ -31,7 +31,8 @@ namespace ParameterHintState { export class Pending { readonly type = Type.Pending; constructor( - readonly request: CancelablePromise + readonly request: CancelablePromise, + readonly previouslyActiveHints: modes.SignatureHelp | undefined, ) { } } @@ -167,8 +168,7 @@ export class ParameterHintsModel extends Disposable { private async doTrigger(triggerId: number): Promise { const isRetrigger = this.state.type === ParameterHintState.Type.Active || this.state.type === ParameterHintState.Type.Pending; - const activeSignatureHelp = this.state.type === ParameterHintState.Type.Active ? this.state.hints : undefined; - + const activeSignatureHelp = this.getLastActiveHints(); this.cancel(true); if (this._pendingTriggers.length === 0) { @@ -192,8 +192,9 @@ export class ParameterHintsModel extends Disposable { const model = this.editor.getModel(); const position = this.editor.getPosition(); - this.state = new ParameterHintState.Pending(createCancelablePromise(token => - provideSignatureHelp(model, position, triggerContext, token))); + this.state = new ParameterHintState.Pending( + createCancelablePromise(token => provideSignatureHelp(model, position, triggerContext, token)), + activeSignatureHelp); try { const result = await this.state.request; @@ -225,6 +226,14 @@ export class ParameterHintsModel extends Disposable { } } + private getLastActiveHints(): modes.SignatureHelp | undefined { + switch (this.state.type) { + case ParameterHintState.Type.Active: return this.state.hints; + case ParameterHintState.Type.Pending: return this.state.previouslyActiveHints; + default: return undefined; + } + } + private get isTriggered(): boolean { return this.state.type === ParameterHintState.Type.Active || this.state.type === ParameterHintState.Type.Pending diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 41e4a66fd1d..2d0d4bcd349 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -14,18 +14,24 @@ import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentW import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import * as modes from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import * as nls from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorHoverBackground, editorHoverBorder, textCodeBlockBackground, textLinkForeground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; -import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; -import { pad } from 'vs/base/common/strings'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; +import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { assertIsDefined } from 'vs/base/common/types'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; const $ = dom.$; +const parameterHintsNextIcon = registerIcon('parameter-hints-next', Codicon.chevronDown); +const parameterHintsPreviousIcon = registerIcon('parameter-hints-previous', Codicon.chevronUp); + export class ParameterHintsWidget extends Disposable implements IContentWidget { private static readonly ID = 'editor.widget.parameterHintsWidget'; @@ -57,7 +63,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { @IModeService modeService: IModeService, ) { super(); - this.markdownRenderer = this._register(new MarkdownRenderer(editor, modeService, openerService)); + this.markdownRenderer = this._register(new MarkdownRenderer({ editor }, modeService, openerService)); this.model = this._register(new ParameterHintsModel(editor)); this.keyVisible = Context.Visible.bindTo(contextKeyService); this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService); @@ -78,9 +84,9 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { wrapper.tabIndex = -1; const controls = dom.append(wrapper, $('.controls')); - const previous = dom.append(controls, $('.button.codicon.codicon-chevron-up')); + const previous = dom.append(controls, $('.button' + parameterHintsPreviousIcon.cssSelector)); const overloads = dom.append(controls, $('.overloads')); - const next = dom.append(controls, $('.button.codicon.codicon-chevron-down')); + const next = dom.append(controls, $('.button' + parameterHintsNextIcon.cssSelector)); const onPreviousClick = stop(domEvent(previous, 'click')); this._register(onPreviousClick(this.previous, this)); @@ -146,13 +152,15 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.visible = true; setTimeout(() => { if (this.domNodes) { - dom.addClass(this.domNodes.element, 'visible'); + this.domNodes.element.classList.add('visible'); } }, 100); this.editor.layoutContentWidget(this); } private hide(): void { + this.renderDisposeables.clear(); + if (!this.visible) { return; } @@ -161,7 +169,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.visible = false; this.announcedLabel = null; if (this.domNodes) { - dom.removeClass(this.domNodes.element, 'visible'); + this.domNodes.element.classList.remove('visible'); } this.editor.layoutContentWidget(this); } @@ -177,16 +185,18 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } private render(hints: modes.SignatureHelp): void { + this.renderDisposeables.clear(); + if (!this.domNodes) { return; } const multiple = hints.signatures.length > 1; - dom.toggleClass(this.domNodes.element, 'multiple', multiple); + this.domNodes.element.classList.toggle('multiple', multiple); this.keyMultipleSignatures.set(multiple); - this.domNodes.signature.innerHTML = ''; - this.domNodes.docs.innerHTML = ''; + this.domNodes.signature.innerText = ''; + this.domNodes.docs.innerText = ''; const signature = hints.signatures[hints.activeSignature]; if (!signature) { @@ -194,56 +204,53 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } const code = dom.append(this.domNodes.signature, $('.code')); - const hasParameters = signature.parameters.length > 0; - const fontInfo = this.editor.getOption(EditorOption.fontInfo); code.style.fontSize = `${fontInfo.fontSize}px`; code.style.fontFamily = fontInfo.fontFamily; + const hasParameters = signature.parameters.length > 0; + const activeParameterIndex = signature.activeParameter ?? hints.activeParameter; + if (!hasParameters) { const label = dom.append(code, $('span')); label.textContent = signature.label; } else { - this.renderParameters(code, signature, hints.activeParameter); + this.renderParameters(code, signature, activeParameterIndex); } - this.renderDisposeables.clear(); - - const activeParameter: modes.ParameterInformation | undefined = signature.parameters[hints.activeParameter]; - - if (activeParameter && activeParameter.documentation) { + const activeParameter: modes.ParameterInformation | undefined = signature.parameters[activeParameterIndex]; + if (activeParameter?.documentation) { const documentation = $('span.documentation'); if (typeof activeParameter.documentation === 'string') { documentation.textContent = activeParameter.documentation; } else { - const renderedContents = this.markdownRenderer.render(activeParameter.documentation); - dom.addClass(renderedContents.element, 'markdown-docs'); - this.renderDisposeables.add(renderedContents); + const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(activeParameter.documentation)); + renderedContents.element.classList.add('markdown-docs'); documentation.appendChild(renderedContents.element); } dom.append(this.domNodes.docs, $('p', {}, documentation)); } - if (signature.documentation === undefined) { /** no op */ } - else if (typeof signature.documentation === 'string') { + if (signature.documentation === undefined) { + /** no op */ + } else if (typeof signature.documentation === 'string') { dom.append(this.domNodes.docs, $('p', {}, signature.documentation)); } else { - const renderedContents = this.markdownRenderer.render(signature.documentation); - dom.addClass(renderedContents.element, 'markdown-docs'); - this.renderDisposeables.add(renderedContents); + const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(signature.documentation)); + renderedContents.element.classList.add('markdown-docs'); dom.append(this.domNodes.docs, renderedContents.element); } const hasDocs = this.hasDocs(signature, activeParameter); - dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs); - dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs); + this.domNodes.signature.classList.toggle('has-docs', hasDocs); + this.domNodes.docs.classList.toggle('empty', !hasDocs); this.domNodes.overloads.textContent = - pad(hints.activeSignature + 1, hints.signatures.length.toString().length) + '/' + hints.signatures.length; + String(hints.activeSignature + 1).padStart(hints.signatures.length.toString().length, '0') + '/' + hints.signatures.length; if (activeParameter) { - const labelToAnnounce = this.getParameterLabel(signature, hints.activeParameter); + const labelToAnnounce = this.getParameterLabel(signature, activeParameterIndex); // Select method gets called on every user type while parameter hints are visible. // We do not want to spam the user with same announcements, so we only announce if the current parameter changed. @@ -258,23 +265,23 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } private hasDocs(signature: modes.SignatureInformation, activeParameter: modes.ParameterInformation | undefined): boolean { - if (activeParameter && typeof (activeParameter.documentation) === 'string' && activeParameter.documentation.length > 0) { + if (activeParameter && typeof activeParameter.documentation === 'string' && assertIsDefined(activeParameter.documentation).length > 0) { return true; } - if (activeParameter && typeof (activeParameter.documentation) === 'object' && activeParameter.documentation.value.length > 0) { + if (activeParameter && typeof activeParameter.documentation === 'object' && assertIsDefined(activeParameter.documentation).value.length > 0) { return true; } - if (typeof (signature.documentation) === 'string' && signature.documentation.length > 0) { + if (signature.documentation && typeof signature.documentation === 'string' && assertIsDefined(signature.documentation).length > 0) { return true; } - if (typeof (signature.documentation) === 'object' && signature.documentation.value.length > 0) { + if (signature.documentation && typeof signature.documentation === 'object' && assertIsDefined(signature.documentation.value).length > 0) { return true; } return false; } - private renderParameters(parent: HTMLElement, signature: modes.SignatureInformation, currentParameter: number): void { - const [start, end] = this.getParameterLabelOffsets(signature, currentParameter); + private renderParameters(parent: HTMLElement, signature: modes.SignatureInformation, activeParameterIndex: number): void { + const [start, end] = this.getParameterLabelOffsets(signature, activeParameterIndex); const beforeSpan = document.createElement('span'); beforeSpan.textContent = signature.label.substring(0, start); @@ -291,10 +298,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { private getParameterLabel(signature: modes.SignatureInformation, paramIdx: number): string { const param = signature.parameters[paramIdx]; - if (typeof param.label === 'string') { - return param.label; - } else { + if (Array.isArray(param.label)) { return signature.label.substring(param.label[0], param.label[1]); + } else { + return param.label; } } @@ -304,10 +311,14 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { return [0, 0]; } else if (Array.isArray(param.label)) { return param.label; + } else if (!param.label.length) { + return [0, 0]; } else { - const idx = signature.label.lastIndexOf(param.label); + const regex = new RegExp(`(\\W|^)${escapeRegExpCharacters(param.label)}(?=\\W|$)`, 'g'); + regex.test(signature.label); + const idx = regex.lastIndex - param.label.length; return idx >= 0 - ? [idx, idx + param.label.length] + ? [idx, regex.lastIndex] : [0, 0]; } } @@ -358,7 +369,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { registerThemingParticipant((theme, collector) => { const border = theme.getColor(editorHoverBorder); if (border) { - const borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + const borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; collector.addRule(`.monaco-editor .parameter-hints-widget { border: ${borderWidth}px solid ${border}; }`); collector.addRule(`.monaco-editor .parameter-hints-widget.multiple .body { border-left: 1px solid ${border.transparent(0.5)}; }`); collector.addRule(`.monaco-editor .parameter-hints-widget .signature.has-docs { border-bottom: 1px solid ${border.transparent(0.5)}; }`); diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 2aa301d692a..a4c8afb561f 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import * as modes from 'vs/editor/common/modes'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -49,7 +49,7 @@ suite('ParameterHintsModel', () => { }); function createMockEditor(fileContents: string) { - const textModel = TextModel.createFromString(fileContents, undefined, undefined, mockFile); + const textModel = createTextModel(fileContents, undefined, undefined, mockFile); const editor = createTestCodeEditor({ model: textModel, serviceCollection: new ServiceCollection( @@ -290,7 +290,7 @@ suite('ParameterHintsModel', () => { hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0); assert.strictEqual(-1, didRequestCancellationOf); - return new Promise((resolve, reject) => + return new Promise((resolve, reject) => hintsModel.onChangedHints(newParamterHints => { try { assert.strictEqual(0, didRequestCancellationOf); @@ -462,6 +462,54 @@ suite('ParameterHintsModel', () => { await getNextHint(model); }); + + test('Retrigger while a pending resolve is still going on should preserve last active signature #96702', (done) => { + const editor = createMockEditor(''); + const model = new ParameterHintsModel(editor, 50); + disposables.add(model); + + const triggerCharacter = 'a'; + const retriggerCharacter = 'b'; + + let invokeCount = 0; + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + signatureHelpTriggerCharacters = [triggerCharacter]; + signatureHelpRetriggerCharacters = [retriggerCharacter]; + + async provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): Promise { + try { + ++invokeCount; + + if (invokeCount === 1) { + assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); + assert.strictEqual(context.triggerCharacter, triggerCharacter); + setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: retriggerCharacter }), 50); + } else if (invokeCount === 2) { + // Trigger again while we wait for resolve to take place + setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: retriggerCharacter }), 50); + await new Promise(resolve => setTimeout(resolve, 1000)); + } else if (invokeCount === 3) { + // Make sure that in a retrigger during a pending resolve, we still have the old active signature. + assert.strictEqual(context.activeSignatureHelp, emptySigHelp); + done(); + } else { + assert.fail('Unexpected invoke'); + } + + return emptySigHelpResult; + } catch (err) { + console.error(err); + done(err); + throw err; + } + } + })); + + editor.trigger('keyboard', Handler.Type, { text: triggerCharacter }); + + getNextHint(model) + .then(() => getNextHint(model)); + }); }); function getNextHint(model: ParameterHintsModel) { diff --git a/src/vs/editor/contrib/peekView/media/peekViewWidget.css b/src/vs/editor/contrib/peekView/media/peekViewWidget.css index d0e9bc3c552..e6b5ccb09f2 100644 --- a/src/vs/editor/contrib/peekView/media/peekViewWidget.css +++ b/src/vs/editor/contrib/peekView/media/peekViewWidget.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .peekview-widget .head { - box-sizing: border-box; + box-sizing: border-box; display: flex; } @@ -14,6 +14,7 @@ font-size: 13px; margin-left: 20px; cursor: pointer; + min-width: 0; } .monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty) { @@ -25,6 +26,15 @@ white-space: nowrap; } +.monaco-editor .peekview-widget .head .peekview-title .dirname { + white-space: nowrap; +} + +.monaco-editor .peekview-widget .head .peekview-title .filename { + overflow: hidden; + text-overflow: ellipsis; +} + .monaco-editor .peekview-widget .head .peekview-title .meta:not(:empty)::before { content: '-'; padding: 0 0.3em; diff --git a/src/vs/editor/contrib/peekView/peekView.ts b/src/vs/editor/contrib/peekView/peekView.ts index 32503e62570..f265a4bfd60 100644 --- a/src/vs/editor/contrib/peekView/peekView.ts +++ b/src/vs/editor/contrib/peekView/peekView.ts @@ -11,30 +11,31 @@ import { Action } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; import { Emitter } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; -import * as strings from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IOptions, IStyles, ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import * as nls from 'vs/nls'; -import { ContextKeyExpr, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor, createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { registerColor, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; - +import { Codicon } from 'vs/base/common/codicons'; +import { MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; export const IPeekViewService = createDecorator('IPeekViewService'); export interface IPeekViewService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; addExclusiveWidget(editor: ICodeEditor, widget: PeekViewWidget): void; } registerSingleton(IPeekViewService, class implements IPeekViewService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _widgets = new Map(); @@ -57,7 +58,7 @@ registerSingleton(IPeekViewService, class implements IPeekViewService { export namespace PeekContext { export const inPeekEditor = new RawContextKey('inReferenceSearchEditor', true); - export const notInPeekEditor: ContextKeyExpr = inPeekEditor.toNegated(); + export const notInPeekEditor = inPeekEditor.toNegated(); } class PeekContextController implements IEditorContribution { @@ -102,7 +103,7 @@ const defaultOptions: IPeekViewOptions = { export abstract class PeekViewWidget extends ZoneWidget { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidClose = new Emitter(); readonly onDidClose = this._onDidClose.event; @@ -114,7 +115,11 @@ export abstract class PeekViewWidget extends ZoneWidget { protected _actionbarWidget?: ActionBar; protected _bodyElement?: HTMLDivElement; - constructor(editor: ICodeEditor, options: IPeekViewOptions = {}) { + constructor( + editor: ICodeEditor, + options: IPeekViewOptions, + @IInstantiationService protected readonly instantiationService: IInstantiationService + ) { super(editor, options); objects.mixin(this.options, defaultOptions, false); } @@ -168,7 +173,7 @@ export abstract class PeekViewWidget extends ZoneWidget { container.appendChild(this._bodyElement); } - protected _fillHead(container: HTMLElement): void { + protected _fillHead(container: HTMLElement, noCloseAction?: boolean): void { const titleElement = dom.$('.peekview-title'); dom.append(this._headElement!, titleElement); dom.addStandardDisposableListener(titleElement, 'click', event => this._onTitleClick(event)); @@ -186,17 +191,29 @@ export abstract class PeekViewWidget extends ZoneWidget { this._actionbarWidget = new ActionBar(actionsContainer, actionBarOptions); this._disposables.add(this._actionbarWidget); - this._actionbarWidget.push(new Action('peekview.close', nls.localize('label.close', "Close"), 'codicon-close', true, () => { - this.dispose(); - return Promise.resolve(); - }), { label: false, icon: true }); + if (!noCloseAction) { + this._actionbarWidget.push(new Action('peekview.close', nls.localize('label.close', "Close"), Codicon.close.classNames, true, () => { + this.dispose(); + return Promise.resolve(); + }), { label: false, icon: true }); + } } protected _fillTitleIcon(container: HTMLElement): void { } protected _getActionBarOptions(): IActionBarOptions { - return {}; + return { + actionViewItemProvider: action => { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); + } + + return undefined; + } + }; } protected _onTitleClick(event: IMouseEvent): void { @@ -205,10 +222,10 @@ export abstract class PeekViewWidget extends ZoneWidget { setTitle(primaryHeading: string, secondaryHeading?: string): void { if (this._primaryHeading && this._secondaryHeading) { - this._primaryHeading.innerHTML = strings.escape(primaryHeading); + this._primaryHeading.innerText = primaryHeading; this._primaryHeading.setAttribute('aria-label', primaryHeading); if (secondaryHeading) { - this._secondaryHeading.innerHTML = strings.escape(secondaryHeading); + this._secondaryHeading.innerText = secondaryHeading; } else { dom.clearNode(this._secondaryHeading); } @@ -218,7 +235,7 @@ export abstract class PeekViewWidget extends ZoneWidget { setMetaTitle(value: string): void { if (this._metaHeading) { if (value) { - this._metaHeading.innerHTML = strings.escape(value); + this._metaHeading.innerText = value; dom.show(this._metaHeading); } else { dom.hide(this._metaHeading); diff --git a/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts b/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts new file mode 100644 index 00000000000..7db794fa83e --- /dev/null +++ b/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AbstractCommandsQuickAccessProvider, ICommandQuickPick, ICommandsQuickAccessOptions } from 'vs/platform/quickinput/browser/commandsQuickAccess'; +import { IEditor } from 'vs/editor/common/editorCommon'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { stripCodicons } from 'vs/base/common/codicons'; + +export abstract class AbstractEditorCommandsQuickAccessProvider extends AbstractCommandsQuickAccessProvider { + + constructor( + options: ICommandsQuickAccessOptions, + instantiationService: IInstantiationService, + keybindingService: IKeybindingService, + commandService: ICommandService, + telemetryService: ITelemetryService, + notificationService: INotificationService + ) { + super(options, instantiationService, keybindingService, commandService, telemetryService, notificationService); + } + + /** + * Subclasses to provide the current active editor control. + */ + protected abstract activeTextEditorControl: IEditor | undefined; + + protected getCodeEditorCommandPicks(): ICommandQuickPick[] { + const activeTextEditorControl = this.activeTextEditorControl; + if (!activeTextEditorControl) { + return []; + } + + const editorCommandPicks: ICommandQuickPick[] = []; + for (const editorAction of activeTextEditorControl.getSupportedActions()) { + editorCommandPicks.push({ + commandId: editorAction.id, + commandAlias: editorAction.alias, + label: stripCodicons(editorAction.label) || editorAction.id, + }); + } + + return editorCommandPicks; + } +} diff --git a/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts new file mode 100644 index 00000000000..039dfbd30df --- /dev/null +++ b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess'; +import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; +import { IModelDeltaDecoration, OverviewRulerLane, ITextModel } from 'vs/editor/common/model'; +import { IRange } from 'vs/editor/common/core/range'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry'; +import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IDisposable, DisposableStore, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; +import { isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { withNullAsUndefined } from 'vs/base/common/types'; +import { once } from 'vs/base/common/functional'; + +interface IEditorLineDecoration { + rangeHighlightId: string; + overviewRulerDecorationId: string; +} + +export interface IEditorNavigationQuickAccessOptions { + canAcceptInBackground?: boolean; +} + +/** + * A reusable quick access provider for the editor with support + * for adding decorations for navigating in the currently active file + * (for example "Go to line", "Go to symbol"). + */ +export abstract class AbstractEditorNavigationQuickAccessProvider implements IQuickAccessProvider { + + constructor(protected options?: IEditorNavigationQuickAccessOptions) { } + + //#region Provider methods + + provide(picker: IQuickPick, token: CancellationToken): IDisposable { + const disposables = new DisposableStore(); + + // Apply options if any + picker.canAcceptInBackground = !!this.options?.canAcceptInBackground; + + // Disable filtering & sorting, we control the results + picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false; + + // Provide based on current active editor + const pickerDisposable = disposables.add(new MutableDisposable()); + pickerDisposable.value = this.doProvide(picker, token); + + // Re-create whenever the active editor changes + disposables.add(this.onDidActiveTextEditorControlChange(() => { + + // Clear old + pickerDisposable.value = undefined; + + // Add new + pickerDisposable.value = this.doProvide(picker, token); + })); + + return disposables; + } + + private doProvide(picker: IQuickPick, token: CancellationToken): IDisposable { + const disposables = new DisposableStore(); + + // With text control + const editor = this.activeTextEditorControl; + if (editor && this.canProvideWithTextEditor(editor)) { + + // Restore any view state if this picker was closed + // without actually going to a line + const codeEditor = getCodeEditor(editor); + if (codeEditor) { + + // Remember view state and update it when the cursor position + // changes even later because it could be that the user has + // configured quick access to remain open when focus is lost and + // we always want to restore the current location. + let lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState()); + disposables.add(codeEditor.onDidChangeCursorPosition(() => { + lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState()); + })); + + disposables.add(once(token.onCancellationRequested)(() => { + if (lastKnownEditorViewState && editor === this.activeTextEditorControl) { + editor.restoreViewState(lastKnownEditorViewState); + } + })); + } + + // Clean up decorations on dispose + disposables.add(toDisposable(() => this.clearDecorations(editor))); + + // Ask subclass for entries + disposables.add(this.provideWithTextEditor(editor, picker, token)); + } + + // Without text control + else { + disposables.add(this.provideWithoutTextEditor(picker, token)); + } + + return disposables; + } + + /** + * Subclasses to implement if they can operate on the text editor. + */ + protected canProvideWithTextEditor(editor: IEditor): boolean { + return true; + } + + /** + * Subclasses to implement to provide picks for the picker when an editor is active. + */ + protected abstract provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable; + + /** + * Subclasses to implement to provide picks for the picker when no editor is active. + */ + protected abstract provideWithoutTextEditor(picker: IQuickPick, token: CancellationToken): IDisposable; + + protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + editor.setSelection(options.range); + editor.revealRangeInCenter(options.range, ScrollType.Smooth); + if (!options.preserveFocus) { + editor.focus(); + } + } + + protected getModel(editor: IEditor | IDiffEditor): ITextModel | undefined { + return isDiffEditor(editor) ? + editor.getModel()?.modified : + editor.getModel() as ITextModel; + } + + //#endregion + + + //#region Editor access + + /** + * Subclasses to provide an event when the active editor control changes. + */ + protected abstract readonly onDidActiveTextEditorControlChange: Event; + + /** + * Subclasses to provide the current active editor control. + */ + protected abstract activeTextEditorControl: IEditor | undefined; + + //#endregion + + + //#region Decorations Utils + + private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined; + + protected addDecorations(editor: IEditor, range: IRange): void { + editor.changeDecorations(changeAccessor => { + + // Reset old decorations if any + const deleteDecorations: string[] = []; + if (this.rangeHighlightDecorationId) { + deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId); + deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId); + + this.rangeHighlightDecorationId = undefined; + } + + // Add new decorations for the range + const newDecorations: IModelDeltaDecoration[] = [ + + // highlight the entire line on the range + { + range, + options: { + className: 'rangeHighlight', + isWholeLine: true + } + }, + + // also add overview ruler highlight + { + range, + options: { + overviewRuler: { + color: themeColorFromId(overviewRulerRangeHighlight), + position: OverviewRulerLane.Full + } + } + } + ]; + + const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations); + + this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId }; + }); + } + + protected clearDecorations(editor: IEditor): void { + const rangeHighlightDecorationId = this.rangeHighlightDecorationId; + if (rangeHighlightDecorationId) { + editor.changeDecorations(changeAccessor => { + changeAccessor.deltaDecorations([ + rangeHighlightDecorationId.overviewRulerDecorationId, + rangeHighlightDecorationId.rangeHighlightId + ], []); + }); + + this.rangeHighlightDecorationId = undefined; + } + } + + //#endregion +} diff --git a/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts new file mode 100644 index 00000000000..0c17d7c0b41 --- /dev/null +++ b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { IRange } from 'vs/editor/common/core/range'; +import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; +import { IPosition } from 'vs/editor/common/core/position'; +import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; + +interface IGotoLineQuickPickItem extends IQuickPickItem, Partial { } + +export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider { + + static PREFIX = ':'; + + constructor() { + super({ canAcceptInBackground: true }); + } + + protected provideWithoutTextEditor(picker: IQuickPick): IDisposable { + const label = localize('cannotRunGotoLine', "Open a text editor first to go to a line."); + + picker.items = [{ label }]; + picker.ariaLabel = label; + + return Disposable.None; + } + + protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + const disposables = new DisposableStore(); + + // Goto line once picked + disposables.add(picker.onDidAccept(event => { + const [item] = picker.selectedItems; + if (item) { + if (!this.isValidLineNumber(editor, item.lineNumber)) { + return; + } + + this.gotoLocation(editor, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods, preserveFocus: event.inBackground }); + + if (!event.inBackground) { + picker.hide(); + } + } + })); + + // React to picker changes + const updatePickerAndEditor = () => { + const position = this.parsePosition(editor, picker.value.trim().substr(AbstractGotoLineQuickAccessProvider.PREFIX.length)); + const label = this.getPickLabel(editor, position.lineNumber, position.column); + + // Picker + picker.items = [{ + lineNumber: position.lineNumber, + column: position.column, + label + }]; + + // ARIA Label + picker.ariaLabel = label; + + // Clear decorations for invalid range + if (!this.isValidLineNumber(editor, position.lineNumber)) { + this.clearDecorations(editor); + return; + } + + // Reveal + const range = this.toRange(position.lineNumber, position.column); + editor.revealRangeInCenter(range, ScrollType.Smooth); + + // Decorate + this.addDecorations(editor, range); + }; + updatePickerAndEditor(); + disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor())); + + // Adjust line number visibility as needed + const codeEditor = getCodeEditor(editor); + if (codeEditor) { + const options = codeEditor.getOptions(); + const lineNumbers = options.get(EditorOption.lineNumbers); + if (lineNumbers.renderType === RenderLineNumbersType.Relative) { + codeEditor.updateOptions({ lineNumbers: 'on' }); + + disposables.add(toDisposable(() => codeEditor.updateOptions({ lineNumbers: 'relative' }))); + } + } + + return disposables; + } + + private toRange(lineNumber = 1, column = 1): IRange { + return { + startLineNumber: lineNumber, + startColumn: column, + endLineNumber: lineNumber, + endColumn: column + }; + } + + private parsePosition(editor: IEditor, value: string): IPosition { + + // Support line-col formats of `line,col`, `line:col`, `line#col` + const numbers = value.split(/,|:|#/).map(part => parseInt(part, 10)).filter(part => !isNaN(part)); + const endLine = this.lineCount(editor) + 1; + + return { + lineNumber: numbers[0] > 0 ? numbers[0] : endLine + numbers[0], + column: numbers[1] + }; + } + + private getPickLabel(editor: IEditor, lineNumber: number, column: number | undefined): string { + + // Location valid: indicate this as picker label + if (this.isValidLineNumber(editor, lineNumber)) { + if (this.isValidColumn(editor, lineNumber, column)) { + return localize('gotoLineColumnLabel', "Go to line {0} and column {1}.", lineNumber, column); + } + + return localize('gotoLineLabel', "Go to line {0}.", lineNumber); + } + + // Location invalid: show generic label + const position = editor.getPosition() || { lineNumber: 1, column: 1 }; + const lineCount = this.lineCount(editor); + if (lineCount > 1) { + return localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Character: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, lineCount); + } + + return localize('gotoLineLabelEmpty', "Current Line: {0}, Character: {1}. Type a line number to navigate to.", position.lineNumber, position.column); + } + + private isValidLineNumber(editor: IEditor, lineNumber: number | undefined): boolean { + if (!lineNumber || typeof lineNumber !== 'number') { + return false; + } + + return lineNumber > 0 && lineNumber <= this.lineCount(editor); + } + + private isValidColumn(editor: IEditor, lineNumber: number, column: number | undefined): boolean { + if (!column || typeof column !== 'number') { + return false; + } + + const model = this.getModel(editor); + if (!model) { + return false; + } + + const positionCandidate = { lineNumber, column }; + + return model.validatePosition(positionCandidate).equals(positionCandidate); + } + + private lineCount(editor: IEditor): number { + return this.getModel(editor)?.getLineCount() ?? 0; + } +} diff --git a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts new file mode 100644 index 00000000000..ca85c3ce869 --- /dev/null +++ b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts @@ -0,0 +1,496 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; +import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes'; +import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { trim, format } from 'vs/base/common/strings'; +import { prepareQuery, IPreparedQuery, pieceToQuery, scoreFuzzy2 } from 'vs/base/common/fuzzyScorer'; +import { IMatch } from 'vs/base/common/filters'; +import { Iterable } from 'vs/base/common/iterator'; +import { Codicon } from 'vs/base/common/codicons'; + +export interface IGotoSymbolQuickPickItem extends IQuickPickItem { + kind: SymbolKind, + index: number, + score?: number; + range?: { decoration: IRange, selection: IRange } +} + +export interface IGotoSymbolQuickAccessProviderOptions extends IEditorNavigationQuickAccessOptions { + openSideBySideDirection?: () => undefined | 'right' | 'down' +} + +export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider { + + static PREFIX = '@'; + static SCOPE_PREFIX = ':'; + static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`; + + constructor(protected options: IGotoSymbolQuickAccessProviderOptions = Object.create(null)) { + super(options); + + options.canAcceptInBackground = true; + } + + protected provideWithoutTextEditor(picker: IQuickPick): IDisposable { + this.provideLabelPick(picker, localize('cannotRunGotoSymbolWithoutEditor', "To go to a symbol, first open a text editor with symbol information.")); + + return Disposable.None; + } + + protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + const model = this.getModel(editor); + if (!model) { + return Disposable.None; + } + + // Provide symbols from model if available in registry + if (DocumentSymbolProviderRegistry.has(model)) { + return this.doProvideWithEditorSymbols(editor, model, picker, token); + } + + // Otherwise show an entry for a model without registry + // But give a chance to resolve the symbols at a later + // point if possible + return this.doProvideWithoutEditorSymbols(editor, model, picker, token); + } + + private doProvideWithoutEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + const disposables = new DisposableStore(); + + // Generic pick for not having any symbol information + this.provideLabelPick(picker, localize('cannotRunGotoSymbolWithoutSymbolProvider', "The active text editor does not provide symbol information.")); + + // Wait for changes to the registry and see if eventually + // we do get symbols. This can happen if the picker is opened + // very early after the model has loaded but before the + // language registry is ready. + // https://github.com/microsoft/vscode/issues/70607 + (async () => { + const result = await this.waitForLanguageSymbolRegistry(model, disposables); + if (!result || token.isCancellationRequested) { + return; + } + + disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token)); + })(); + + return disposables; + } + + private provideLabelPick(picker: IQuickPick, label: string): void { + picker.items = [{ label, index: 0, kind: SymbolKind.String }]; + picker.ariaLabel = label; + } + + protected async waitForLanguageSymbolRegistry(model: ITextModel, disposables: DisposableStore): Promise { + if (DocumentSymbolProviderRegistry.has(model)) { + return true; + } + + let symbolProviderRegistryPromiseResolve: (res: boolean) => void; + const symbolProviderRegistryPromise = new Promise(resolve => symbolProviderRegistryPromiseResolve = resolve); + + // Resolve promise when registry knows model + const symbolProviderListener = disposables.add(DocumentSymbolProviderRegistry.onDidChange(() => { + if (DocumentSymbolProviderRegistry.has(model)) { + symbolProviderListener.dispose(); + + symbolProviderRegistryPromiseResolve(true); + } + })); + + // Resolve promise when we get disposed too + disposables.add(toDisposable(() => symbolProviderRegistryPromiseResolve(false))); + + return symbolProviderRegistryPromise; + } + + private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + const disposables = new DisposableStore(); + + // Goto symbol once picked + disposables.add(picker.onDidAccept(event => { + const [item] = picker.selectedItems; + if (item && item.range) { + this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground }); + + if (!event.inBackground) { + picker.hide(); + } + } + })); + + // Goto symbol side by side if enabled + disposables.add(picker.onDidTriggerItemButton(({ item }) => { + if (item && item.range) { + this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, forceSideBySide: true }); + + picker.hide(); + } + })); + + // Resolve symbols from document once and reuse this + // request for all filtering and typing then on + const symbolsPromise = this.getDocumentSymbols(model, true, token); + + // Set initial picks and update on type + let picksCts: CancellationTokenSource | undefined = undefined; + const updatePickerItems = async () => { + + // Cancel any previous ask for picks and busy + picksCts?.dispose(true); + picker.busy = false; + + // Create new cancellation source for this run + picksCts = new CancellationTokenSource(token); + + // Collect symbol picks + picker.busy = true; + try { + const query = prepareQuery(picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim()); + const items = await this.doGetSymbolPicks(symbolsPromise, query, undefined, picksCts.token); + if (token.isCancellationRequested) { + return; + } + + if (items.length > 0) { + picker.items = items; + } else { + if (query.original.length > 0) { + this.provideLabelPick(picker, localize('noMatchingSymbolResults', "No matching editor symbols")); + } else { + this.provideLabelPick(picker, localize('noSymbolResults', "No editor symbols")); + } + } + } finally { + if (!token.isCancellationRequested) { + picker.busy = false; + } + } + }; + disposables.add(picker.onDidChangeValue(() => updatePickerItems())); + updatePickerItems(); + + // Reveal and decorate when active item changes + // However, ignore the very first event so that + // opening the picker is not immediately revealing + // and decorating the first entry. + let ignoreFirstActiveEvent = true; + disposables.add(picker.onDidChangeActive(() => { + const [item] = picker.activeItems; + if (item && item.range) { + if (ignoreFirstActiveEvent) { + ignoreFirstActiveEvent = false; + return; + } + + // Reveal + editor.revealRangeInCenter(item.range.selection, ScrollType.Smooth); + + // Decorate + this.addDecorations(editor, item.range.decoration); + } + })); + + return disposables; + } + + protected async doGetSymbolPicks(symbolsPromise: Promise, query: IPreparedQuery, options: { extraContainerLabel?: string } | undefined, token: CancellationToken): Promise> { + const symbols = await symbolsPromise; + if (token.isCancellationRequested) { + return []; + } + + const filterBySymbolKind = query.original.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0; + const filterPos = filterBySymbolKind ? 1 : 0; + + // Split between symbol and container query + let symbolQuery: IPreparedQuery; + let containerQuery: IPreparedQuery | undefined; + if (query.values && query.values.length > 1) { + symbolQuery = pieceToQuery(query.values[0]); // symbol: only match on first part + containerQuery = pieceToQuery(query.values.slice(1)); // container: match on all but first parts + } else { + symbolQuery = query; + } + + // Convert to symbol picks and apply filtering + const filteredSymbolPicks: IGotoSymbolQuickPickItem[] = []; + for (let index = 0; index < symbols.length; index++) { + const symbol = symbols[index]; + + const symbolLabel = trim(symbol.name); + const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`; + const symbolLabelIconOffset = symbolLabelWithIcon.length - symbolLabel.length; + + let containerLabel = symbol.containerName; + if (options?.extraContainerLabel) { + if (containerLabel) { + containerLabel = `${options.extraContainerLabel} • ${containerLabel}`; + } else { + containerLabel = options.extraContainerLabel; + } + } + + let symbolScore: number | undefined = undefined; + let symbolMatches: IMatch[] | undefined = undefined; + + let containerScore: number | undefined = undefined; + let containerMatches: IMatch[] | undefined = undefined; + + if (query.original.length > filterPos) { + + // First: try to score on the entire query, it is possible that + // the symbol matches perfectly (e.g. searching for "change log" + // can be a match on a markdown symbol "change log"). In that + // case we want to skip the container query altogether. + let skipContainerQuery = false; + if (symbolQuery !== query) { + [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, { ...query, values: undefined /* disable multi-query support */ }, filterPos, symbolLabelIconOffset); + if (typeof symbolScore === 'number') { + skipContainerQuery = true; // since we consumed the query, skip any container matching + } + } + + // Otherwise: score on the symbol query and match on the container later + if (typeof symbolScore !== 'number') { + [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, symbolQuery, filterPos, symbolLabelIconOffset); + if (typeof symbolScore !== 'number') { + continue; + } + } + + // Score by container if specified + if (!skipContainerQuery && containerQuery) { + if (containerLabel && containerQuery.original.length > 0) { + [containerScore, containerMatches] = scoreFuzzy2(containerLabel, containerQuery); + } + + if (typeof containerScore !== 'number') { + continue; + } + + if (typeof symbolScore === 'number') { + symbolScore += containerScore; // boost symbolScore by containerScore + } + } + } + + const deprecated = symbol.tags && symbol.tags.indexOf(SymbolTag.Deprecated) >= 0; + + filteredSymbolPicks.push({ + index, + kind: symbol.kind, + score: symbolScore, + label: symbolLabelWithIcon, + ariaLabel: symbolLabel, + description: containerLabel, + highlights: deprecated ? undefined : { + label: symbolMatches, + description: containerMatches + }, + range: { + selection: Range.collapseToStart(symbol.selectionRange), + decoration: symbol.range + }, + strikethrough: deprecated, + buttons: (() => { + const openSideBySideDirection = this.options?.openSideBySideDirection ? this.options?.openSideBySideDirection() : undefined; + if (!openSideBySideDirection) { + return undefined; + } + + return [ + { + iconClass: openSideBySideDirection === 'right' ? Codicon.splitHorizontal.classNames : Codicon.splitVertical.classNames, + tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom") + } + ]; + })() + }); + } + + // Sort by score + const sortedFilteredSymbolPicks = filteredSymbolPicks.sort((symbolA, symbolB) => filterBySymbolKind ? + this.compareByKindAndScore(symbolA, symbolB) : + this.compareByScore(symbolA, symbolB) + ); + + // Add separator for types + // - @ only total number of symbols + // - @: grouped by symbol kind + let symbolPicks: Array = []; + if (filterBySymbolKind) { + let lastSymbolKind: SymbolKind | undefined = undefined; + let lastSeparator: IQuickPickSeparator | undefined = undefined; + let lastSymbolKindCounter = 0; + + function updateLastSeparatorLabel(): void { + if (lastSeparator && typeof lastSymbolKind === 'number' && lastSymbolKindCounter > 0) { + lastSeparator.label = format(NLS_SYMBOL_KIND_CACHE[lastSymbolKind] || FALLBACK_NLS_SYMBOL_KIND, lastSymbolKindCounter); + } + } + + for (const symbolPick of sortedFilteredSymbolPicks) { + + // Found new kind + if (lastSymbolKind !== symbolPick.kind) { + + // Update last separator with number of symbols we found for kind + updateLastSeparatorLabel(); + + lastSymbolKind = symbolPick.kind; + lastSymbolKindCounter = 1; + + // Add new separator for new kind + lastSeparator = { type: 'separator' }; + symbolPicks.push(lastSeparator); + } + + // Existing kind, keep counting + else { + lastSymbolKindCounter++; + } + + // Add to final result + symbolPicks.push(symbolPick); + } + + // Update last separator with number of symbols we found for kind + updateLastSeparatorLabel(); + } else if (sortedFilteredSymbolPicks.length > 0) { + symbolPicks = [ + { label: localize('symbols', "symbols ({0})", filteredSymbolPicks.length), type: 'separator' }, + ...sortedFilteredSymbolPicks + ]; + } + + return symbolPicks; + } + + private compareByScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number { + if (typeof symbolA.score !== 'number' && typeof symbolB.score === 'number') { + return 1; + } else if (typeof symbolA.score === 'number' && typeof symbolB.score !== 'number') { + return -1; + } + + if (typeof symbolA.score === 'number' && typeof symbolB.score === 'number') { + if (symbolA.score > symbolB.score) { + return -1; + } else if (symbolA.score < symbolB.score) { + return 1; + } + } + + if (symbolA.index < symbolB.index) { + return -1; + } else if (symbolA.index > symbolB.index) { + return 1; + } + + return 0; + } + + private compareByKindAndScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number { + const kindA = NLS_SYMBOL_KIND_CACHE[symbolA.kind] || FALLBACK_NLS_SYMBOL_KIND; + const kindB = NLS_SYMBOL_KIND_CACHE[symbolB.kind] || FALLBACK_NLS_SYMBOL_KIND; + + // Sort by type first if scoped search + const result = kindA.localeCompare(kindB); + if (result === 0) { + return this.compareByScore(symbolA, symbolB); + } + + return result; + } + + protected async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise { + const model = await OutlineModel.create(document, token); + if (token.isCancellationRequested) { + return []; + } + + const roots: DocumentSymbol[] = []; + for (const child of model.children.values()) { + if (child instanceof OutlineElement) { + roots.push(child.symbol); + } else { + roots.push(...Iterable.map(child.children.values(), child => child.symbol)); + } + } + + let flatEntries: DocumentSymbol[] = []; + if (flatten) { + this.flattenDocumentSymbols(flatEntries, roots, ''); + } else { + flatEntries = roots; + } + + return flatEntries.sort((symbolA, symbolB) => Range.compareRangesUsingStarts(symbolA.range, symbolB.range)); + } + + private flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void { + for (const entry of entries) { + bucket.push({ + kind: entry.kind, + tags: entry.tags, + name: entry.name, + detail: entry.detail, + containerName: entry.containerName || overrideContainerLabel, + range: entry.range, + selectionRange: entry.selectionRange, + children: undefined, // we flatten it... + }); + + // Recurse over children + if (entry.children) { + this.flattenDocumentSymbols(bucket, entry.children, entry.name); + } + } + } +} + +// #region NLS Helpers + +const FALLBACK_NLS_SYMBOL_KIND = localize('property', "properties ({0})"); +const NLS_SYMBOL_KIND_CACHE: { [type: number]: string } = { + [SymbolKind.Method]: localize('method', "methods ({0})"), + [SymbolKind.Function]: localize('function', "functions ({0})"), + [SymbolKind.Constructor]: localize('_constructor', "constructors ({0})"), + [SymbolKind.Variable]: localize('variable', "variables ({0})"), + [SymbolKind.Class]: localize('class', "classes ({0})"), + [SymbolKind.Struct]: localize('struct', "structs ({0})"), + [SymbolKind.Event]: localize('event', "events ({0})"), + [SymbolKind.Operator]: localize('operator', "operators ({0})"), + [SymbolKind.Interface]: localize('interface', "interfaces ({0})"), + [SymbolKind.Namespace]: localize('namespace', "namespaces ({0})"), + [SymbolKind.Package]: localize('package', "packages ({0})"), + [SymbolKind.TypeParameter]: localize('typeParameter', "type parameters ({0})"), + [SymbolKind.Module]: localize('modules', "modules ({0})"), + [SymbolKind.Property]: localize('property', "properties ({0})"), + [SymbolKind.Enum]: localize('enum', "enumerations ({0})"), + [SymbolKind.EnumMember]: localize('enumMember', "enumeration members ({0})"), + [SymbolKind.String]: localize('string', "strings ({0})"), + [SymbolKind.File]: localize('file', "files ({0})"), + [SymbolKind.Array]: localize('array', "arrays ({0})"), + [SymbolKind.Number]: localize('number', "numbers ({0})"), + [SymbolKind.Boolean]: localize('boolean', "booleans ({0})"), + [SymbolKind.Object]: localize('object', "objects ({0})"), + [SymbolKind.Key]: localize('key', "keys ({0})"), + [SymbolKind.Field]: localize('field', "fields ({0})"), + [SymbolKind.Constant]: localize('constant', "constants ({0})") +}; + +//#endregion diff --git a/src/vs/editor/contrib/rename/media/onTypeRename.css b/src/vs/editor/contrib/rename/media/onTypeRename.css new file mode 100644 index 00000000000..16bb0178528 --- /dev/null +++ b/src/vs/editor/contrib/rename/media/onTypeRename.css @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .on-type-rename-decoration { + border-left: 1px solid transparent; + /* So border can be transparent */ + background-clip: padding-box; +} diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/rename/onTypeRename.ts new file mode 100644 index 00000000000..105dfb2ddb2 --- /dev/null +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -0,0 +1,463 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/onTypeRename'; +import * as nls from 'vs/nls'; +import { registerEditorContribution, registerModelAndPositionCommand, EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import * as arrays from 'vs/base/common/arrays'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Position, IPosition } from 'vs/editor/common/core/position'; +import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { OnTypeRenameProviderRegistry } from 'vs/editor/common/modes'; +import { first, createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { isPromiseCanceledError, onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; +import * as strings from 'vs/base/common/strings'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; + +export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('onTypeRenameInputVisible', false); + +export class OnTypeRenameContribution extends Disposable implements IEditorContribution { + + public static readonly ID = 'editor.contrib.onTypeRename'; + + private static readonly DECORATION = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges, + className: 'on-type-rename-decoration' + }); + + static get(editor: ICodeEditor): OnTypeRenameContribution { + return editor.getContribution(OnTypeRenameContribution.ID); + } + + private _debounceDuration = 200; + + private readonly _editor: ICodeEditor; + private _enabled: boolean; + + private readonly _visibleContextKey: IContextKey; + + private _rangeUpdateTriggerPromise: Promise | null; + private _rangeSyncTriggerPromise: Promise | null; + + private _currentRequest: CancelablePromise | null; + private _currentRequestPosition: Position | null; + private _currentRequestModelVersion: number | null; + + private _currentDecorations: string[]; // The one at index 0 is the reference one + private _languageWordPattern: RegExp | null; + private _currentWordPattern: RegExp | null; + private _ignoreChangeEvent: boolean; + + private readonly _localToDispose = this._register(new DisposableStore()); + + constructor( + editor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + this._editor = editor; + this._enabled = false; + this._visibleContextKey = CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); + + this._currentDecorations = []; + this._languageWordPattern = null; + this._currentWordPattern = null; + this._ignoreChangeEvent = false; + this._localToDispose = this._register(new DisposableStore()); + + this._rangeUpdateTriggerPromise = null; + this._rangeSyncTriggerPromise = null; + + this._currentRequest = null; + this._currentRequestPosition = null; + this._currentRequestModelVersion = null; + + this._register(this._editor.onDidChangeModel(() => this.reinitialize())); + + this._register(this._editor.onDidChangeConfiguration(e => { + if (e.hasChanged(EditorOption.renameOnType)) { + this.reinitialize(); + } + })); + this._register(OnTypeRenameProviderRegistry.onDidChange(() => this.reinitialize())); + this._register(this._editor.onDidChangeModelLanguage(() => this.reinitialize())); + + this.reinitialize(); + } + + private reinitialize() { + const model = this._editor.getModel(); + const isEnabled = model !== null && this._editor.getOption(EditorOption.renameOnType) && OnTypeRenameProviderRegistry.has(model); + if (isEnabled === this._enabled) { + return; + } + + this._enabled = isEnabled; + + this.clearRanges(); + this._localToDispose.clear(); + + if (!isEnabled || model === null) { + return; + } + + this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); + this._localToDispose.add(model.onDidChangeLanguageConfiguration(() => { + this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); + })); + + const rangeUpdateScheduler = new Delayer(this._debounceDuration); + const triggerRangeUpdate = () => { + this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges(), this._debounceDuration); + }; + const rangeSyncScheduler = new Delayer(0); + const triggerRangeSync = (decorations: string[]) => { + this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations)); + }; + this._localToDispose.add(this._editor.onDidChangeCursorPosition(() => { + triggerRangeUpdate(); + })); + this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { + if (!this._ignoreChangeEvent) { + if (this._currentDecorations.length > 0) { + const referenceRange = model.getDecorationRange(this._currentDecorations[0]); + if (referenceRange && e.changes.every(c => referenceRange.intersectRanges(c.range))) { + triggerRangeSync(this._currentDecorations); + return; + } + } + } + triggerRangeUpdate(); + })); + this._localToDispose.add({ + dispose: () => { + rangeUpdateScheduler.cancel(); + rangeSyncScheduler.cancel(); + } + }); + this.updateRanges(); + } + + private _syncRanges(decorations: string[]): void { + // dalayed invocation, make sure we're still on + if (!this._editor.hasModel() || decorations !== this._currentDecorations || decorations.length === 0) { + // nothing to do + return; + } + + const model = this._editor.getModel(); + const referenceRange = model.getDecorationRange(decorations[0]); + + if (!referenceRange || referenceRange.startLineNumber !== referenceRange.endLineNumber) { + return this.clearRanges(); + } + + const referenceValue = model.getValueInRange(referenceRange); + if (this._currentWordPattern) { + const match = referenceValue.match(this._currentWordPattern); + const matchLength = match ? match[0].length : 0; + if (matchLength !== referenceValue.length) { + return this.clearRanges(); + } + } + + let edits: IIdentifiedSingleEditOperation[] = []; + for (let i = 1, len = decorations.length; i < len; i++) { + const mirrorRange = model.getDecorationRange(decorations[i]); + if (!mirrorRange) { + continue; + } + if (mirrorRange.startLineNumber !== mirrorRange.endLineNumber) { + edits.push({ + range: mirrorRange, + text: referenceValue + }); + } else { + let oldValue = model.getValueInRange(mirrorRange); + let newValue = referenceValue; + let rangeStartColumn = mirrorRange.startColumn; + let rangeEndColumn = mirrorRange.endColumn; + + const commonPrefixLength = strings.commonPrefixLength(oldValue, newValue); + rangeStartColumn += commonPrefixLength; + oldValue = oldValue.substr(commonPrefixLength); + newValue = newValue.substr(commonPrefixLength); + + const commonSuffixLength = strings.commonSuffixLength(oldValue, newValue); + rangeEndColumn -= commonSuffixLength; + oldValue = oldValue.substr(0, oldValue.length - commonSuffixLength); + newValue = newValue.substr(0, newValue.length - commonSuffixLength); + + if (rangeStartColumn !== rangeEndColumn || newValue.length !== 0) { + edits.push({ + range: new Range(mirrorRange.startLineNumber, rangeStartColumn, mirrorRange.endLineNumber, rangeEndColumn), + text: newValue + }); + } + } + } + + if (edits.length === 0) { + return; + } + + try { + this._ignoreChangeEvent = true; + const prevEditOperationType = this._editor._getViewModel().getPrevEditOperationType(); + this._editor.executeEdits('onTypeRename', edits); + this._editor._getViewModel().setPrevEditOperationType(prevEditOperationType); + } finally { + this._ignoreChangeEvent = false; + } + } + + public dispose(): void { + this.clearRanges(); + super.dispose(); + } + + public clearRanges(): void { + this._visibleContextKey.set(false); + this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, []); + if (this._currentRequest) { + this._currentRequest.cancel(); + this._currentRequest = null; + this._currentRequestPosition = null; + } + } + + public get currentUpdateTriggerPromise(): Promise { + return this._rangeUpdateTriggerPromise || Promise.resolve(); + } + + public get currentSyncTriggerPromise(): Promise { + return this._rangeSyncTriggerPromise || Promise.resolve(); + } + + public async updateRanges(force = false): Promise { + if (!this._editor.hasModel()) { + this.clearRanges(); + return; + } + + const position = this._editor.getPosition(); + if (!this._enabled && !force || this._editor.getSelections().length > 1) { + // disabled or multicursor + this.clearRanges(); + return; + } + + const model = this._editor.getModel(); + const modelVersionId = model.getVersionId(); + if (this._currentRequestPosition && this._currentRequestModelVersion === modelVersionId) { + if (position.equals(this._currentRequestPosition)) { + return; // same position + } + if (this._currentDecorations && this._currentDecorations.length > 0) { + const range = model.getDecorationRange(this._currentDecorations[0]); + if (range && range.containsPosition(position)) { + return; // just moving inside the existing primary range + } + } + } + + this._currentRequestPosition = position; + this._currentRequestModelVersion = modelVersionId; + const request = createCancelablePromise(async token => { + try { + const response = await getOnTypeRenameRanges(model, position, token); + if (request !== this._currentRequest) { + return; + } + this._currentRequest = null; + if (modelVersionId !== model.getVersionId()) { + return; + } + + let ranges: IRange[] = []; + if (response?.ranges) { + ranges = response.ranges; + } + + this._currentWordPattern = response?.wordPattern || this._languageWordPattern; + + let foundReferenceRange = false; + for (let i = 0, len = ranges.length; i < len; i++) { + if (Range.containsPosition(ranges[i], position)) { + foundReferenceRange = true; + if (i !== 0) { + const referenceRange = ranges[i]; + ranges.splice(i, 1); + ranges.unshift(referenceRange); + } + break; + } + } + + if (!foundReferenceRange) { + // Cannot do on type rename if the ranges are not where the cursor is... + this.clearRanges(); + return; + } + + const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: OnTypeRenameContribution.DECORATION })); + this._visibleContextKey.set(true); + this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations); + } catch (err) { + if (!isPromiseCanceledError(err)) { + onUnexpectedError(err); + } + if (this._currentRequest === request || !this._currentRequest) { + // stop if we are still the latest request + this.clearRanges(); + } + } + }); + this._currentRequest = request; + return request; + } + + // for testing + public setDebounceDuration(timeInMS: number) { + this._debounceDuration = timeInMS; + } + + // private printDecorators(model: ITextModel) { + // return this._currentDecorations.map(d => { + // const range = model.getDecorationRange(d); + // if (range) { + // return this.printRange(range); + // } + // return 'invalid'; + // }).join(','); + // } + + // private printChanges(changes: IModelContentChange[]) { + // return changes.map(c => { + // return `${this.printRange(c.range)} - ${c.text}`; + // } + // ).join(','); + // } + + // private printRange(range: IRange) { + // return `${range.startLineNumber},${range.startColumn}/${range.endLineNumber},${range.endColumn}`; + // } +} + +export class OnTypeRenameAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.onTypeRename', + label: nls.localize('onTypeRename.label', "On Type Rename Symbol"), + alias: 'On Type Rename Symbol', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasRenameProvider), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F2, + weight: KeybindingWeight.EditorContrib + } + }); + } + + runCommand(accessor: ServicesAccessor, args: [URI, IPosition]): void | Promise { + const editorService = accessor.get(ICodeEditorService); + const [uri, pos] = Array.isArray(args) && args || [undefined, undefined]; + + if (URI.isUri(uri) && Position.isIPosition(pos)) { + return editorService.openCodeEditor({ resource: uri }, editorService.getActiveCodeEditor()).then(editor => { + if (!editor) { + return; + } + editor.setPosition(pos); + editor.invokeWithinContext(accessor => { + this.reportTelemetry(accessor, editor); + return this.run(accessor, editor); + }); + }, onUnexpectedError); + } + + return super.runCommand(accessor, args); + } + + run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = OnTypeRenameContribution.get(editor); + if (controller) { + return Promise.resolve(controller.updateRanges(true)); + } + return Promise.resolve(); + } +} + +const OnTypeRenameCommand = EditorCommand.bindToContribution(OnTypeRenameContribution.get); +registerEditorCommand(new OnTypeRenameCommand({ + id: 'cancelOnTypeRenameInput', + precondition: CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE, + handler: x => x.clearRanges(), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + weight: KeybindingWeight.EditorContrib + 99, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] + } +})); + + +export function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<{ + ranges: IRange[], + wordPattern?: RegExp +} | undefined | null> { + const orderedByScore = OnTypeRenameProviderRegistry.ordered(model); + + // in order of score ask the occurrences provider + // until someone response with a good result + // (good = none empty array) + return first<{ + ranges: IRange[], + wordPattern?: RegExp + } | undefined>(orderedByScore.map(provider => () => { + return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((res) => { + if (!res) { + return undefined; + } + + return { + ranges: res.ranges, + wordPattern: res.wordPattern || provider.wordPattern + }; + }, (err) => { + onUnexpectedExternalError(err); + return undefined; + }); + + }), result => !!result && arrays.isNonEmptyArray(result?.ranges)); +} + +export const editorOnTypeRenameBackground = registerColor('editor.onTypeRenameBackground', { dark: Color.fromHex('#f00').transparent(0.3), light: Color.fromHex('#f00').transparent(0.3), hc: Color.fromHex('#f00').transparent(0.3) }, nls.localize('editorOnTypeRenameBackground', 'Background color when the editor auto renames on type.')); +registerThemingParticipant((theme, collector) => { + const editorOnTypeRenameBackgroundColor = theme.getColor(editorOnTypeRenameBackground); + if (editorOnTypeRenameBackgroundColor) { + collector.addRule(`.monaco-editor .on-type-rename-decoration { background: ${editorOnTypeRenameBackgroundColor}; border-left-color: ${editorOnTypeRenameBackgroundColor}; }`); + } +}); + +registerModelAndPositionCommand('_executeRenameOnTypeProvider', (model, position) => getOnTypeRenameRanges(model, position, CancellationToken.None)); + +registerEditorContribution(OnTypeRenameContribution.ID, OnTypeRenameContribution); +registerEditorAction(OnTypeRenameAction); diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index e39a0949664..415473087a8 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { illegalArgument, onUnexpectedError } from 'vs/base/common/errors'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -22,22 +22,23 @@ import { MessageController } from 'vs/editor/contrib/message/messageController'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IdleValue, raceCancellation } from 'vs/base/common/async'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, ConfigurationScope, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { assertType } from 'vs/base/common/types'; class RenameSkeleton { private readonly _providers: RenameProvider[]; + private _providerRenameIdx: number = 0; constructor( private readonly model: ITextModel, @@ -51,30 +52,45 @@ class RenameSkeleton { } async resolveRenameLocation(token: CancellationToken): Promise { - const firstProvider = this._providers[0]; - if (!firstProvider) { - return undefined; - } - let res: RenameLocation & Rejection | undefined; - if (firstProvider.resolveRenameLocation) { - res = withNullAsUndefined(await firstProvider.resolveRenameLocation(this.model, this.position, token)); - } + const rejects: string[] = []; - if (!res) { - const word = this.model.getWordAtPosition(this.position); - if (word) { - return { - range: new Range(this.position.lineNumber, word.startColumn, this.position.lineNumber, word.endColumn), - text: word.word - }; + for (this._providerRenameIdx = 0; this._providerRenameIdx < this._providers.length; this._providerRenameIdx++) { + const provider = this._providers[this._providerRenameIdx]; + if (!provider.resolveRenameLocation) { + break; } + let res = await provider.resolveRenameLocation(this.model, this.position, token); + if (!res) { + continue; + } + if (res.rejectReason) { + rejects.push(res.rejectReason); + continue; + } + return res; } - return res; + const word = this.model.getWordAtPosition(this.position); + if (!word) { + return { + range: Range.fromPositions(this.position), + text: '', + rejectReason: rejects.length > 0 ? rejects.join('\n') : undefined + }; + } + return { + range: new Range(this.position.lineNumber, word.startColumn, this.position.lineNumber, word.endColumn), + text: word.word, + rejectReason: rejects.length > 0 ? rejects.join('\n') : undefined + }; } - async provideRenameEdits(newName: string, i: number, rejects: string[], token: CancellationToken): Promise { + async provideRenameEdits(newName: string, token: CancellationToken): Promise { + return this._provideRenameEdits(newName, this._providerRenameIdx, [], token); + } + + private async _provideRenameEdits(newName: string, i: number, rejects: string[], token: CancellationToken): Promise { const provider = this._providers[i]; if (!provider) { return { @@ -85,16 +101,21 @@ class RenameSkeleton { const result = await provider.provideRenameEdits(this.model, this.position, newName, token); if (!result) { - return this.provideRenameEdits(newName, i + 1, rejects.concat(nls.localize('no result', "No result.")), token); + return this._provideRenameEdits(newName, i + 1, rejects.concat(nls.localize('no result', "No result.")), token); } else if (result.rejectReason) { - return this.provideRenameEdits(newName, i + 1, rejects.concat(result.rejectReason), token); + return this._provideRenameEdits(newName, i + 1, rejects.concat(result.rejectReason), token); } return result; } } export async function rename(model: ITextModel, position: Position, newName: string): Promise { - return new RenameSkeleton(model, position).provideRenameEdits(newName, 0, [], CancellationToken.None); + const skeleton = new RenameSkeleton(model, position); + const loc = await skeleton.resolveRenameLocation(CancellationToken.None); + if (loc?.rejectReason) { + return { edits: [], rejectReason: loc.rejectReason }; + } + return skeleton.provideRenameEdits(newName, CancellationToken.None); } // --- register actions and commands @@ -168,6 +189,8 @@ class RenameController implements IEditorContribution { if (this._cts.token.isCancellationRequested) { return undefined; } + this._cts.dispose(); + this._cts = new EditorStateCancellationTokenSource(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value, loc.range); // do rename at location let selection = this.editor.getSelection(); @@ -179,8 +202,8 @@ class RenameController implements IEditorContribution { selectionEnd = Math.min(loc.range.endColumn, selection.endColumn) - loc.range.startColumn; } - const supportPreview = this._configService.getValue(this.editor.getModel().uri, 'editor.rename.enablePreview'); - const inputFieldResult = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview); + const supportPreview = this._bulkEditService.hasPreviewHandler() && this._configService.getValue(this.editor.getModel().uri, 'editor.rename.enablePreview'); + const inputFieldResult = await this._renameInputField.value.getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview, this._cts.token); // no result, only hint to focus the editor or not if (typeof inputFieldResult === 'boolean') { @@ -192,7 +215,7 @@ class RenameController implements IEditorContribution { this.editor.focus(); - const renameOperation = raceCancellation(skeleton.provideRenameEdits(inputFieldResult.newName, 0, [], this._cts.token), this._cts.token).then(async renameResult => { + const renameOperation = raceCancellation(skeleton.provideRenameEdits(inputFieldResult.newName, this._cts.token), this._cts.token).then(async renameResult => { if (!renameResult || !this.editor.hasModel()) { return; @@ -203,10 +226,11 @@ class RenameController implements IEditorContribution { return; } - this._bulkEditService.apply(renameResult, { + this._bulkEditService.apply(ResourceEdit.convert(renameResult), { editor: this.editor, showPreview: inputFieldResult.wantsPreview, - label: nls.localize('label', "Renaming '{0}'", loc?.text) + label: nls.localize('label', "Renaming '{0}'", loc?.text), + quotableLabel: nls.localize('quotableLabel', "Renaming {0}", loc?.text), }).then(result => { if (result.ariaSummary) { alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, result.ariaSummary)); @@ -227,11 +251,11 @@ class RenameController implements IEditorContribution { } acceptRenameInput(wantsPreview: boolean): void { - this._renameInputField.getValue().acceptInput(wantsPreview); + this._renameInputField.value.acceptInput(wantsPreview); } cancelRenameInput(): void { - this._renameInputField.getValue().cancelInput(true); + this._renameInputField.value.cancelInput(true); } } @@ -327,16 +351,14 @@ registerEditorCommand(new RenameCommand({ // ---- api bridge command -registerDefaultLanguageCommand('_executeDocumentRenameProvider', function (model, position, args) { - let { newName } = args; - if (typeof newName !== 'string') { - throw illegalArgument('newName'); - } +registerModelAndPositionCommand('_executeDocumentRenameProvider', function (model, position, ...args) { + const [newName] = args; + assertType(typeof newName === 'string'); return rename(model, position, newName); }); -//todo@joh use editor options world +//todo@jrieken use editor options world Registry.as(Extensions.Configuration).registerConfiguration({ id: 'editor', properties: { diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index 1a3e60e0071..c794e4ccc4a 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -7,15 +7,15 @@ import 'vs/css!./renameInputField'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; -import { IRange, Range } from 'vs/editor/common/core/range'; +import { IRange } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { inputBackground, inputBorder, inputForeground, widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { toggleClass } from 'vs/base/browser/dom'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false); @@ -53,7 +53,7 @@ export class RenameInputField implements IContentWidget { } })); - this._disposables.add(_themeService.onThemeChange(this._updateStyles, this)); + this._disposables.add(_themeService.onDidColorThemeChange(this._updateStyles, this)); } dispose(): void { @@ -82,18 +82,18 @@ export class RenameInputField implements IContentWidget { const updateLabel = () => { const [accept, preview] = this._acceptKeybindings; this._keybindingService.lookupKeybinding(accept); - this._label!.innerText = localize('label', "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); + this._label!.innerText = localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Rename, Shift+F2 to Preview"'] }, "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); }; updateLabel(); this._disposables.add(this._keybindingService.onDidUpdateKeybindings(updateLabel)); this._updateFont(); - this._updateStyles(this._themeService.getTheme()); + this._updateStyles(this._themeService.getColorTheme()); } return this._domNode; } - private _updateStyles(theme: ITheme): void { + private _updateStyles(theme: IColorTheme): void { if (!this._input || !this._domNode) { return; } @@ -134,6 +134,14 @@ export class RenameInputField implements IContentWidget { }; } + afterRender(position: ContentWidgetPositionPreference | null): void { + if (!position) { + // cancel rename when input widget isn't rendered anymore + this.cancelInput(true); + } + } + + private _currentAcceptInput?: (wantsPreview: boolean) => void; private _currentCancelInput?: (focusEditor: boolean) => void; @@ -149,9 +157,9 @@ export class RenameInputField implements IContentWidget { } } - getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean): Promise { + getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean, token: CancellationToken): Promise { - toggleClass(this._domNode!, 'preview', supportPreview); + this._domNode!.classList.toggle('preview', supportPreview); this._position = new Position(where.startLineNumber, where.startColumn); this._input!.value = value; @@ -185,14 +193,7 @@ export class RenameInputField implements IContentWidget { }); }; - let onCursorChanged = () => { - const editorPosition = this._editor.getPosition(); - if (!editorPosition || !Range.containsPosition(where, editorPosition)) { - this.cancelInput(true); - } - }; - - disposeOnDone.add(this._editor.onDidChangeCursorSelection(onCursorChanged)); + token.onCancellationRequested(() => this.cancelInput(true)); disposeOnDone.add(this._editor.onDidBlurEditorWidget(() => this.cancelInput(false))); this._show(); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts new file mode 100644 index 00000000000..05dba5e4b62 --- /dev/null +++ b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts @@ -0,0 +1,457 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { Handler } from 'vs/editor/common/editorCommon'; +import * as modes from 'vs/editor/common/modes'; +import { OnTypeRenameContribution } from 'vs/editor/contrib/rename/onTypeRename'; +import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; +import { ITextModel } from 'vs/editor/common/model'; +import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; + +const mockFile = URI.parse('test:somefile.ttt'); +const mockFileSelector = { scheme: 'test' }; +const timeout = 30; + +interface TestEditor { + setPosition(pos: Position): Promise; + setSelection(sel: IRange): Promise; + trigger(source: string | null | undefined, handlerId: string, payload: any): Promise; + undo(): void; + redo(): void; +} + +suite('On type rename', () => { + const disposables = new DisposableStore(); + + setup(() => { + disposables.clear(); + }); + + teardown(() => { + disposables.clear(); + }); + + function createMockEditor(text: string | string[]): ITestCodeEditor { + const model = typeof text === 'string' + ? createTextModel(text, undefined, undefined, mockFile) + : createTextModel(text.join('\n'), undefined, undefined, mockFile); + + const editor = createTestCodeEditor({ model }); + disposables.add(model); + disposables.add(editor); + + return editor; + } + + + function testCase( + name: string, + initialState: { text: string | string[], responseWordPattern?: RegExp, providerWordPattern?: RegExp }, + operations: (editor: TestEditor) => Promise, + expectedEndText: string | string[] + ) { + test(name, async () => { + disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, { + wordPattern: initialState.providerWordPattern, + provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) { + const wordAtPos = model.getWordAtPosition(pos); + if (wordAtPos) { + const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false); + assert.ok(matches.length > 0); + return { ranges: matches.map(m => m.range), wordPattern: initialState.responseWordPattern }; + } + return { ranges: [], wordPattern: initialState.responseWordPattern }; + } + })); + + const editor = createMockEditor(initialState.text); + editor.updateOptions({ renameOnType: true }); + const ontypeRenameContribution = editor.registerAndInstantiateContribution( + OnTypeRenameContribution.ID, + OnTypeRenameContribution + ); + ontypeRenameContribution.setDebounceDuration(0); + + const testEditor: TestEditor = { + setPosition(pos: Position) { + editor.setPosition(pos); + return ontypeRenameContribution.currentUpdateTriggerPromise; + }, + setSelection(sel: IRange) { + editor.setSelection(sel); + return ontypeRenameContribution.currentUpdateTriggerPromise; + }, + trigger(source: string | null | undefined, handlerId: string, payload: any) { + editor.trigger(source, handlerId, payload); + return ontypeRenameContribution.currentSyncTriggerPromise; + }, + undo() { + CoreEditingCommands.Undo.runEditorCommand(null, editor, null); + }, + redo() { + CoreEditingCommands.Redo.runEditorCommand(null, editor, null); + } + }; + + await operations(testEditor); + + return new Promise((resolve) => { + setTimeout(() => { + if (typeof expectedEndText === 'string') { + assert.equal(editor.getModel()!.getValue(), expectedEndText); + } else { + assert.equal(editor.getModel()!.getValue(), expectedEndText.join('\n')); + } + resolve(); + }, timeout); + }); + }); + } + + const state = { + text: '' + }; + + /** + * Simple insertion + */ + testCase('Simple insert - initial', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Simple insert - middle', state, async (editor) => { + const pos = new Position(1, 3); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Simple insert - end', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + /** + * Simple insertion - end + */ + testCase('Simple insert end - initial', state, async (editor) => { + const pos = new Position(1, 8); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Simple insert end - middle', state, async (editor) => { + const pos = new Position(1, 9); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Simple insert end - end', state, async (editor) => { + const pos = new Position(1, 11); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + /** + * Boundary insertion + */ + testCase('Simple insert - out of boundary', state, async (editor) => { + const pos = new Position(1, 1); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, 'i'); + + testCase('Simple insert - out of boundary 2', state, async (editor) => { + const pos = new Position(1, 6); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, 'i'); + + testCase('Simple insert - out of boundary 3', state, async (editor) => { + const pos = new Position(1, 7); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Simple insert - out of boundary 4', state, async (editor) => { + const pos = new Position(1, 12); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, 'i'); + + /** + * Insert + Move + */ + testCase('Continuous insert', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Insert - move - insert', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + await editor.setPosition(new Position(1, 4)); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Insert - move - insert outside region', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + await editor.setPosition(new Position(1, 7)); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, 'i'); + + /** + * Selection insert + */ + testCase('Selection insert - simple', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.setSelection(new Range(1, 2, 1, 3)); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Selection insert - whole', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.setSelection(new Range(1, 2, 1, 5)); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Selection insert - across boundary', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.setSelection(new Range(1, 1, 1, 3)); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, 'ioo>'); + + /** + * @todo + * Undefined behavior + */ + // testCase('Selection insert - across two boundary', state, async (editor) => { + // const pos = new Position(1, 2); + // await editor.setPosition(pos); + // await ontypeRenameContribution.updateLinkedUI(pos); + // await editor.setSelection(new Range(1, 4, 1, 9)); + // await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + // }, ''); + + /** + * Break out behavior + */ + testCase('Breakout - type space', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: ' ' }); + }, ''); + + testCase('Breakout - type space then undo', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: ' ' }); + editor.undo(); + }, ''); + + testCase('Breakout - type space in middle', state, async (editor) => { + const pos = new Position(1, 4); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: ' ' }); + }, ''); + + testCase('Breakout - paste content starting with space', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Paste, { text: ' i="i"' }); + }, ''); + + testCase('Breakout - paste content starting with space then undo', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Paste, { text: ' i="i"' }); + editor.undo(); + }, ''); + + testCase('Breakout - paste content starting with space in middle', state, async (editor) => { + const pos = new Position(1, 4); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Paste, { text: ' i' }); + }, ''); + + /** + * Break out with custom provider wordPattern + */ + + const state3 = { + ...state, + providerWordPattern: /[a-yA-Y]+/ + }; + + testCase('Breakout with stop pattern - insert', state3, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + testCase('Breakout with stop pattern - insert stop char', state3, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'z' }); + }, ''); + + testCase('Breakout with stop pattern - paste char', state3, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Paste, { text: 'z' }); + }, ''); + + testCase('Breakout with stop pattern - paste string', state3, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Paste, { text: 'zo' }); + }, ''); + + testCase('Breakout with stop pattern - insert at end', state3, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'z' }); + }, ''); + + const state4 = { + ...state, + providerWordPattern: /[a-yA-Y]+/, + responseWordPattern: /[a-eA-E]+/ + }; + + testCase('Breakout with stop pattern - insert stop char, respos', state4, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, ''); + + /** + * Delete + */ + testCase('Delete - left char', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', 'deleteLeft', {}); + }, ''); + + testCase('Delete - left char then undo', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', 'deleteLeft', {}); + editor.undo(); + }, ''); + + testCase('Delete - left word', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', 'deleteWordLeft', {}); + }, '<>'); + + testCase('Delete - left word then undo', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', 'deleteWordLeft', {}); + editor.undo(); + editor.undo(); + }, ''); + + /** + * Todo: Fix test + */ + // testCase('Delete - left all', state, async (editor) => { + // const pos = new Position(1, 3); + // await editor.setPosition(pos); + // await ontypeRenameContribution.updateLinkedUI(pos); + // await editor.trigger('keyboard', 'deleteAllLeft', {}); + // }, '>'); + + /** + * Todo: Fix test + */ + // testCase('Delete - left all then undo', state, async (editor) => { + // const pos = new Position(1, 5); + // await editor.setPosition(pos); + // await ontypeRenameContribution.updateLinkedUI(pos); + // await editor.trigger('keyboard', 'deleteAllLeft', {}); + // editor.undo(); + // }, '>'); + + testCase('Delete - left all then undo twice', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.trigger('keyboard', 'deleteAllLeft', {}); + editor.undo(); + editor.undo(); + }, ''); + + testCase('Delete - selection', state, async (editor) => { + const pos = new Position(1, 5); + await editor.setPosition(pos); + await editor.setSelection(new Range(1, 2, 1, 3)); + await editor.trigger('keyboard', 'deleteLeft', {}); + }, ''); + + testCase('Delete - selection across boundary', state, async (editor) => { + const pos = new Position(1, 3); + await editor.setPosition(pos); + await editor.setSelection(new Range(1, 1, 1, 3)); + await editor.trigger('keyboard', 'deleteLeft', {}); + }, 'oo>'); + + /** + * Undo / redo + */ + testCase('Undo/redo - simple undo', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + editor.undo(); + editor.undo(); + }, ''); + + testCase('Undo/redo - simple undo/redo', state, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + editor.undo(); + editor.redo(); + }, ''); + + /** + * Multi line + */ + const state2 = { + text: [ + '', + '' + ] + }; + + testCase('Multiline insert', state2, async (editor) => { + const pos = new Position(1, 2); + await editor.setPosition(pos); + await editor.trigger('keyboard', Handler.Type, { text: 'i' }); + }, [ + '', + '' + ]); +}); diff --git a/src/vs/editor/contrib/smartSelect/bracketSelections.ts b/src/vs/editor/contrib/smartSelect/bracketSelections.ts index 69bbb6eadea..3842ad87b54 100644 --- a/src/vs/editor/contrib/smartSelect/bracketSelections.ts +++ b/src/vs/editor/contrib/smartSelect/bracketSelections.ts @@ -19,8 +19,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider { result.push(bucket); const ranges = new Map>(); - await new Promise(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges)); - await new Promise(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket)); + await new Promise(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges)); + await new Promise(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket)); } return result; diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index c5e66c3b2f4..713d850325d 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -18,11 +18,12 @@ import * as modes from 'vs/editor/common/modes'; import * as nls from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; class SelectionRanges { @@ -47,43 +48,36 @@ class SelectionRanges { class SmartSelectController implements IEditorContribution { - public static readonly ID = 'editor.contrib.smartSelectController'; + static readonly ID = 'editor.contrib.smartSelectController'; static get(editor: ICodeEditor): SmartSelectController { return editor.getContribution(SmartSelectController.ID); } - private readonly _editor: ICodeEditor; - private _state?: SelectionRanges[]; private _selectionListener?: IDisposable; private _ignoreSelection: boolean = false; - constructor(editor: ICodeEditor) { - this._editor = editor; - } + constructor(private readonly _editor: ICodeEditor) { } dispose(): void { - dispose(this._selectionListener); + this._selectionListener?.dispose(); } - run(forward: boolean): Promise | void { + async run(forward: boolean): Promise { if (!this._editor.hasModel()) { return; } const selections = this._editor.getSelections(); const model = this._editor.getModel(); - if (!modes.SelectionRangeRegistry.has(model)) { return; } - - let promise: Promise = Promise.resolve(undefined); - if (!this._state) { - promise = provideSelectionRanges(model, selections.map(s => s.getPosition()), CancellationToken.None).then(ranges => { + + await provideSelectionRanges(model, selections.map(s => s.getPosition()), this._editor.getOption(EditorOption.smartSelect), CancellationToken.None).then(ranges => { if (!arrays.isNonEmptyArray(ranges) || ranges.length !== selections.length) { // invalid result return; @@ -106,31 +100,28 @@ class SmartSelectController implements IEditorContribution { this._state = ranges.map(ranges => new SelectionRanges(0, ranges)); // listen to caret move and forget about state - dispose(this._selectionListener); + this._selectionListener?.dispose(); this._selectionListener = this._editor.onDidChangeCursorPosition(() => { if (!this._ignoreSelection) { - dispose(this._selectionListener); + this._selectionListener?.dispose(); this._state = undefined; } }); }); } - return promise.then(() => { - if (!this._state) { - // no state - return; - } - this._state = this._state.map(state => state.mov(forward)); - const selections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition())); - this._ignoreSelection = true; - try { - this._editor.setSelections(selections); - } finally { - this._ignoreSelection = false; - } - - }); + if (!this._state) { + // no state + return; + } + this._state = this._state.map(state => state.mov(forward)); + const newSelections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition())); + this._ignoreSelection = true; + try { + this._editor.setSelections(newSelections); + } finally { + this._ignoreSelection = false; + } } } @@ -213,7 +204,11 @@ registerEditorAction(ShrinkSelectionAction); // word selection modes.SelectionRangeRegistry.register('*', new WordSelectionRangeProvider()); -export function provideSelectionRanges(model: ITextModel, positions: Position[], token: CancellationToken): Promise { +export interface SelectionRangesOptions { + selectLeadingAndTrailingWhitespace: boolean +} + +export async function provideSelectionRanges(model: ITextModel, positions: Position[], options: SelectionRangesOptions, token: CancellationToken): Promise { const providers = modes.SelectionRangeRegistry.all(model); @@ -243,66 +238,69 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], }, onUnexpectedExternalError)); } - return Promise.all(work).then(() => { + await Promise.all(work); - return allRawRanges.map(oneRawRanges => { + return allRawRanges.map(oneRawRanges => { - if (oneRawRanges.length === 0) { - return []; + if (oneRawRanges.length === 0) { + return []; + } + + // sort all by start/end position + oneRawRanges.sort((a, b) => { + if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) { + return 1; + } else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) { + return -1; + } else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) { + return -1; + } else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) { + return 1; + } else { + return 0; } - - // sort all by start/end position - oneRawRanges.sort((a, b) => { - if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) { - return 1; - } else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) { - return -1; - } else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) { - return -1; - } else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) { - return 1; - } else { - return 0; - } - }); - - // remove ranges that don't contain the former range or that are equal to the - // former range - let oneRanges: Range[] = []; - let last: Range | undefined; - for (const range of oneRawRanges) { - if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) { - oneRanges.push(range); - last = range; - } - } - - // add ranges that expand trivia at line starts and ends whenever a range - // wraps onto the a new line - let oneRangesWithTrivia: Range[] = [oneRanges[0]]; - for (let i = 1; i < oneRanges.length; i++) { - const prev = oneRanges[i - 1]; - const cur = oneRanges[i]; - if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { - // add line/block range without leading/failing whitespace - const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); - if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { - oneRangesWithTrivia.push(rangeNoWhitespace); - } - // add line/block range - const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); - if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { - oneRangesWithTrivia.push(rangeFull); - } - } - oneRangesWithTrivia.push(cur); - } - return oneRangesWithTrivia; }); + + // remove ranges that don't contain the former range or that are equal to the + // former range + let oneRanges: Range[] = []; + let last: Range | undefined; + for (const range of oneRawRanges) { + if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) { + oneRanges.push(range); + last = range; + } + } + + if (!options.selectLeadingAndTrailingWhitespace) { + return oneRanges; + } + + // add ranges that expand trivia at line starts and ends whenever a range + // wraps onto the a new line + let oneRangesWithTrivia: Range[] = [oneRanges[0]]; + for (let i = 1; i < oneRanges.length; i++) { + const prev = oneRanges[i - 1]; + const cur = oneRanges[i]; + if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { + // add line/block range without leading/failing whitespace + const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); + if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { + oneRangesWithTrivia.push(rangeNoWhitespace); + } + // add line/block range + const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); + if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { + oneRangesWithTrivia.push(rangeFull); + } + } + oneRangesWithTrivia.push(cur); + } + return oneRangesWithTrivia; }); } registerModelCommand('_executeSelectionRangeProvider', function (model, ...args) { const [positions] = args; - return provideSelectionRanges(model, positions, CancellationToken.None); + return provideSelectionRanges(model, positions, { selectLeadingAndTrailingWhitespace: true }, CancellationToken.None); }); diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 2b19d53b201..4c566492b68 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -19,6 +19,9 @@ import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSe import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { NullLogService } from 'vs/platform/log/common/log'; +import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; class MockJSMode extends MockMode { @@ -47,7 +50,8 @@ suite('SmartSelect', () => { setup(() => { const configurationService = new TestConfigurationService(); - modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService()); + const dialogService = new TestDialogService(); + modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService())); mode = new MockJSMode(); }); @@ -56,10 +60,10 @@ suite('SmartSelect', () => { mode.dispose(); }); - async function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[]): Promise { + async function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[], selectLeadingAndTrailingWhitespace = true): Promise { let uri = URI.file('test.js'); let model = modelService.createModel(text.join('\n'), new StaticLanguageSelector(mode.getLanguageIdentifier()), uri); - let [actual] = await provideSelectionRanges(model, [new Position(lineNumber, column)], CancellationToken.None); + let [actual] = await provideSelectionRanges(model, [new Position(lineNumber, column)], { selectLeadingAndTrailingWhitespace }, CancellationToken.None); let actualStr = actual!.map(r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn).toString()); let desiredStr = ranges.reverse().map(r => String(r)); @@ -93,6 +97,28 @@ suite('SmartSelect', () => { ]); }); + test('config: selectLeadingAndTrailingWhitespace', async () => { + + await assertGetRangesToPosition([ + 'aaa', + '\tbbb', + '' + ], 2, 3, [ + new Range(1, 1, 3, 1), // all + new Range(2, 1, 2, 5), // line w/ triva + new Range(2, 2, 2, 5), // bbb + ], true); + + await assertGetRangesToPosition([ + 'aaa', + '\tbbb', + '' + ], 2, 3, [ + new Range(1, 1, 3, 1), // all + new Range(2, 2, 2, 5), // () inside + ], false); + }); + test('getRangesToPosition #56886. Skip empty lines correctly.', () => { return assertGetRangesToPosition([ @@ -180,9 +206,11 @@ suite('SmartSelect', () => { // -- bracket selections async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise { + let index = value.indexOf('|'); + value = value.replace('|', ''); let model = modelService.createModel(value, new StaticLanguageSelector(mode.getLanguageIdentifier()), URI.parse('fake:lang')); - let pos = model.getPositionAt(value.indexOf('|')); + let pos = model.getPositionAt(index); let all = await provider.provideSelectionRanges(model, [pos], CancellationToken.None); let ranges = all![0]; @@ -197,18 +225,18 @@ suite('SmartSelect', () => { test('bracket selection', async () => { await assertRanges(new BracketSelectionRangeProvider(), '(|)', - new Range(1, 2, 1, 3), new Range(1, 1, 1, 4) + new Range(1, 2, 1, 2), new Range(1, 1, 1, 3) ); await assertRanges(new BracketSelectionRangeProvider(), '[[[](|)]]', - new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), // () - new Range(1, 3, 1, 8), new Range(1, 2, 1, 9), // [[]()] - new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), // [[[]()]] + new Range(1, 6, 1, 6), new Range(1, 5, 1, 7), // () + new Range(1, 3, 1, 7), new Range(1, 2, 1, 8), // [[]()] + new Range(1, 2, 1, 8), new Range(1, 1, 1, 9), // [[[]()]] ); await assertRanges(new BracketSelectionRangeProvider(), '[a[](|)a]', - new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), - new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), + new Range(1, 6, 1, 6), new Range(1, 5, 1, 7), + new Range(1, 2, 1, 8), new Range(1, 1, 1, 9), ); // no bracket @@ -219,23 +247,23 @@ suite('SmartSelect', () => { await assertRanges(new BracketSelectionRangeProvider(), '|[[[]()]]'); // edge - await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); - await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); + await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 8), new Range(1, 1, 1, 9)); + await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 8), new Range(1, 1, 1, 9)); - await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 16), new Range(1, 12, 1, 17)); - await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 17), new Range(1, 13, 1, 18), new Range(1, 2, 1, 26), new Range(1, 1, 1, 27)); + await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 15), new Range(1, 12, 1, 16)); + await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 16), new Range(1, 13, 1, 17), new Range(1, 2, 1, 25), new Range(1, 1, 1, 26)); }); test('bracket with leading/trailing', async () => { await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b){\n foo(|);\n}', - new Range(2, 7, 2, 8), new Range(2, 6, 2, 9), + new Range(2, 7, 2, 7), new Range(2, 6, 2, 8), new Range(1, 13, 3, 1), new Range(1, 12, 3, 2), new Range(1, 1, 3, 2), new Range(1, 1, 3, 2), ); await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b)\n{\n foo(|);\n}', - new Range(3, 7, 3, 8), new Range(3, 6, 3, 9), + new Range(3, 7, 3, 7), new Range(3, 6, 3, 8), new Range(2, 2, 4, 1), new Range(2, 1, 4, 2), new Range(1, 1, 4, 2), new Range(1, 1, 4, 2), ); @@ -244,60 +272,60 @@ suite('SmartSelect', () => { test('in-word ranges', async () => { await assertRanges(new WordSelectionRangeProvider(), 'f|ooBar', - new Range(1, 1, 1, 5), // foo - new Range(1, 1, 1, 8), // fooBar - new Range(1, 1, 1, 8), // doc + new Range(1, 1, 1, 4), // foo + new Range(1, 1, 1, 7), // fooBar + new Range(1, 1, 1, 7), // doc ); await assertRanges(new WordSelectionRangeProvider(), 'f|oo_Ba', - new Range(1, 1, 1, 5), - new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), + new Range(1, 1, 1, 4), + new Range(1, 1, 1, 7), + new Range(1, 1, 1, 7), ); await assertRanges(new WordSelectionRangeProvider(), 'f|oo-Ba', - new Range(1, 1, 1, 5), - new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), + new Range(1, 1, 1, 4), + new Range(1, 1, 1, 7), + new Range(1, 1, 1, 7), ); }); test('Default selection should select current word/hump first in camelCase #67493', async function () { await assertRanges(new WordSelectionRangeProvider(), 'Abs|tractSmartSelect', - new Range(1, 1, 1, 10), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 1, 1, 9), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'AbstractSma|rtSelect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac-Sma|rt-elect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt_elect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt-elect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rtSelect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); }); @@ -321,4 +349,49 @@ suite('SmartSelect', () => { reg.dispose(); }); + + test('Expand selection in words with underscores is inconsistent #90589', async function () { + + await assertRanges(new WordSelectionRangeProvider(), 'Hel|lo_World', + new Range(1, 1, 1, 6), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello_Wo|rld', + new Range(1, 7, 1, 12), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello|_World', + new Range(1, 1, 1, 6), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello_|World', + new Range(1, 7, 1, 12), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello|-World', + new Range(1, 1, 1, 6), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello-|World', + new Range(1, 7, 1, 12), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello|World', + new Range(1, 6, 1, 11), + new Range(1, 1, 1, 11), + new Range(1, 1, 1, 11), + ); + }); }); diff --git a/src/vs/editor/contrib/smartSelect/wordSelections.ts b/src/vs/editor/contrib/smartSelect/wordSelections.ts index 7402202af54..280663f59a8 100644 --- a/src/vs/editor/contrib/smartSelect/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/wordSelections.ts @@ -40,7 +40,7 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { // LEFT anchor (start) for (; start >= 0; start--) { let ch = word.charCodeAt(start); - if (ch === CharCode.Underline || ch === CharCode.Dash) { + if ((start !== offset) && (ch === CharCode.Underline || ch === CharCode.Dash)) { // foo-bar OR foo_bar break; } else if (isLowerAsciiLetter(ch) && isUpperAsciiLetter(lastCh)) { diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 322b36075ca..6459f4402f2 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; -import { repeat } from 'vs/base/common/strings'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; @@ -19,6 +18,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from ' import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; +import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; export interface ISnippetInsertOptions { overwriteBefore: number; @@ -27,6 +27,7 @@ export interface ISnippetInsertOptions { undoStopBefore: boolean; undoStopAfter: boolean; clipboardText: string | undefined; + overtypingCapturer: OvertypingCapturer | undefined; } const _defaultOptions: ISnippetInsertOptions = { @@ -35,7 +36,8 @@ const _defaultOptions: ISnippetInsertOptions = { undoStopBefore: true, undoStopAfter: true, adjustWhitespace: true, - clipboardText: undefined + clipboardText: undefined, + overtypingCapturer: undefined }; export class SnippetController2 implements IEditorContribution { @@ -73,7 +75,7 @@ export class SnippetController2 implements IEditorContribution { this._inSnippet.reset(); this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); - dispose(this._session); + this._session?.dispose(); this._snippetListener.dispose(); } @@ -191,7 +193,7 @@ export class SnippetController2 implements IEditorContribution { insertText: option.value, // insertText: `\${1|${after.concat(before).join(',')}|}$0`, // snippetType: 'textmate', - sortText: repeat('a', i + 1), + sortText: 'a'.repeat(i + 1), range: Range.fromPositions(this._editor.getPosition()!, this._editor.getPosition()!.delta(0, first.value.length)) }; })); @@ -209,7 +211,7 @@ export class SnippetController2 implements IEditorContribution { this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); this._snippetListener.clear(); - dispose(this._session); + this._session?.dispose(); this._session = undefined; this._modelVersionId = -1; if (resetSelection) { diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index c82aebf6181..b6bd2eb37d3 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -586,6 +586,10 @@ export class SnippetParser { return value.replace(/\$|}|\\/g, '\\$&'); } + static guessNeedsClipboard(template: string): boolean { + return /\${?CLIPBOARD/.test(template); + } + private _scanner: Scanner = new Scanner(); private _token: Token = { type: TokenType.EOF, pos: 0, len: 0 }; diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 8af3c14063b..a4e60627d8d 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -14,7 +14,6 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet, Marker } from './snippetParser'; @@ -24,6 +23,8 @@ import * as colors from 'vs/platform/theme/common/colorRegistry'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; +import { CharCode } from 'vs/base/common/charCode'; registerThemingParticipant((theme, collector) => { @@ -65,9 +66,7 @@ export class OneSnippet { dispose(): void { if (this._placeholderDecorations) { - let toRemove: string[] = []; - this._placeholderDecorations.forEach(handle => toRemove.push(handle)); - this._editor.deltaDecorations(toRemove, []); + this._editor.deltaDecorations([...this._placeholderDecorations.values()], []); } this._placeholderGroups.length = 0; } @@ -170,16 +169,16 @@ export class OneSnippet { // change stickness to never grow when typing at its edges // so that in-active tabstops never grow - this._placeholderDecorations!.forEach((id, placeholder) => { + for (const [placeholder, id] of this._placeholderDecorations!) { if (!activePlaceholders.has(placeholder)) { accessor.changeDecorationOptions(id, placeholder.isFinalTabstop ? OneSnippet._decor.inactiveFinal : OneSnippet._decor.inactive); } - }); + } return selections; - })!; + }); - return !couldSkipThisPlaceholder ? newSelections : this.move(fwd); + return !couldSkipThisPlaceholder ? newSelections ?? [] : this.move(fwd); } private _hasPlaceholderBeenCollapsed(placeholder: Placeholder): boolean { @@ -305,14 +304,14 @@ export class OneSnippet { public getEnclosingRange(): Range | undefined { let result: Range | undefined; const model = this._editor.getModel(); - this._placeholderDecorations!.forEach((decorationId) => { + for (const decorationId of this._placeholderDecorations!.values()) { const placeholderRange = withNullAsUndefined(model.getDecorationRange(decorationId)); if (!result) { result = placeholderRange; } else { result = result.plusRange(placeholderRange!); } - }); + } return result; } } @@ -322,13 +321,15 @@ export interface ISnippetSessionInsertOptions { overwriteAfter: number; adjustWhitespace: boolean; clipboardText: string | undefined; + overtypingCapturer: OvertypingCapturer | undefined; } const _defaultOptions: ISnippetSessionInsertOptions = { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: true, - clipboardText: undefined + clipboardText: undefined, + overtypingCapturer: undefined }; export class SnippetSession { @@ -337,26 +338,45 @@ export class SnippetSession { const line = model.getLineContent(position.lineNumber); const lineLeadingWhitespace = getLeadingWhitespace(line, 0, position.column - 1); + // the snippet as inserted + let snippetTextString: string | undefined; + snippet.walk(marker => { - if (marker instanceof Text && !(marker.parent instanceof Choice)) { - // adjust indentation of text markers, except for choise elements - // which get adjusted when being selected - const lines = marker.value.split(/\r\n|\r|\n/); + // all text elements that are not inside choice + if (!(marker instanceof Text) || marker.parent instanceof Choice) { + return true; + } - if (adjustIndentation) { - for (let i = 1; i < lines.length; i++) { - let templateLeadingWhitespace = getLeadingWhitespace(lines[i]); - lines[i] = model.normalizeIndentation(lineLeadingWhitespace + templateLeadingWhitespace) + lines[i].substr(templateLeadingWhitespace.length); + const lines = marker.value.split(/\r\n|\r|\n/); + + if (adjustIndentation) { + // adjust indentation of snippet test + // -the snippet-start doesn't get extra-indented (lineLeadingWhitespace), only normalized + // -all N+1 lines get extra-indented and normalized + // -the text start get extra-indented and normalized when following a linebreak + const offset = snippet.offset(marker); + if (offset === 0) { + // snippet start + lines[0] = model.normalizeIndentation(lines[0]); + + } else { + // check if text start is after a linebreak + snippetTextString = snippetTextString ?? snippet.toString(); + let prevChar = snippetTextString.charCodeAt(offset - 1); + if (prevChar === CharCode.LineFeed || prevChar === CharCode.CarriageReturn) { + lines[0] = model.normalizeIndentation(lineLeadingWhitespace + lines[0]); } } - - if (adjustNewlines) { - const newValue = lines.join(model.getEOL()); - if (newValue !== marker.value) { - marker.parent.replace(marker, [new Text(newValue)]); - } + for (let i = 1; i < lines.length; i++) { + lines[i] = model.normalizeIndentation(lineLeadingWhitespace + lines[i]); } } + + const newValue = lines.join(model.getEOL()); + if (newValue !== marker.value) { + marker.parent.replace(marker, [new Text(newValue)]); + snippetTextString = undefined; + } return true; }); } @@ -385,7 +405,7 @@ export class SnippetSession { return selection; } - static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined, overtypingCapturer: OvertypingCapturer | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { const edits: IIdentifiedSingleEditOperation[] = []; const snippets: OneSnippet[] = []; @@ -396,9 +416,7 @@ export class SnippetSession { const workspaceService = editor.invokeWithinContext(accessor => accessor.get(IWorkspaceContextService, optional)); const modelBasedVariableResolver = editor.invokeWithinContext(accessor => new ModelBasedVariableResolver(accessor.get(ILabelService, optional), model)); - - const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional)); - const readClipboardText = () => clipboardText || clipboardService && clipboardService.readTextSync(); + const readClipboardText = () => clipboardText; let delta = 0; @@ -454,8 +472,8 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), - new SelectionBasedVariableResolver(model, selection), - new CommentBasedVariableResolver(model), + new SelectionBasedVariableResolver(model, selection, idx, overtypingCapturer), + new CommentBasedVariableResolver(model, selection), new TimeBasedVariableResolver, new WorkspaceBasedVariableResolver(workspaceService), new RandomBasedVariableResolver, @@ -468,6 +486,7 @@ export class SnippetSession { // that ensures the primiary cursor stays primary despite not being // the one with lowest start position edits[idx] = EditOperation.replace(snippetSelection, snippet.toString()); + edits[idx].identifier = { major: idx, minor: 0 }; // mark the edit so only our undo edits will be used to generate end cursors snippets[idx] = new OneSnippet(editor, snippet, offset); } @@ -500,14 +519,16 @@ export class SnippetSession { } // make insert edit and start with first selections - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText, this._options.overtypingCapturer); this._snippets = snippets; this._editor.executeEdits('snippet', edits, undoEdits => { if (this._snippets[0].hasPlaceholder) { return this._move(true); } else { - return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); + return undoEdits + .filter(edit => !!edit.identifier) // only use our undo edits + .map(edit => Selection.fromPositions(edit.range.getEndPosition())); } }); this._editor.revealRange(this._editor.getSelections()[0]); @@ -518,7 +539,7 @@ export class SnippetSession { return; } this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]); - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText, options.overtypingCapturer); this._editor.executeEdits('snippet', edits, undoEdits => { for (const snippet of this._snippets) { @@ -529,7 +550,11 @@ export class SnippetSession { if (this._snippets[0].hasPlaceholder) { return this._move(undefined); } else { - return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); + return ( + undoEdits + .filter(edit => !!edit.identifier) // only use our undo edits + .map(edit => Selection.fromPositions(edit.range.getEndPosition())) + ); } }); } @@ -594,8 +619,7 @@ export class SnippetSession { // that contain at least one selection. for all remaining snippets // the same placeholder (and their ranges) must be used. if (allPossibleSelections.size === 0) { - possibleSelections.forEach((ranges, index) => { - + for (const [index, ranges] of possibleSelections) { ranges.sort(Range.compareRangesUsingStarts); for (const selection of selections) { if (ranges[0].containsRange(selection)) { @@ -603,7 +627,7 @@ export class SnippetSession { break; } } - }); + } } if (allPossibleSelections.size === 0) { @@ -624,11 +648,10 @@ export class SnippetSession { // selection selections.sort(Range.compareRangesUsingStarts); - allPossibleSelections.forEach((ranges, index) => { - + for (let [index, ranges] of allPossibleSelections) { if (ranges.length !== selections.length) { allPossibleSelections.delete(index); - return; + continue; } ranges.sort(Range.compareRangesUsingStarts); @@ -636,10 +659,10 @@ export class SnippetSession { for (let i = 0; i < ranges.length; i++) { if (!ranges[i].containsRange(selections[i])) { allPossibleSelections.delete(index); - return; + continue; } } - }); + } // from all possible selections we have deleted those // that don't match with the current selection. if we don't diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index 09fd3ca9806..cd5d505bcc9 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -10,12 +10,13 @@ import { ITextModel } from 'vs/editor/common/model'; import { Selection } from 'vs/editor/common/core/selection'; import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad, endsWith } from 'vs/base/common/strings'; +import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace } from 'vs/base/common/strings'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILabelService } from 'vs/platform/label/common/label'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { URI } from 'vs/base/common/uri'; +import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({ 'CURRENT_YEAR': true, @@ -71,7 +72,9 @@ export class SelectionBasedVariableResolver implements VariableResolver { constructor( private readonly _model: ITextModel, - private readonly _selection: Selection + private readonly _selection: Selection, + private readonly _selectionIdx: number, + private readonly _overtypingCapturer: OvertypingCapturer | undefined ) { // } @@ -82,7 +85,18 @@ export class SelectionBasedVariableResolver implements VariableResolver { if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { let value = this._model.getValueInRange(this._selection) || undefined; - if (value && this._selection.startLineNumber !== this._selection.endLineNumber && variable.snippet) { + let isMultiline = this._selection.startLineNumber !== this._selection.endLineNumber; + + // If there was no selected text, try to get last overtyped text + if (!value && this._overtypingCapturer) { + const info = this._overtypingCapturer.getLastOvertypedInfo(this._selectionIdx); + if (info) { + value = info.value; + isMultiline = info.multiline; + } + } + + if (value && isMultiline && variable.snippet) { // Selection is a multiline string which we indentation we now // need to adjust. We compare the indentation of this variable // with the indentation at the editor position and add potential @@ -208,14 +222,15 @@ export class ClipboardBasedVariableResolver implements VariableResolver { } export class CommentBasedVariableResolver implements VariableResolver { constructor( - private readonly _model: ITextModel + private readonly _model: ITextModel, + private readonly _selection: Selection ) { // } resolve(variable: Variable): string | undefined { const { name } = variable; - const language = this._model.getLanguageIdentifier(); - const config = LanguageConfigurationRegistry.getComments(language.id); + const langId = this._model.getLanguageIdAtPosition(this._selection.selectionStartLineNumber, this._selection.selectionStartColumn); + const config = LanguageConfigurationRegistry.getComments(langId); if (!config) { return undefined; } @@ -244,15 +259,15 @@ export class TimeBasedVariableResolver implements VariableResolver { } else if (name === 'CURRENT_YEAR_SHORT') { return String(new Date().getFullYear()).slice(-2); } else if (name === 'CURRENT_MONTH') { - return pad((new Date().getMonth().valueOf() + 1), 2); + return String(new Date().getMonth().valueOf() + 1).padStart(2, '0'); } else if (name === 'CURRENT_DATE') { - return pad(new Date().getDate().valueOf(), 2); + return String(new Date().getDate().valueOf()).padStart(2, '0'); } else if (name === 'CURRENT_HOUR') { - return pad(new Date().getHours().valueOf(), 2); + return String(new Date().getHours().valueOf()).padStart(2, '0'); } else if (name === 'CURRENT_MINUTE') { - return pad(new Date().getMinutes().valueOf(), 2); + return String(new Date().getMinutes().valueOf()).padStart(2, '0'); } else if (name === 'CURRENT_SECOND') { - return pad(new Date().getSeconds().valueOf(), 2); + return String(new Date().getSeconds().valueOf()).padStart(2, '0'); } else if (name === 'CURRENT_DAY_NAME') { return TimeBasedVariableResolver.dayNames[new Date().getDay()]; } else if (name === 'CURRENT_DAY_NAME_SHORT') { @@ -300,7 +315,7 @@ export class WorkspaceBasedVariableResolver implements VariableResolver { } let filename = path.basename(workspaceIdentifier.configPath.path); - if (endsWith(filename, WORKSPACE_EXTENSION)) { + if (filename.endsWith(WORKSPACE_EXTENSION)) { filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); } return filename; @@ -312,7 +327,7 @@ export class WorkspaceBasedVariableResolver implements VariableResolver { let filename = path.basename(workspaceIdentifier.configPath.path); let folderpath = workspaceIdentifier.configPath.fsPath; - if (endsWith(folderpath, filename)) { + if (folderpath.endsWith(filename)) { folderpath = folderpath.substr(0, folderpath.length - filename.length - 1); } return (folderpath ? normalizeDriveLetter(folderpath) : '/'); diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts index 05db9b1f12b..8c9be33cf11 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts @@ -6,8 +6,7 @@ import * as assert from 'assert'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; -import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { Cursor } from 'vs/editor/common/controller/cursor'; +import { ITestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -29,7 +28,7 @@ class TestSnippetController extends SnippetController2 { suite('SnippetController', () => { - function snippetTest(cb: (editor: TestCodeEditor, cursor: Cursor, template: string, snippetController: TestSnippetController) => void, lines?: string[]): void { + function snippetTest(cb: (editor: ITestCodeEditor, template: string, snippetController: TestSnippetController) => void, lines?: string[]): void { if (!lines) { lines = [ @@ -41,11 +40,11 @@ suite('SnippetController', () => { ]; } - withTestCodeEditor(lines, {}, (editor, cursor) => { + withTestCodeEditor(lines, {}, (editor) => { editor.getModel()!.updateOptions({ insertSpaces: false }); - let snippetController = editor.registerAndInstantiateContribution(TestSnippetController.ID, TestSnippetController); + let snippetController = editor.registerAndInstantiateContribution(TestSnippetController.ID, TestSnippetController); let template = [ 'for (var ${1:index}; $1 < ${2:array}.length; $1++) {', '\tvar element = $2[$1];', @@ -53,13 +52,13 @@ suite('SnippetController', () => { '}' ].join('\n'); - cb(editor, cursor, template, snippetController); + cb(editor, template, snippetController); snippetController.dispose(); }); } test('Simple accepted', () => { - snippetTest((editor, cursor, template, snippetController) => { + snippetTest((editor, template, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(template); @@ -95,7 +94,7 @@ suite('SnippetController', () => { }); test('Simple canceled', () => { - snippetTest((editor, cursor, template, snippetController) => { + snippetTest((editor, template, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(template); @@ -110,7 +109,7 @@ suite('SnippetController', () => { }); // test('Stops when deleting lines above', () => { - // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // snippetTest((editor, codeSnippet, snippetController) => { // editor.setPosition({ lineNumber: 4, column: 2 }); // snippetController.insert(codeSnippet, 0, 0); @@ -127,7 +126,7 @@ suite('SnippetController', () => { // }); // test('Stops when deleting lines below', () => { - // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // snippetTest((editor, codeSnippet, snippetController) => { // editor.setPosition({ lineNumber: 4, column: 2 }); // snippetController.run(codeSnippet, 0, 0); @@ -144,7 +143,7 @@ suite('SnippetController', () => { // }); // test('Stops when inserting lines above', () => { - // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // snippetTest((editor, codeSnippet, snippetController) => { // editor.setPosition({ lineNumber: 4, column: 2 }); // snippetController.run(codeSnippet, 0, 0); @@ -161,7 +160,7 @@ suite('SnippetController', () => { // }); // test('Stops when inserting lines below', () => { - // snippetTest((editor, cursor, codeSnippet, snippetController) => { + // snippetTest((editor, codeSnippet, snippetController) => { // editor.setPosition({ lineNumber: 4, column: 2 }); // snippetController.run(codeSnippet, 0, 0); @@ -178,7 +177,7 @@ suite('SnippetController', () => { // }); test('Stops when calling model.setValue()', () => { - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(codeSnippet); @@ -189,7 +188,7 @@ suite('SnippetController', () => { }); test('Stops when undoing', () => { - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(codeSnippet); @@ -200,7 +199,7 @@ suite('SnippetController', () => { }); test('Stops when moving cursor outside', () => { - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(codeSnippet); @@ -211,7 +210,7 @@ suite('SnippetController', () => { }); test('Stops when disconnecting editor model', () => { - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(codeSnippet); @@ -222,7 +221,7 @@ suite('SnippetController', () => { }); test('Stops when disposing editor', () => { - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); snippetController.insert(codeSnippet); @@ -233,7 +232,7 @@ suite('SnippetController', () => { }); test('Final tabstop with multiple selections', () => { - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setSelections([ new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), @@ -248,7 +247,7 @@ suite('SnippetController', () => { assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString()); }); - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setSelections([ new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), @@ -263,7 +262,7 @@ suite('SnippetController', () => { assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString()); }); - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setSelections([ new Selection(1, 1, 1, 1), new Selection(1, 5, 1, 5), @@ -278,7 +277,7 @@ suite('SnippetController', () => { assert.ok(second.equalsRange({ startLineNumber: 1, startColumn: 14, endLineNumber: 1, endColumn: 14 }), second.toString()); }); - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setSelections([ new Selection(1, 1, 1, 1), new Selection(1, 5, 1, 5), @@ -293,7 +292,7 @@ suite('SnippetController', () => { assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString()); }); - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setSelections([ new Selection(1, 1, 1, 1), new Selection(1, 5, 1, 5), @@ -308,7 +307,7 @@ suite('SnippetController', () => { assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString()); }); - snippetTest((editor, cursor, codeSnippet, snippetController) => { + snippetTest((editor, codeSnippet, snippetController) => { editor.setSelections([ new Selection(2, 7, 2, 7), ]); @@ -322,7 +321,7 @@ suite('SnippetController', () => { }); test('Final tabstop, #11742 simple', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelection(new Selection(1, 19, 1, 19)); @@ -335,7 +334,7 @@ suite('SnippetController', () => { }, ['example example sc']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelection(new Selection(1, 3, 1, 3)); @@ -353,7 +352,7 @@ suite('SnippetController', () => { }, ['af']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelection(new Selection(1, 3, 1, 3)); @@ -371,7 +370,7 @@ suite('SnippetController', () => { }, ['af']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelection(new Selection(1, 9, 1, 9)); @@ -390,7 +389,7 @@ suite('SnippetController', () => { test('Final tabstop, #11742 different indents', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(2, 4, 2, 4), @@ -416,7 +415,7 @@ suite('SnippetController', () => { test('Final tabstop, #11890 stay at the beginning', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 5, 1, 5) @@ -440,7 +439,7 @@ suite('SnippetController', () => { test('Final tabstop, no tabstop', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 3, 1, 3) @@ -457,7 +456,7 @@ suite('SnippetController', () => { test('Multiple cursor and overwriteBefore/After, issue #11060', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 7, 1, 7), @@ -470,7 +469,7 @@ suite('SnippetController', () => { }, ['this._', 'abc']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 7, 1, 7), @@ -483,7 +482,7 @@ suite('SnippetController', () => { }, ['this._', 'abc']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 7, 1, 7), @@ -497,7 +496,7 @@ suite('SnippetController', () => { }, ['this._', 'abc', 'def_']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 7, 1, 7), // primary at `this._` @@ -511,7 +510,7 @@ suite('SnippetController', () => { }, ['this._', 'abc', 'def._']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(3, 6, 3, 6), // primary at `def._` @@ -525,7 +524,7 @@ suite('SnippetController', () => { }, ['this._', 'abc', 'def._']); - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(2, 4, 2, 4), // primary at `abc` @@ -542,7 +541,7 @@ suite('SnippetController', () => { }); test('Multiple cursor and overwriteBefore/After, #16277', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 5, 1, 5), @@ -558,7 +557,7 @@ suite('SnippetController', () => { test('Insert snippet twice, #19449', () => { - snippetTest((editor, cursor, codeSnippet, controller) => { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 1, 1, 1) @@ -571,7 +570,7 @@ suite('SnippetController', () => { }, ['for (var i=0; i { + snippetTest((editor, codeSnippet, controller) => { editor.setSelections([ new Selection(1, 1, 1, 1) diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts index e2e7d2d023d..7ec29476951 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts @@ -11,6 +11,8 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { NullLogService } from 'vs/platform/log/common/log'; import { Handler } from 'vs/editor/common/editorCommon'; +import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('SnippetController2', function () { @@ -35,7 +37,7 @@ suite('SnippetController2', function () { setup(function () { contextKeys = new MockContextKeyService(); - model = TextModel.createFromString('if\n $state\nfi'); + model = createTextModel('if\n $state\nfi'); editor = createTestCodeEditor({ model: model }); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); assert.equal(model.getEOL(), '\n'); @@ -428,4 +430,29 @@ suite('SnippetController2', function () { assertSelections(editor, new Selection(2, 5, 2, 5)); assertContextKeys(contextKeys, false, false, false); }); + + test('issue #90135: confusing trim whitespace edits', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + CoreEditingCommands.Tab.runEditorCommand(null, editor, null); + + ctrl.insert('\nfoo'); + assertSelections(editor, new Selection(2, 8, 2, 8)); + }); + + test('leading TAB by snippets won\'t replace by spaces #101870', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + model.updateOptions({ insertSpaces: true, tabSize: 4 }); + ctrl.insert('\tHello World\n\tNew Line'); + assert.strictEqual(model.getValue(), ' Hello World\n New Line'); + }); + + test('leading TAB by snippets won\'t replace by spaces #101870 (part 2)', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + model.updateOptions({ insertSpaces: true, tabSize: 4 }); + ctrl.insert('\tHello World\n\tNew Line\n${1:\tmore}'); + assert.strictEqual(model.getValue(), ' Hello World\n New Line\n more'); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index d4ae07a4a49..f72727ccdce 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -11,6 +11,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { SnippetSession } from 'vs/editor/contrib/snippet/snippetSession'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('SnippetSession', function () { @@ -26,7 +27,7 @@ suite('SnippetSession', function () { } setup(function () { - model = TextModel.createFromString('function foo() {\n console.log(a);\n}'); + model = createTextModel('function foo() {\n console.log(a);\n}'); editor = createTestCodeEditor({ model: model }) as IActiveCodeEditor; editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); assert.equal(model.getEOL(), '\n'); @@ -126,7 +127,7 @@ suite('SnippetSession', function () { test('snippets, newline NO whitespace adjust', () => { editor.setSelection(new Selection(2, 5, 2, 5)); - const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined }); + const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: undefined }); session.insert(); assert.equal(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}'); }); @@ -648,7 +649,7 @@ suite('SnippetSession', function () { assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 12))); editor.setSelections([new Selection(1, 9, 1, 12)]); - new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined }).insert(); + new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }).insert(); assert.equal(model.getValue(), 'console.far'); }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 06eb5f8777c..0bb4cfe347a 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -11,7 +11,8 @@ import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/sni import { TextModel } from 'vs/editor/common/model/textModel'; import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; -import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test'; +import { mock } from 'vs/base/test/common/mock'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Snippet Variables Resolver', function () { @@ -25,7 +26,7 @@ suite('Snippet Variables Resolver', function () { let resolver: VariableResolver; setup(function () { - model = TextModel.createFromString([ + model = createTextModel([ 'this is line one', 'this is line two', ' this is line three' @@ -33,7 +34,7 @@ suite('Snippet Variables Resolver', function () { resolver = new CompositeSnippetVariableResolver([ new ModelBasedVariableResolver(labelService, model), - new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1)), + new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1), 0, undefined), ]); }); @@ -67,7 +68,7 @@ suite('Snippet Variables Resolver', function () { resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) + createTextModel('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) ); assertVariableResolve(resolver, 'TM_FILENAME', 'ghi'); if (!isWindows) { @@ -77,7 +78,7 @@ suite('Snippet Variables Resolver', function () { resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')) + createTextModel('', undefined, undefined, URI.parse('mem:fff.ts')) ); assertVariableResolve(resolver, 'TM_DIRECTORY', ''); assertVariableResolve(resolver, 'TM_FILEPATH', 'fff.ts'); @@ -92,7 +93,7 @@ suite('Snippet Variables Resolver', function () { } }; - const model = TextModel.createFromString([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt')); + const model = createTextModel([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt')); const resolver = new CompositeSnippetVariableResolver([new ModelBasedVariableResolver(labelService, model)]); @@ -101,24 +102,24 @@ suite('Snippet Variables Resolver', function () { test('editor variables, selection', function () { - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3)); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3), 0, undefined); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); - resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2)); + resolver = new SelectionBasedVariableResolver(model, new Selection(2, 3, 1, 2), 0, undefined); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); - resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2)); + resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 1, 2), 0, undefined); assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); - resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1)); + resolver = new SelectionBasedVariableResolver(model, new Selection(3, 1, 3, 1), 0, undefined); assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); }); @@ -144,19 +145,19 @@ suite('Snippet Variables Resolver', function () { resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) + createTextModel('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'ghi'); resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('mem:.git')) + createTextModel('', undefined, undefined, URI.parse('mem:.git')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', '.git'); resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('mem:foo.')) + createTextModel('', undefined, undefined, URI.parse('mem:foo.')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'foo'); }); @@ -180,7 +181,7 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve2('${ThisIsAVar/([A-Z]).*(Var)/$2-${1:/downcase}/}', 'Var-t'); assertVariableResolve2('${Foo/(.*)/${1:+Bar}/img}', 'Bar'); - //https://github.com/Microsoft/vscode/issues/33162 + //https://github.com/microsoft/vscode/issues/33162 assertVariableResolve2('export default class ${TM_FILENAME/(\\w+)\\.js/$1/g}', 'export default class FooFile', 'FooFile.js'); assertVariableResolve2('${foobarfoobar/(foo)/${1:+FAR}/g}', 'FARbarFARbar'); // global @@ -301,7 +302,7 @@ suite('Snippet Variables Resolver', function () { let workspace: IWorkspace; let resolver: VariableResolver; const workspaceService = new class implements IWorkspaceContextService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; _throw = () => { throw new Error(); }; onDidChangeWorkbenchState = this._throw; onDidChangeWorkspaceName = this._throw; diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index fbc6954b44d..43dd9e1b95c 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -10,22 +10,12 @@ import { InternalSuggestOptions } from 'vs/editor/common/config/editorOptions'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; import { CharCode } from 'vs/base/common/charCode'; import { compareIgnoreCase } from 'vs/base/common/strings'; +import { quickSelect } from 'vs/base/common/arrays'; type StrictCompletionItem = Required; -/* __GDPR__FRAGMENT__ - "ICompletionStats" : { - "suggestionCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "snippetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "textCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } -*/ -// __GDPR__TODO__: This is a dynamically extensible structure which can not be declared statically. export interface ICompletionStats { - suggestionCount: number; - snippetCount: number; - textCount: number; - [name: string]: any; + pLabelLen: number; } export class LineContext { @@ -41,6 +31,9 @@ const enum Refilter { Incr = 2 } +/** + * Sorted, filtered completion view model + * */ export class CompletionModel { private readonly _items: CompletionItem[]; @@ -52,7 +45,7 @@ export class CompletionModel { private _lineContext: LineContext; private _refilterKind: Refilter; private _filteredItems?: StrictCompletionItem[]; - private _isIncomplete?: Set; + private _providerInfo?: Map; private _stats?: ICompletionStats; constructor( @@ -61,7 +54,8 @@ export class CompletionModel { lineContext: LineContext, wordDistance: WordDistance, options: InternalSuggestOptions, - snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none' + snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none', + readonly clipboardText: string | undefined ) { this._items = items; this._column = column; @@ -95,13 +89,24 @@ export class CompletionModel { return this._filteredItems!; } + get allProvider(): IterableIterator { + this._ensureCachedState(); + return this._providerInfo!.keys(); + } + get incomplete(): Set { this._ensureCachedState(); - return this._isIncomplete!; + const result = new Set(); + for (let [provider, incomplete] of this._providerInfo!) { + if (incomplete) { + result.add(provider); + } + } + return result; } adopt(except: Set): CompletionItem[] { - let res = new Array(); + let res: CompletionItem[] = []; for (let i = 0; i < this._items.length;) { if (!except.has(this._items[i].provider)) { res.push(this._items[i]); @@ -131,8 +136,9 @@ export class CompletionModel { private _createCachedState(): void { - this._isIncomplete = new Set(); - this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 }; + this._providerInfo = new Map(); + + const labelLengths: number[] = []; const { leadingLineContent, characterCountDelta } = this._lineContext; let word = ''; @@ -151,12 +157,13 @@ export class CompletionModel { const item = source[i]; - // collect those supports that signaled having - // an incomplete result - if (item.container.incomplete) { - this._isIncomplete.add(item.provider); + if (item.isInvalid) { + continue; // SKIP invalid items } + // collect all support, know if their result is incomplete + this._providerInfo.set(item.provider, Boolean(item.container.incomplete)); + // 'word' is that remainder of the current line that we // filter and score against. In theory each suggestion uses a // different word, but in practice not - that's why we cache @@ -167,6 +174,8 @@ export class CompletionModel { wordLow = word.toLowerCase(); } + const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name; + // remember the word against which this item was // scored item.word = word; @@ -192,7 +201,6 @@ export class CompletionModel { } } - const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name; if (wordPos >= wordLen) { // the wordPos at which scoring starts is the whole word // and therefore the same rules as not having a word apply @@ -232,15 +240,16 @@ export class CompletionModel { target.push(item as StrictCompletionItem); // update stats - this._stats.suggestionCount++; - switch (item.completion.kind) { - case CompletionItemKind.Snippet: this._stats.snippetCount++; break; - case CompletionItemKind.Text: this._stats.textCount++; break; - } + labelLengths.push(textLabel.length); } this._filteredItems = target.sort(this._snippetCompareFn); this._refilterKind = Refilter.Nothing; + this._stats = { + pLabelLen: labelLengths.length ? + quickSelect(labelLengths.length - .85, labelLengths, (a, b) => a - b) + : 0 + }; } private static _compareCompletionItems(a: StrictCompletionItem, b: StrictCompletionItem): number { diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 432fbf09650..29fa4575fa9 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -4,67 +4,89 @@ *--------------------------------------------------------------------------------------------*/ /* Suggest widget*/ -.monaco-editor .suggest-widget { - z-index: 40; -} - -/** Initial widths **/ .monaco-editor .suggest-widget { width: 430px; + z-index: 40; + display: flex; + flex-direction: column; } -.monaco-editor .suggest-widget > .message, -.monaco-editor .suggest-widget > .tree, -.monaco-editor .suggest-widget > .details { +.monaco-editor .suggest-widget.message { + flex-direction: row; + align-items: center; +} + +.monaco-editor .suggest-widget, +.monaco-editor .suggest-details { + flex: 0 1 auto; width: 100%; border-style: solid; border-width: 1px; - box-sizing: border-box; } -.monaco-editor.hc-black .suggest-widget > .message, -.monaco-editor.hc-black .suggest-widget > .tree, -.monaco-editor.hc-black .suggest-widget > .details { +.monaco-editor.hc-black .suggest-widget, +.monaco-editor.hc-black .suggest-details { border-width: 2px; } -/** Adjust width when docs are expanded to the side **/ -.monaco-editor .suggest-widget.docs-side { - width: 660px; +/* Styles for status bar part */ + + +.monaco-editor .suggest-widget .suggest-status-bar { + box-sizing: border-box; + display: none; + flex-flow: row nowrap; + justify-content: space-between; + width: 100%; + font-size: 80%; + padding: 0 4px 0 4px; + border-top: 1px solid transparent; + overflow: hidden; } -.monaco-editor .suggest-widget.docs-side > .tree, -.monaco-editor .suggest-widget.docs-side > .details { - width: 50%; - float: left; +.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar { + display: flex; } -.monaco-editor .suggest-widget.docs-side.list-right > .tree, -.monaco-editor .suggest-widget.docs-side.list-right > .details { - float: right; +.monaco-editor .suggest-widget .suggest-status-bar .left { + padding-right: 8px; } -/* MarkupContent Layout */ -.monaco-editor .suggest-widget > .details ul { - padding-left: 20px; -} -.monaco-editor .suggest-widget > .details ol { - padding-left: 20px; +.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-label { + opacity: 0.5; + color: inherit; } -.monaco-editor .suggest-widget > .details p code { - font-family: var(--monaco-monospace-font); +.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label { + margin-right: 0; +} + +.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label::after { + content: ', '; + margin-right: 0.3em; +} + +.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row>.contents>.main>.right>.readMore, +.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused.string-label>.contents>.main>.right>.readMore { + display: none; +} + +.monaco-editor .suggest-widget.with-status-bar:not(.docs-side) .monaco-list .monaco-list-row:hover>.contents>.main>.right.can-expand-details>.details-label { + width: 100%; } /* Styles for Message element for when widget is loading or is empty */ -.monaco-editor .suggest-widget > .message { + +.monaco-editor .suggest-widget>.message { padding-left: 22px; } /** Styles for the list element **/ -.monaco-editor .suggest-widget > .tree { + +.monaco-editor .suggest-widget>.tree { height: 100%; + width: 100%; } .monaco-editor .suggest-widget .monaco-list { @@ -87,14 +109,14 @@ touch-action: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents { flex: 1; height: 100%; overflow: hidden; padding-left: 2px; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main { display: flex; overflow: hidden; text-overflow: ellipsis; @@ -102,8 +124,7 @@ justify-content: space-between; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left, .monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right { display: flex; } @@ -113,93 +134,108 @@ /** ReadMore Icon styles **/ -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore::before { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.header>.codicon-close, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.readMore::before { color: inherit; opacity: 1; font-size: 14px; cursor: pointer; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.header>.codicon-close { position: absolute; - top: 2px; + top: 6px; right: 2px; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close:hover, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore:hover { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.header>.codicon-close:hover, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.readMore:hover { opacity: 1; } /** signature, qualifier, type/details opacity **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label { + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.signature-label, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.qualifier-label, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label { opacity: 0.7; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.signature-label { + overflow: hidden; + text-overflow: ellipsis; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.qualifier-label { margin-left: 4px; opacity: 0.4; font-size: 90%; text-overflow: ellipsis; overflow: hidden; - line-height: 17px; align-self: center; } /** Type Info and icon next to the label in the focused completion item **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label { - margin-left: 0.8em; +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label { + margin-left: 1.1em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label > .monaco-tokenized-source { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label>.monaco-tokenized-source { display: inline; } /** Details: if using CompletionItem#details, show on focus **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label { +.monaco-editor .suggest-widget:not(.shows-details) .monaco-list .monaco-list-row.focused>.contents>.main>.right>.details-label { display: inline; } /** Details: if using CompletionItemLabel#details, always show **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right.always-show-details > .details-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right.always-show-details > .details-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label)>.contents>.main>.right>.details-label, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused:not(.string-label)>.contents>.main>.right>.details-label { display: inline; } /** Ellipsis on hover **/ -.monaco-editor .suggest-widget:not(.docs-side) .monaco-list .monaco-list-row:hover > .contents > .main > .right.can-expand-details > .details-label { + +.monaco-editor .suggest-widget:not(.docs-side) .monaco-list .monaco-list-row:hover>.contents>.main>.right.can-expand-details>.details-label { width: calc(100% - 26px); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left { flex-shrink: 1; + flex-grow: 1; overflow: hidden; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label { - flex-shrink: 0; -} -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right { - overflow: hidden; - margin-left: 16px; - flex-shrink: 0; - max-width: 45%; -} -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.monaco-icon-label { + flex-shrink: 0; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label)>.contents>.main>.left>.monaco-icon-label { + max-width: 100%; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label>.contents>.main>.left>.monaco-icon-label { + flex-shrink: 1; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right { + overflow: hidden; + flex-shrink: 4; + max-width: 70%; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.readMore { display: inline-block; position: absolute; right: 10px; @@ -209,26 +245,29 @@ } /** Do NOT display ReadMore when docs is side/below **/ -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore, -.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { + +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row>.contents>.main>.right>.readMore, .monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row>.contents>.main>.right>.readMore { display: none !important; } /** Do NOT display ReadMore when using plain CompletionItemLabel (details/documentation might not be resolved) **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right:not(.always-show-details) > .readMore { + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label>.contents>.main>.right>.readMore { display: none; } + /** Focused item can show ReadMore, but can't when docs is side/below **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore { + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused.string-label>.contents>.main>.right>.readMore { display: inline-block; } -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore, -.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row>.contents>.main>.right>.readMore, +.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row>.contents>.main>.right>.readMore { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover > .contents > .main > .right > .readMore { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover>.contents>.main>.right>.readMore { visibility: visible; } @@ -238,7 +277,8 @@ opacity: 0.66; text-decoration: unset; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated > .monaco-icon-label-container > .monaco-icon-name-container { + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated>.monaco-icon-label-container>.monaco-icon-name-container { text-decoration: line-through; } @@ -266,8 +306,7 @@ margin-right: 4px; } -.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon, -.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .suggest-icon::before { +.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon, .monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .suggest-icon::before { display: none; } @@ -280,83 +319,102 @@ } /** Styles for the docs of the completion item in focus **/ -.monaco-editor .suggest-widget .details { + +.monaco-editor .suggest-details-container { + z-index: 41; +} + +.monaco-editor .suggest-details { display: flex; flex-direction: column; cursor: default; } -.monaco-editor .suggest-widget .details.no-docs { +.monaco-editor .suggest-details.no-docs { display: none; } -.monaco-editor .suggest-widget.docs-below .details { - border-top-width: 0; -} - -.monaco-editor .suggest-widget .details > .monaco-scrollable-element { +.monaco-editor .suggest-details>.monaco-scrollable-element { flex: 1; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body { - position: absolute; +.monaco-editor .suggest-details>.monaco-scrollable-element>.body { box-sizing: border-box; height: 100%; width: 100%; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.header>.type { flex: 2; overflow: hidden; text-overflow: ellipsis; opacity: 0.7; - word-break: break-all; + white-space: pre; margin: 0 24px 0 0; padding: 4px 0 12px 5px; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.header>.type.auto-wrap { + white-space: normal; + word-break: break-all; +} + + +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs { margin: 0; padding: 4px 5px; white-space: pre-wrap; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs { - padding: 0; - white-space: initial; +.monaco-editor .suggest-details.no-type>.monaco-scrollable-element>.body>.docs { + margin-right: 24px; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div, -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > span:not(:empty) { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs.markdown-docs { + padding: 0; + white-space: initial; + min-height: calc(1rem + 8px); +} + +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs.markdown-docs>div, +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs.markdown-docs>span:not(:empty) { padding: 4px 5px; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div > p:first-child { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs.markdown-docs>div>p:first-child { margin-top: 0; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div > p:last-child { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs.markdown-docs>div>p:last-child { margin-bottom: 0; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs .code { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs .code { white-space: pre-wrap; word-wrap: break-word; } -.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty { +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>.docs.markdown-docs .codicon { + vertical-align: sub; +} + +.monaco-editor .suggest-details>.monaco-scrollable-element>.body>p:empty { display: none; } -.monaco-editor .suggest-widget .details code { +.monaco-editor .suggest-details code { border-radius: 3px; padding: 0 0.4em; } - -/* replace/insert decorations */ - -.monaco-editor .suggest-insert-unexpected { - font-style: italic; +.monaco-editor .suggest-details ul { + padding-left: 20px; } +.monaco-editor .suggest-details ol { + padding-left: 20px; +} + +.monaco-editor .suggest-details p code { + font-family: var(--monaco-monospace-font); +} diff --git a/src/vs/editor/contrib/suggest/resizable.ts b/src/vs/editor/contrib/suggest/resizable.ts new file mode 100644 index 00000000000..25e1dcb3d68 --- /dev/null +++ b/src/vs/editor/contrib/suggest/resizable.ts @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Dimension } from 'vs/base/browser/dom'; +import { Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; + + +export interface IResizeEvent { + dimension: Dimension; + done: boolean; + north?: boolean; + east?: boolean; + south?: boolean; + west?: boolean; +} + +export class ResizableHTMLElement { + + readonly domNode: HTMLElement; + + private readonly _onDidWillResize = new Emitter(); + readonly onDidWillResize: Event = this._onDidWillResize.event; + + private readonly _onDidResize = new Emitter(); + readonly onDidResize: Event = this._onDidResize.event; + + private readonly _northSash: Sash; + private readonly _eastSash: Sash; + private readonly _southSash: Sash; + private readonly _westSash: Sash; + private readonly _sashListener = new DisposableStore(); + + private _size = new Dimension(0, 0); + private _minSize = new Dimension(0, 0); + private _maxSize = new Dimension(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); + private _preferredSize?: Dimension; + + constructor() { + this.domNode = document.createElement('div'); + this._eastSash = new Sash(this.domNode, { getVerticalSashLeft: () => this._size.width }, { orientation: Orientation.VERTICAL }); + this._westSash = new Sash(this.domNode, { getVerticalSashLeft: () => 0 }, { orientation: Orientation.VERTICAL }); + this._northSash = new Sash(this.domNode, { getHorizontalSashTop: () => 0 }, { orientation: Orientation.HORIZONTAL }); + this._southSash = new Sash(this.domNode, { getHorizontalSashTop: () => this._size.height }, { orientation: Orientation.HORIZONTAL }); + + this._northSash.orthogonalStartSash = this._westSash; + this._northSash.orthogonalEndSash = this._eastSash; + this._southSash.orthogonalStartSash = this._westSash; + this._southSash.orthogonalEndSash = this._eastSash; + + let currentSize: Dimension | undefined; + let deltaY = 0; + let deltaX = 0; + + this._sashListener.add(Event.any(this._northSash.onDidStart, this._eastSash.onDidStart, this._southSash.onDidStart, this._westSash.onDidStart)(() => { + if (currentSize === undefined) { + this._onDidWillResize.fire(); + currentSize = this._size; + deltaY = 0; + deltaX = 0; + } + })); + this._sashListener.add(Event.any(this._northSash.onDidEnd, this._eastSash.onDidEnd, this._southSash.onDidEnd, this._westSash.onDidEnd)(() => { + if (currentSize !== undefined) { + currentSize = undefined; + deltaY = 0; + deltaX = 0; + this._onDidResize.fire({ dimension: this._size, done: true }); + } + })); + + this._sashListener.add(this._eastSash.onDidChange(e => { + if (currentSize) { + deltaX = e.currentX - e.startX; + this.layout(currentSize.height + deltaY, currentSize.width + deltaX); + this._onDidResize.fire({ dimension: this._size, done: false, east: true }); + } + })); + this._sashListener.add(this._westSash.onDidChange(e => { + if (currentSize) { + deltaX = -(e.currentX - e.startX); + this.layout(currentSize.height + deltaY, currentSize.width + deltaX); + this._onDidResize.fire({ dimension: this._size, done: false, west: true }); + } + })); + this._sashListener.add(this._northSash.onDidChange(e => { + if (currentSize) { + deltaY = -(e.currentY - e.startY); + this.layout(currentSize.height + deltaY, currentSize.width + deltaX); + this._onDidResize.fire({ dimension: this._size, done: false, north: true }); + } + })); + this._sashListener.add(this._southSash.onDidChange(e => { + if (currentSize) { + deltaY = e.currentY - e.startY; + this.layout(currentSize.height + deltaY, currentSize.width + deltaX); + this._onDidResize.fire({ dimension: this._size, done: false, south: true }); + } + })); + + this._sashListener.add(Event.any(this._eastSash.onDidReset, this._westSash.onDidReset)(e => { + if (this._preferredSize) { + this.layout(this._size.height, this._preferredSize.width); + this._onDidResize.fire({ dimension: this._size, done: true }); + } + })); + this._sashListener.add(Event.any(this._northSash.onDidReset, this._southSash.onDidReset)(e => { + if (this._preferredSize) { + this.layout(this._preferredSize.height, this._size.width); + this._onDidResize.fire({ dimension: this._size, done: true }); + } + })); + } + + dispose(): void { + this._northSash.dispose(); + this._southSash.dispose(); + this._eastSash.dispose(); + this._westSash.dispose(); + this._sashListener.dispose(); + this.domNode.remove(); + } + + enableSashes(north: boolean, east: boolean, south: boolean, west: boolean): void { + this._northSash.state = north ? SashState.Enabled : SashState.Disabled; + this._eastSash.state = east ? SashState.Enabled : SashState.Disabled; + this._southSash.state = south ? SashState.Enabled : SashState.Disabled; + this._westSash.state = west ? SashState.Enabled : SashState.Disabled; + } + + layout(height: number = this.size.height, width: number = this.size.width): void { + + const { height: minHeight, width: minWidth } = this._minSize; + const { height: maxHeight, width: maxWidth } = this._maxSize; + + height = Math.max(minHeight, Math.min(maxHeight, height)); + width = Math.max(minWidth, Math.min(maxWidth, width)); + + const newSize = new Dimension(width, height); + if (!Dimension.equals(newSize, this._size)) { + this.domNode.style.height = height + 'px'; + this.domNode.style.width = width + 'px'; + this._size = newSize; + this._northSash.layout(); + this._eastSash.layout(); + this._southSash.layout(); + this._westSash.layout(); + } + } + + get size() { + return this._size; + } + + set maxSize(value: Dimension) { + this._maxSize = value; + } + + get maxSize() { + return this._maxSize; + } + + set minSize(value: Dimension) { + this._minSize = value; + } + + get minSize() { + return this._minSize; + } + + set preferredSize(value: Dimension | undefined) { + this._preferredSize = value; + } + + get preferredSize() { + return this._preferredSize; + } +} diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 03746920084..650701dea1f 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { first } from 'vs/base/common/async'; -import { assign } from 'vs/base/common/objects'; import { onUnexpectedExternalError, canceled, isPromiseCanceledError } from 'vs/base/common/errors'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -16,22 +14,28 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Range } from 'vs/editor/common/core/range'; import { FuzzyScore } from 'vs/base/common/filters'; -import { isDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { isDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; +import { StopWatch } from 'vs/base/common/stopwatch'; export const Context = { Visible: new RawContextKey('suggestWidgetVisible', false), + DetailsVisible: new RawContextKey('suggestWidgetDetailsVisible', false), MultipleSuggestions: new RawContextKey('suggestWidgetMultipleSuggestions', false), MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true), - AcceptSuggestionsOnEnter: new RawContextKey('acceptSuggestionOnEnter', true) + AcceptSuggestionsOnEnter: new RawContextKey('acceptSuggestionOnEnter', true), + HasInsertAndReplaceRange: new RawContextKey('suggestionHasInsertAndReplaceRange', false), + InsertMode: new RawContextKey<'insert' | 'replace'>('suggestionInsertMode', undefined), + CanResolve: new RawContextKey('suggestionCanResolve', false), }; +export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar'); + export class CompletionItem { _brand!: 'ISuggestionItem'; - readonly resolve: (token: CancellationToken) => Promise; - isResolved: boolean = false; - // readonly editStart: IPosition; readonly editInsertEnd: IPosition; @@ -45,18 +49,24 @@ export class CompletionItem { readonly sortTextLow?: string; readonly filterTextLow?: string; + // validation + readonly isInvalid: boolean = false; + // sorting, filtering score: FuzzyScore = FuzzyScore.Default; distance: number = 0; idx?: number; word?: string; + // resolving + private _isResolved?: boolean; + private _resolveCache?: Promise; + constructor( readonly position: IPosition, readonly completion: modes.CompletionItem, readonly container: modes.CompletionList, readonly provider: modes.CompletionItemProvider, - model: ITextModel ) { this.textLabel = typeof completion.label === 'string' ? completion.label @@ -65,6 +75,9 @@ export class CompletionItem { // ensure lower-variants (perf) this.labelLow = this.textLabel.toLowerCase(); + // validate label + this.isInvalid = !this.textLabel; + this.sortTextLow = completion.sortText && completion.sortText.toLowerCase(); this.filterTextLow = completion.filterText && completion.filterText.toLowerCase(); @@ -73,43 +86,57 @@ export class CompletionItem { this.editStart = new Position(completion.range.startLineNumber, completion.range.startColumn); this.editInsertEnd = new Position(completion.range.endLineNumber, completion.range.endColumn); this.editReplaceEnd = new Position(completion.range.endLineNumber, completion.range.endColumn); + + // validate range + this.isInvalid = this.isInvalid + || Range.spansMultipleLines(completion.range) || completion.range.startLineNumber !== position.lineNumber; + } else { this.editStart = new Position(completion.range.insert.startLineNumber, completion.range.insert.startColumn); this.editInsertEnd = new Position(completion.range.insert.endLineNumber, completion.range.insert.endColumn); this.editReplaceEnd = new Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn); + + // validate ranges + this.isInvalid = this.isInvalid + || Range.spansMultipleLines(completion.range.insert) || Range.spansMultipleLines(completion.range.replace) + || completion.range.insert.startLineNumber !== position.lineNumber || completion.range.replace.startLineNumber !== position.lineNumber + || completion.range.insert.startColumn !== completion.range.replace.startColumn; } // create the suggestion resolver - const { resolveCompletionItem } = provider; - if (typeof resolveCompletionItem !== 'function') { - this.resolve = () => Promise.resolve(); - this.isResolved = true; - } else { - let cached: Promise | undefined; - this.resolve = (token) => { - if (!cached) { - cached = Promise.resolve(resolveCompletionItem.call(provider, model, position, completion, token)).then(value => { - assign(completion, value); - this.isResolved = true; - }, err => { - if (isPromiseCanceledError(err)) { - // the IPC queue will reject the request with the - // cancellation error -> reset cached - cached = undefined; - } - }); - token.onCancellationRequested(() => { - if (!this.isResolved) { - // cancellation after the request has been - // dispatched -> reset cache - cached = undefined; - } - }); - } - return cached; - }; + if (typeof provider.resolveCompletionItem !== 'function') { + this._resolveCache = Promise.resolve(); + this._isResolved = true; } } + + // ---- resolving + + get isResolved(): boolean { + return !!this._isResolved; + } + + async resolve(token: CancellationToken) { + if (!this._resolveCache) { + const sub = token.onCancellationRequested(() => { + this._resolveCache = undefined; + this._isResolved = false; + }); + this._resolveCache = Promise.resolve(this.provider.resolveCompletionItem!(this.completion, token)).then(value => { + Object.assign(this.completion, value); + this._isResolved = true; + sub.dispose(); + }, err => { + if (isPromiseCanceledError(err)) { + // the IPC queue will reject the request with the + // cancellation error -> reset cached + this._resolveCache = undefined; + this._isResolved = false; + } + }); + } + return this._resolveCache; + } } export const enum SnippetSortOrder { @@ -139,97 +166,127 @@ export function setSnippetSuggestSupport(support: modes.CompletionItemProvider): return old; } -export function provideSuggestionItems( +export interface CompletionDurationEntry { + readonly providerName: string; + readonly elapsedProvider: number; + readonly elapsedOverall: number; +} + +export interface CompletionDurations { + readonly entries: readonly CompletionDurationEntry[]; + readonly elapsed: number; +} + +export class CompletionItemModel { + constructor( + readonly items: CompletionItem[], + readonly needsClipboard: boolean, + readonly durations: CompletionDurations, + readonly disposable: IDisposable, + ) { } +} + +export async function provideSuggestionItems( model: ITextModel, position: Position, options: CompletionOptions = CompletionOptions.default, context: modes.CompletionContext = { triggerKind: modes.CompletionTriggerKind.Invoke }, token: CancellationToken = CancellationToken.None -): Promise { +): Promise { + + const sw = new StopWatch(true); + position = position.clone(); const word = model.getWordAtPosition(position); const defaultReplaceRange = word ? new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn) : Range.fromPositions(position); - const defaultInsertRange = defaultReplaceRange.setEndPosition(position.lineNumber, position.column); + const defaultRange = { replace: defaultReplaceRange, insert: defaultReplaceRange.setEndPosition(position.lineNumber, position.column) }; - // const wordUntil = model.getWordUntilPosition(position); - // const defaultRange = new Range(position.lineNumber, wordUntil.startColumn, position.lineNumber, wordUntil.endColumn); - - position = position.clone(); - - // get provider groups, always add snippet suggestion provider - const supports = modes.CompletionProviderRegistry.orderedGroups(model); - - // add snippets provider unless turned off - if (!options.kindFilter.has(modes.CompletionItemKind.Snippet) && _snippetSuggestSupport) { - supports.unshift([_snippetSuggestSupport]); - } - - const allSuggestions: CompletionItem[] = []; + const result: CompletionItem[] = []; const disposables = new DisposableStore(); - let hasResult = false; + const durations: CompletionDurationEntry[] = []; + let needsClipboard = false; + + const onCompletionList = (provider: modes.CompletionItemProvider, container: modes.CompletionList | null | undefined, sw: StopWatch) => { + if (!container) { + return; + } + for (let suggestion of container.suggestions) { + if (!options.kindFilter.has(suggestion.kind)) { + // fill in default range when missing + if (!suggestion.range) { + suggestion.range = defaultRange; + } + // fill in default sortText when missing + if (!suggestion.sortText) { + suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; + } + if (!needsClipboard && suggestion.insertTextRules && suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) { + needsClipboard = SnippetParser.guessNeedsClipboard(suggestion.insertText); + } + result.push(new CompletionItem(position, suggestion, container, provider)); + } + } + if (isDisposable(container)) { + disposables.add(container); + } + durations.push({ + providerName: provider._debugDisplayName ?? 'unkown_provider', elapsedProvider: container.duration ?? -1, elapsedOverall: sw.elapsed() + }); + }; + + // ask for snippets in parallel to asking "real" providers. Only do something if configured to + // do so - no snippet filter, no special-providers-only request + const snippetCompletions = (async () => { + if (!_snippetSuggestSupport || options.kindFilter.has(modes.CompletionItemKind.Snippet)) { + return; + } + if (options.providerFilter.size > 0 && !options.providerFilter.has(_snippetSuggestSupport)) { + return; + } + const sw = new StopWatch(true); + const list = await _snippetSuggestSupport.provideCompletionItems(model, position, context, token); + onCompletionList(_snippetSuggestSupport, list, sw); + })(); // add suggestions from contributed providers - providers are ordered in groups of // equal score and once a group produces a result the process stops - const factory = supports.map(supports => () => { + // get provider groups, always add snippet suggestion provider + for (let providerGroup of modes.CompletionProviderRegistry.orderedGroups(model)) { + // for each support in the group ask for suggestions - return Promise.all(supports.map(provider => { + let lenBefore = result.length; + await Promise.all(providerGroup.map(async provider => { if (options.providerFilter.size > 0 && !options.providerFilter.has(provider)) { - return undefined; + return; + } + try { + const sw = new StopWatch(true); + const list = await provider.provideCompletionItems(model, position, context, token); + onCompletionList(provider, list, sw); + } catch (err) { + onUnexpectedExternalError(err); } - - return Promise.resolve(provider.provideCompletionItems(model, position, context, token)).then(container => { - - const len = allSuggestions.length; - - if (container) { - for (let suggestion of container.suggestions || []) { - if (!options.kindFilter.has(suggestion.kind)) { - - // fill in default range when missing - if (!suggestion.range) { - suggestion.range = { insert: defaultInsertRange, replace: defaultReplaceRange }; - } - // fill in default sortText when missing - if (!suggestion.sortText) { - suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; - } - - allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model)); - } - } - if (isDisposable(container)) { - disposables.add(container); - } - } - - if (len !== allSuggestions.length && provider !== _snippetSuggestSupport) { - hasResult = true; - } - - }, onUnexpectedExternalError); })); - }); - const result = first(factory, () => { - // stop on result or cancellation - return hasResult || token.isCancellationRequested; - }).then(() => { - if (token.isCancellationRequested) { - disposables.dispose(); - return Promise.reject(canceled()); + if (lenBefore !== result.length || token.isCancellationRequested) { + break; } - return allSuggestions.sort(getSuggestionComparator(options.snippetSortOrder)); - }); + } - // result.then(items => { - // console.log(model.getWordUntilPosition(position), items.map(item => `${item.suggestion.label}, type=${item.suggestion.type}, incomplete?${item.container.incomplete}, overwriteBefore=${item.suggestion.overwriteBefore}`)); - // return items; - // }, err => { - // console.warn(model.getWordUntilPosition(position), err); - // }); + await snippetCompletions; - return result; + if (token.isCancellationRequested) { + disposables.dispose(); + return Promise.reject(canceled()); + } + + return new CompletionItemModel( + result.sort(getSuggestionComparator(options.snippetSortOrder)), + needsClipboard, + { entries: durations, elapsed: sw.elapsed() }, + disposables, + ); } @@ -291,27 +348,23 @@ registerDefaultLanguageCommand('_executeCompletionItemProvider', async (model, p suggestions: [] }; - const disposables = new DisposableStore(); const resolving: Promise[] = []; const maxItemsToResolve = args['maxItemsToResolve'] || 0; - const items = await provideSuggestionItems(model, position); - for (const item of items) { + const completions = await provideSuggestionItems(model, position); + for (const item of completions.items) { if (resolving.length < maxItemsToResolve) { resolving.push(item.resolve(CancellationToken.None)); } result.incomplete = result.incomplete || item.container.incomplete; result.suggestions.push(item.completion); - if (isDisposable(item.container)) { - disposables.add(item.container); - } } try { await Promise.all(resolving); return result; } finally { - setTimeout(() => disposables.dispose(), 100); + setTimeout(() => completions.disposable.dispose(), 100); } }); diff --git a/src/vs/editor/contrib/suggest/suggestAlternatives.ts b/src/vs/editor/contrib/suggest/suggestAlternatives.ts index 6c9a148e618..7c72d74d4f3 100644 --- a/src/vs/editor/contrib/suggest/suggestAlternatives.ts +++ b/src/vs/editor/contrib/suggest/suggestAlternatives.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CompletionModel } from './completionModel'; @@ -34,7 +34,7 @@ export class SuggestAlternatives { reset(): void { this._ckOtherSuggestions.reset(); - dispose(this._listener); + this._listener?.dispose(); this._model = undefined; this._acceptNext = undefined; this._ignore = false; diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 719c5446138..887064414ba 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -8,6 +8,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyCode, KeyMod, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { dispose, IDisposable, DisposableStore, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -23,27 +24,28 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { Context as SuggestContext, CompletionItem } from './suggest'; +import { Context as SuggestContext, CompletionItem, suggestWidgetStatusbarMenu } from './suggest'; import { SuggestAlternatives } from './suggestAlternatives'; import { State, SuggestModel } from './suggestModel'; import { ISelectedSuggestion, SuggestWidget } from './suggestWidget'; import { WordContextKey } from 'vs/editor/contrib/suggest/wordContextKey'; import { Event } from 'vs/base/common/event'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IdleValue } from 'vs/base/common/async'; import { isObject, assertType } from 'vs/base/common/types'; import { CommitCharacterController } from './suggestCommitCharacters'; -import { IPosition } from 'vs/editor/common/core/position'; +import { OvertypingCapturer } from './suggestOvertypingCapturer'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import * as platform from 'vs/base/common/platform'; -import { SuggestRangeHighlighter } from 'vs/editor/contrib/suggest/suggestRangeHighlighter'; +import { MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { StopWatch } from 'vs/base/common/stopwatch'; -/** - * Stop suggest widget from disappearing when clicking into other areas - * For development purpose only - */ -const _sticky = false; +// sticky suggest widget which doesn't disappear on focus out and such +let _sticky = false; +// _sticky = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this class LineSuffix { @@ -109,17 +111,23 @@ export class SuggestController implements IEditorContribution { private readonly _alternatives: IdleValue; private readonly _lineSuffix = new MutableDisposable(); private readonly _toDispose = new DisposableStore(); + private readonly _overtypingCapturer: IdleValue; constructor( editor: ICodeEditor, - @IEditorWorkerService editorWorker: IEditorWorkerService, @ISuggestMemoryService private readonly _memoryService: ISuggestMemoryService, @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILogService private readonly _logService: ILogService, ) { this.editor = editor; - this.model = new SuggestModel(this.editor, editorWorker); + this.model = _instantiationService.createInstance(SuggestModel, this.editor,); + + // context key: update insert/replace mode + const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService); + ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode); + this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)); this.widget = this._toDispose.add(new IdleValue(() => { @@ -138,9 +146,19 @@ export class SuggestController implements IEditorContribution { })); // Wire up makes text edit context key - let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService); + const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService); + const ctxHasInsertAndReplace = SuggestContext.HasInsertAndReplaceRange.bindTo(this._contextKeyService); + const ctxCanResolve = SuggestContext.CanResolve.bindTo(this._contextKeyService); + + this._toDispose.add(toDisposable(() => { + ctxMakesTextEdit.reset(); + ctxHasInsertAndReplace.reset(); + ctxCanResolve.reset(); + })); + this._toDispose.add(widget.onDidFocus(({ item }) => { + // (ctx: makesTextEdit) const position = this.editor.getPosition()!; const startColumn = item.editStart.column; const endColumn = position.column; @@ -161,9 +179,14 @@ export class SuggestController implements IEditorContribution { }); value = oldText !== item.completion.insertText; } - makesTextEdit.set(value); + ctxMakesTextEdit.set(value); + + // (ctx: hasInsertAndReplaceRange) + ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd)); + + // (ctx: canResolve) + ctxCanResolve.set(Boolean(item.provider.resolveCompletionItem) || Boolean(item.completion.documentation) || item.completion.detail !== item.completion.label); })); - this._toDispose.add(toDisposable(() => makesTextEdit.reset())); this._toDispose.add(widget.onDetailsKeyDown(e => { // cmd + c on macOS, ctrl + c on Win / Linux @@ -183,6 +206,11 @@ export class SuggestController implements IEditorContribution { return widget; })); + // Wire up text overtyping capture + this._overtypingCapturer = this._toDispose.add(new IdleValue(() => { + return this._toDispose.add(new OvertypingCapturer(this.editor, this.model)); + })); + this._alternatives = this._toDispose.add(new IdleValue(() => { return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService)); })); @@ -190,18 +218,18 @@ export class SuggestController implements IEditorContribution { this._toDispose.add(_instantiationService.createInstance(WordContextKey, editor)); this._toDispose.add(this.model.onDidTrigger(e => { - this.widget.getValue().showTriggered(e.auto, e.shy ? 250 : 50); + this.widget.value.showTriggered(e.auto, e.shy ? 250 : 50); this._lineSuffix.value = new LineSuffix(this.editor.getModel()!, e.position); })); this._toDispose.add(this.model.onDidSuggest(e => { if (!e.shy) { let index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, e.completionModel.items); - this.widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto); + this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.auto); } })); this._toDispose.add(this.model.onDidCancel(e => { if (!e.retrigger) { - this.widget.getValue().hideWidget(); + this.widget.value.hideWidget(); } })); this._toDispose.add(this.editor.onDidBlurEditorWidget(() => { @@ -219,9 +247,6 @@ export class SuggestController implements IEditorContribution { }; this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig())); updateFromConfig(); - - // create range highlighter - this._toDispose.add(new SuggestRangeHighlighter(this)); } dispose(): void { @@ -237,7 +262,7 @@ export class SuggestController implements IEditorContribution { flags: InsertFlags ): void { if (!event || !event.item) { - this._alternatives.getValue().reset(); + this._alternatives.value.reset(); this.model.cancel(); this.model.clear(); return; @@ -249,7 +274,10 @@ export class SuggestController implements IEditorContribution { const model = this.editor.getModel(); const modelVersionNow = model.getAlternativeVersionId(); const { item } = event; - const { completion: suggestion } = item; + + // + const tasks: Promise[] = []; + const cts = new CancellationTokenSource(); // pushing undo stops *before* additional text edits and // *after* the main edit @@ -263,12 +291,75 @@ export class SuggestController implements IEditorContribution { // keep item in memory this._memoryService.memorize(model, this.editor.getPosition(), item); - if (Array.isArray(suggestion.additionalTextEdits)) { - this.editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); + + if (Array.isArray(item.completion.additionalTextEdits)) { + // sync additional edits + const scrollState = StableEditorScrollState.capture(this.editor); + this.editor.executeEdits( + 'suggestController.additionalTextEdits.sync', + item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)) + ); + scrollState.restoreRelativeVerticalPositionOfCursor(this.editor); + + } else if (!item.isResolved) { + // async additional edits + const sw = new StopWatch(true); + let position: IPosition | undefined; + + const docListener = model.onDidChangeContent(e => { + if (e.isFlush) { + cts.cancel(); + docListener.dispose(); + return; + } + for (let change of e.changes) { + const thisPosition = Range.getEndPosition(change.range); + if (!position || Position.isBefore(thisPosition, position)) { + position = thisPosition; + } + } + }); + + let oldFlags = flags; + flags |= InsertFlags.NoAfterUndoStop; + let didType = false; + let typeListener = this.editor.onWillType(() => { + typeListener.dispose(); + didType = true; + if (!(oldFlags & InsertFlags.NoAfterUndoStop)) { + this.editor.pushUndoStop(); + } + }); + + tasks.push(item.resolve(cts.token).then(() => { + if (!item.completion.additionalTextEdits || cts.token.isCancellationRequested) { + return false; + } + if (position && item.completion.additionalTextEdits.some(edit => Position.isBefore(position!, Range.getStartPosition(edit.range)))) { + return false; + } + if (didType) { + this.editor.pushUndoStop(); + } + const scrollState = StableEditorScrollState.capture(this.editor); + this.editor.executeEdits( + 'suggestController.additionalTextEdits.async', + item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)) + ); + scrollState.restoreRelativeVerticalPositionOfCursor(this.editor); + if (didType || !(oldFlags & InsertFlags.NoAfterUndoStop)) { + this.editor.pushUndoStop(); + } + return true; + }).then(applied => { + this._logService.trace('[suggest] async resolving of edits DONE (ms, applied?)', sw.elapsed(), applied); + docListener.dispose(); + typeListener.dispose(); + })); } - let { insertText } = suggestion; - if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { + let { insertText } = item.completion; + if (!(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { insertText = SnippetParser.escape(insertText); } @@ -277,32 +368,35 @@ export class SuggestController implements IEditorContribution { overwriteAfter: info.overwriteAfter, undoStopBefore: false, undoStopAfter: false, - adjustWhitespace: !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) + adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace), + clipboardText: event.model.clipboardText, + overtypingCapturer: this._overtypingCapturer.value }); if (!(flags & InsertFlags.NoAfterUndoStop)) { this.editor.pushUndoStop(); } - if (!suggestion.command) { + if (!item.completion.command) { // done this.model.cancel(); - this.model.clear(); - } else if (suggestion.command.id === TriggerSuggestAction.id) { + } else if (item.completion.command.id === TriggerSuggestAction.id) { // retigger this.model.trigger({ auto: true, shy: false }, true); } else { // exec command, done - this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : [])) - .catch(onUnexpectedError) - .finally(() => this.model.clear()); // <- clear only now, keep commands alive + tasks.push(this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])).catch(onUnexpectedError)); this.model.cancel(); } if (flags & InsertFlags.KeepAlternativeSuggestions) { - this._alternatives.getValue().set(event, next => { + this._alternatives.value.set(event, next => { + + // cancel resolving of additional edits + cts.cancel(); + // this is not so pretty. when inserting the 'next' // suggestion we undo until we are at the state at // which we were before inserting the previous suggestion... @@ -319,7 +413,13 @@ export class SuggestController implements IEditorContribution { }); } - this._alertCompletionItem(event.item); + this._alertCompletionItem(item); + + // clear only now - after all tasks are done + Promise.all(tasks).finally(() => { + this.model.clear(); + cts.dispose(); + }); } getOverwriteInfo(item: CompletionItem, toggleMode: boolean): { overwriteBefore: number, overwriteAfter: number } { @@ -343,7 +443,7 @@ export class SuggestController implements IEditorContribution { private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; if (isNonEmptyArray(suggestion.additionalTextEdits)) { - let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); + let msg = nls.localize('aria.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); alert(msg); } } @@ -425,7 +525,7 @@ export class SuggestController implements IEditorContribution { } acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, alternativeOverwriteConfig: boolean): void { - const item = this.widget.getValue().getFocusedItem(); + const item = this.widget.value.getFocusedItem(); let flags = 0; if (keepAlternativeSuggestions) { flags |= InsertFlags.KeepAlternativeSuggestions; @@ -435,55 +535,58 @@ export class SuggestController implements IEditorContribution { } this._insertSuggestion(item, flags); } - acceptNextSuggestion() { - this._alternatives.getValue().next(); + this._alternatives.value.next(); } acceptPrevSuggestion() { - this._alternatives.getValue().prev(); + this._alternatives.value.prev(); } cancelSuggestWidget(): void { this.model.cancel(); this.model.clear(); - this.widget.getValue().hideWidget(); + this.widget.value.hideWidget(); } selectNextSuggestion(): void { - this.widget.getValue().selectNext(); + this.widget.value.selectNext(); } selectNextPageSuggestion(): void { - this.widget.getValue().selectNextPage(); + this.widget.value.selectNextPage(); } selectLastSuggestion(): void { - this.widget.getValue().selectLast(); + this.widget.value.selectLast(); } selectPrevSuggestion(): void { - this.widget.getValue().selectPrevious(); + this.widget.value.selectPrevious(); } selectPrevPageSuggestion(): void { - this.widget.getValue().selectPreviousPage(); + this.widget.value.selectPreviousPage(); } selectFirstSuggestion(): void { - this.widget.getValue().selectFirst(); + this.widget.value.selectFirst(); } toggleSuggestionDetails(): void { - this.widget.getValue().toggleDetails(); + this.widget.value.toggleDetails(); } toggleExplainMode(): void { - this.widget.getValue().toggleExplainMode(); + this.widget.value.toggleExplainMode(); } toggleSuggestionFocus(): void { - this.widget.getValue().toggleDetailsFocus(); + this.widget.value.toggleDetailsFocus(); + } + + resetWidgetSize(): void { + this.widget.value.resetPersistedSize(); } } @@ -500,7 +603,8 @@ export class TriggerSuggestAction extends EditorAction { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Space, - mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.Alt | KeyCode.Escape] }, + secondary: [KeyMod.CtrlCmd | KeyCode.KEY_I], + mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.Alt | KeyCode.Escape, KeyMod.CtrlCmd | KeyCode.KEY_I] }, weight: KeybindingWeight.EditorContrib } }); @@ -528,11 +632,8 @@ const SuggestCommand = EditorCommand.bindToContribution(Sugge registerEditorCommand(new SuggestCommand({ id: 'acceptSelectedSuggestion', precondition: SuggestContext.Visible, - handler(x, args) { - const alternative: boolean = typeof args === 'object' && typeof args.alternative === 'boolean' - ? args.alternative - : false; - x.acceptSelectedSuggestion(true, alternative); + handler(x) { + x.acceptSelectedSuggestion(true, false); } })); @@ -549,19 +650,55 @@ KeybindingsRegistry.registerKeybindingRule({ id: 'acceptSelectedSuggestion', when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit), primary: KeyCode.Enter, - weight + weight, }); -// shift+enter and shift+tab use the alternative-flag so that the suggest controller -// is doing the opposite of the editor.suggest.overwriteOnAccept-configuration -KeybindingsRegistry.registerKeybindingRule({ - id: 'acceptSelectedSuggestion', - when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus), - primary: KeyMod.Shift | KeyCode.Tab, - secondary: [KeyMod.Shift | KeyCode.Enter], - args: { alternative: true }, - weight +MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, { + command: { id: 'acceptSelectedSuggestion', title: nls.localize('accept.insert', "Insert") }, + group: 'left', + order: 1, + when: SuggestContext.HasInsertAndReplaceRange.toNegated() }); +MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, { + command: { id: 'acceptSelectedSuggestion', title: nls.localize('accept.insert', "Insert") }, + group: 'left', + order: 1, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('insert')) +}); +MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, { + command: { id: 'acceptSelectedSuggestion', title: nls.localize('accept.replace', "Replace") }, + group: 'left', + order: 1, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('replace')) +}); + +registerEditorCommand(new SuggestCommand({ + id: 'acceptAlternativeSelectedSuggestion', + precondition: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus), + kbOpts: { + weight: weight, + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Shift | KeyCode.Enter, + secondary: [KeyMod.Shift | KeyCode.Tab], + }, + handler(x) { + x.acceptSelectedSuggestion(false, true); + }, + menuOpts: [{ + menuId: suggestWidgetStatusbarMenu, + group: 'left', + order: 2, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('insert')), + title: nls.localize('accept.replace', "Replace") + }, { + menuId: suggestWidgetStatusbarMenu, + group: 'left', + order: 2, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('replace')), + title: nls.localize('accept.insert', "Insert") + }] +})); + // continue to support the old command CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion'); @@ -649,7 +786,20 @@ registerEditorCommand(new SuggestCommand({ kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Space, mac: { primary: KeyMod.WinCtrl | KeyCode.Space } - } + }, + menuOpts: [{ + menuId: suggestWidgetStatusbarMenu, + group: 'right', + order: 1, + when: ContextKeyExpr.and(SuggestContext.DetailsVisible, SuggestContext.CanResolve), + title: nls.localize('detail.more', "show less") + }, { + menuId: suggestWidgetStatusbarMenu, + group: 'right', + order: 1, + when: ContextKeyExpr.and(SuggestContext.DetailsVisible.toNegated(), SuggestContext.CanResolve), + title: nls.localize('detail.less', "show more") + }] })); registerEditorCommand(new SuggestCommand({ @@ -679,6 +829,7 @@ registerEditorCommand(new SuggestCommand({ registerEditorCommand(new SuggestCommand({ id: 'insertBestCompletion', precondition: ContextKeyExpr.and( + EditorContextKeys.textInputFocus, ContextKeyExpr.equals('config.editor.tabCompletion', 'on'), WordContextKey.AtEnd, SuggestContext.Visible.toNegated(), @@ -698,6 +849,7 @@ registerEditorCommand(new SuggestCommand({ registerEditorCommand(new SuggestCommand({ id: 'insertNextSuggestion', precondition: ContextKeyExpr.and( + EditorContextKeys.textInputFocus, ContextKeyExpr.equals('config.editor.tabCompletion', 'on'), SuggestAlternatives.OtherSuggestions, SuggestContext.Visible.toNegated(), @@ -714,6 +866,7 @@ registerEditorCommand(new SuggestCommand({ registerEditorCommand(new SuggestCommand({ id: 'insertPrevSuggestion', precondition: ContextKeyExpr.and( + EditorContextKeys.textInputFocus, ContextKeyExpr.equals('config.editor.tabCompletion', 'on'), SuggestAlternatives.OtherSuggestions, SuggestContext.Visible.toNegated(), @@ -726,3 +879,20 @@ registerEditorCommand(new SuggestCommand({ primary: KeyMod.Shift | KeyCode.Tab } })); + + +registerEditorAction(class extends EditorAction { + + constructor() { + super({ + id: 'editor.action.resetSuggestSize', + label: nls.localize('suggest.reset.label', "Reset Suggest Widget Size"), + alias: 'Reset Suggest Widget Size', + precondition: undefined + }); + } + + run(_accessor: ServicesAccessor, editor: ICodeEditor): void { + SuggestController.get(editor).resetWidgetSize(); + } +}); diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index 20f3b4b352d..b2c98b605e2 100644 --- a/src/vs/editor/contrib/suggest/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/suggestMemory.ts @@ -9,21 +9,24 @@ import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/ import { ITextModel } from 'vs/editor/common/model'; import { IPosition } from 'vs/editor/common/core/position'; import { CompletionItemKind, completionKindFromString } from 'vs/editor/common/modes'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { CompletionItem } from 'vs/editor/contrib/suggest/suggest'; +import { IModeService } from 'vs/editor/common/services/modeService'; export abstract class Memory { + constructor(readonly name: MemMode) { } + select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number { if (items.length === 0) { return 0; } let topScore = items[0].score[0]; - for (let i = 1; i < items.length; i++) { + for (let i = 0; i < items.length; i++) { const { score, completion: suggestion } = items[i]; if (score[0] !== topScore) { // stop when leaving the group of top matches @@ -46,6 +49,10 @@ export abstract class Memory { export class NoMemory extends Memory { + constructor() { + super('first'); + } + memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void { // no-op } @@ -67,6 +74,10 @@ export interface MemItem { export class LRUMemory extends Memory { + constructor() { + super('recentlyUsed'); + } + private _cache = new LRUCache(300, 0.66); private _seq = 0; @@ -121,11 +132,7 @@ export class LRUMemory extends Memory { } toJSON(): object { - let data: [string, MemItem][] = []; - this._cache.forEach((value, key) => { - data.push([key, value]); - }); - return data; + return this._cache.toJSON(); } fromJSON(data: [string, MemItem][]): void { @@ -143,6 +150,10 @@ export class LRUMemory extends Memory { export class PrefixMemory extends Memory { + constructor() { + super('recentlyUsedByPrefix'); + } + private _trie = TernarySearchTree.forStrings(); private _seq = 0; @@ -206,85 +217,86 @@ export class PrefixMemory extends Memory { export type MemMode = 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'; -export class SuggestMemoryService extends Disposable implements ISuggestMemoryService { +export class SuggestMemoryService implements ISuggestMemoryService { + + private static readonly _strategyCtors = new Map([ + ['recentlyUsedByPrefix', PrefixMemory], + ['recentlyUsed', LRUMemory], + ['first', NoMemory] + ]); + + private static readonly _storagePrefix = 'suggest/memories'; readonly _serviceBrand: undefined; - private readonly _storagePrefix = 'suggest/memories'; private readonly _persistSoon: RunOnceScheduler; - private _mode!: MemMode; - private _shareMem!: boolean; - private _strategy!: Memory; + private readonly _disposables = new DisposableStore(); + + private _strategy?: Memory; constructor( @IStorageService private readonly _storageService: IStorageService, + @IModeService private readonly _modeService: IModeService, @IConfigurationService private readonly _configService: IConfigurationService, ) { - super(); - - const update = () => { - const mode = this._configService.getValue('editor.suggestSelection'); - const share = this._configService.getValue('editor.suggest.shareSuggestSelections'); - this._update(mode, share, false); - }; - - this._persistSoon = this._register(new RunOnceScheduler(() => this._saveState(), 500)); - this._register(_storageService.onWillSaveState(e => { + this._persistSoon = new RunOnceScheduler(() => this._saveState(), 500); + this._disposables.add(_storageService.onWillSaveState(e => { if (e.reason === WillSaveStateReason.SHUTDOWN) { this._saveState(); } })); - - this._register(this._configService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('editor.suggestSelection') || e.affectsConfiguration('editor.suggest.shareSuggestSelections')) { - update(); - } - })); - this._register(this._storageService.onDidChangeStorage(e => { - if (e.scope === StorageScope.GLOBAL && e.key.indexOf(this._storagePrefix) === 0) { - if (!document.hasFocus()) { - // windows that aren't focused have to drop their current - // storage value and accept what's stored now - this._update(this._mode, this._shareMem, true); - } - } - })); - update(); } - private _update(mode: MemMode, shareMem: boolean, force: boolean): void { - if (!force && this._mode === mode && this._shareMem === shareMem) { - return; - } - this._shareMem = shareMem; - this._mode = mode; - this._strategy = mode === 'recentlyUsedByPrefix' ? new PrefixMemory() : mode === 'recentlyUsed' ? new LRUMemory() : new NoMemory(); - - try { - const scope = shareMem ? StorageScope.GLOBAL : StorageScope.WORKSPACE; - const raw = this._storageService.get(`${this._storagePrefix}/${this._mode}`, scope); - if (raw) { - this._strategy.fromJSON(JSON.parse(raw)); - } - } catch (e) { - // things can go wrong with JSON... - } + dispose(): void { + this._disposables.dispose(); + this._persistSoon.dispose(); } memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void { - this._strategy.memorize(model, pos, item); + this._withStrategy(model, pos).memorize(model, pos, item); this._persistSoon.schedule(); } select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number { - return this._strategy.select(model, pos, items); + return this._withStrategy(model, pos).select(model, pos, items); + } + + private _withStrategy(model: ITextModel, pos: IPosition): Memory { + + const mode = this._configService.getValue('editor.suggestSelection', { + overrideIdentifier: this._modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(pos.lineNumber, pos.column))?.language, + resource: model.uri + }); + + if (this._strategy?.name !== mode) { + + this._saveState(); + const ctor = SuggestMemoryService._strategyCtors.get(mode) || NoMemory; + this._strategy = new ctor(); + + try { + const share = this._configService.getValue('editor.suggest.shareSuggestSelections'); + const scope = share ? StorageScope.GLOBAL : StorageScope.WORKSPACE; + const raw = this._storageService.get(`${SuggestMemoryService._storagePrefix}/${mode}`, scope); + if (raw) { + this._strategy.fromJSON(JSON.parse(raw)); + } + } catch (e) { + // things can go wrong with JSON... + } + } + + return this._strategy; } private _saveState() { - const raw = JSON.stringify(this._strategy); - const scope = this._shareMem ? StorageScope.GLOBAL : StorageScope.WORKSPACE; - this._storageService.store(`${this._storagePrefix}/${this._mode}`, raw, scope); + if (this._strategy) { + const share = this._configService.getValue('editor.suggest.shareSuggestSelections'); + const scope = share ? StorageScope.GLOBAL : StorageScope.WORKSPACE; + const raw = JSON.stringify(this._strategy); + this._storageService.store(`${SuggestMemoryService._storagePrefix}/${this._strategy.name}`, raw, scope); + } } } @@ -292,7 +304,7 @@ export class SuggestMemoryService extends Disposable implements ISuggestMemorySe export const ISuggestMemoryService = createDecorator('ISuggestMemories'); export interface ISuggestMemoryService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void; select(model: ITextModel, pos: IPosition, items: CompletionItem[]): number; } diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index f4607a3b65c..6cad7c8b6d2 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose, DisposableStore, isDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Position, IPosition } from 'vs/editor/common/core/position'; @@ -15,13 +14,16 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel, IWordAtPosition } from 'vs/editor/common/model'; import { CompletionItemProvider, StandardTokenType, CompletionContext, CompletionProviderRegistry, CompletionTriggerKind, CompletionItemKind } from 'vs/editor/common/modes'; import { CompletionModel } from './completionModel'; -import { CompletionItem, getSuggestionComparator, provideSuggestionItems, getSnippetSuggestSupport, SnippetSortOrder, CompletionOptions } from './suggest'; +import { CompletionItem, getSuggestionComparator, provideSuggestionItems, getSnippetSuggestSupport, SnippetSortOrder, CompletionOptions, CompletionDurations } from './suggest'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isLowSurrogate, isHighSurrogate } from 'vs/base/common/strings'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ILogService } from 'vs/platform/log/common/log'; export interface ICancelEvent { readonly retrigger: boolean; @@ -43,6 +45,7 @@ export interface ISuggestEvent { export interface SuggestTriggerContext { readonly auto: boolean; readonly shy: boolean; + readonly triggerKind?: CompletionTriggerKind; readonly triggerCharacter?: string; } @@ -116,7 +119,10 @@ export class SuggestModel implements IDisposable { constructor( private readonly _editor: ICodeEditor, - private readonly _editorWorker: IEditorWorkerService + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + @IClipboardService private readonly _clipboardService: IClipboardService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ILogService private readonly _logService: ILogService, ) { this._currentSelection = this._editor.getSelection() || new Selection(1, 1, 1, 1); @@ -227,8 +233,10 @@ export class SuggestModel implements IDisposable { if (supports) { // keep existing items that where not computed by the // supports/providers that want to trigger now - const items: CompletionItem[] | undefined = this._completionModel ? this._completionModel.adopt(supports) : undefined; - this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items); + const existing = this._completionModel + ? { items: this._completionModel.adopt(supports), clipboardText: this._completionModel.clipboardText } + : undefined; + this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, existing); } }; @@ -245,10 +253,8 @@ export class SuggestModel implements IDisposable { cancel(retrigger: boolean = false): void { if (this._state !== State.Idle) { this._triggerQuickSuggest.cancel(); - if (this._requestToken) { - this._requestToken.cancel(); - this._requestToken = undefined; - } + this._requestToken?.cancel(); + this._requestToken = undefined; this._state = State.Idle; this._completionModel = undefined; this._context = undefined; @@ -281,7 +287,7 @@ export class SuggestModel implements IDisposable { this._currentSelection = this._editor.getSelection(); if (!e.selection.isEmpty() - || e.reason !== CursorChangeReason.NotSet + || (e.reason !== CursorChangeReason.NotSet && e.reason !== CursorChangeReason.Explicit) || (e.source !== 'keyboard' && e.source !== 'deleteLeft') ) { // Early exit if nothing needs to be done! @@ -294,7 +300,7 @@ export class SuggestModel implements IDisposable { return; } - if (this._state === State.Idle) { + if (this._state === State.Idle && e.reason === CursorChangeReason.NotSet) { if (this._editor.getOption(EditorOption.quickSuggestions) === false) { // not enabled @@ -350,6 +356,11 @@ export class SuggestModel implements IDisposable { }, this._quickSuggestDelay); + + } else if (this._state !== State.Idle && e.reason === CursorChangeReason.Explicit) { + // suggest is active and something like cursor keys are used to move + // the cursor. this means we can refilter at the new position + this._refilterCompletionItems(); } } @@ -373,7 +384,7 @@ export class SuggestModel implements IDisposable { }); } - trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: Set, existingItems?: CompletionItem[]): void { + trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: Set, existing?: { items: CompletionItem[], clipboardText: string | undefined }): void { if (!this._editor.hasModel()) { return; } @@ -391,16 +402,12 @@ export class SuggestModel implements IDisposable { this._context = ctx; // Build context for request - let suggestCtx: CompletionContext; + let suggestCtx: CompletionContext = { triggerKind: context.triggerKind ?? CompletionTriggerKind.Invoke }; if (context.triggerCharacter) { suggestCtx = { triggerKind: CompletionTriggerKind.TriggerCharacter, triggerCharacter: context.triggerCharacter }; - } else if (onlyFrom && onlyFrom.size > 0) { - suggestCtx = { triggerKind: CompletionTriggerKind.TriggerForIncompleteCompletions }; - } else { - suggestCtx = { triggerKind: CompletionTriggerKind.Invoke }; } this._requestToken = new CancellationTokenSource(); @@ -421,10 +428,10 @@ export class SuggestModel implements IDisposable { break; } - let itemKindFilter = SuggestModel._createItemKindFilter(this._editor); - let wordDistance = WordDistance.create(this._editorWorker, this._editor); + const itemKindFilter = SuggestModel._createItemKindFilter(this._editor); + const wordDistance = WordDistance.create(this._editorWorkerService, this._editor); - let items = provideSuggestionItems( + const completions = provideSuggestionItems( model, this._editor.getPosition(), new CompletionOptions(snippetSortOrder, itemKindFilter, onlyFrom), @@ -432,9 +439,9 @@ export class SuggestModel implements IDisposable { this._requestToken.token ); - Promise.all([items, wordDistance]).then(([items, wordDistance]) => { + Promise.all([completions, wordDistance]).then(async ([completions, wordDistance]) => { - dispose(this._requestToken); + this._requestToken?.dispose(); if (this._state === State.Idle) { return; @@ -444,11 +451,17 @@ export class SuggestModel implements IDisposable { return; } - const model = this._editor.getModel(); + let clipboardText = existing?.clipboardText; + if (!clipboardText && completions.needsClipboard) { + clipboardText = await this._clipboardService.readText(); + } - if (isNonEmptyArray(existingItems)) { + const model = this._editor.getModel(); + let items = completions.items; + + if (existing) { const cmpFn = getSuggestionComparator(snippetSortOrder); - items = items.concat(existingItems).sort(cmpFn); + items = items.concat(existing.items).sort(cmpFn); } const ctx = new LineContext(model, this._editor.getPosition(), auto, context.shy); @@ -458,21 +471,31 @@ export class SuggestModel implements IDisposable { }, wordDistance, this._editor.getOption(EditorOption.suggest), - this._editor.getOption(EditorOption.snippetSuggestions) + this._editor.getOption(EditorOption.snippetSuggestions), + clipboardText ); // store containers so that they can be disposed later - for (const item of items) { - if (isDisposable(item.container)) { - this._completionDisposables.add(item.container); - } - } + this._completionDisposables.add(completions.disposable); this._onNewContext(ctx); + // finally report telemetry about durations + this._reportDurationsTelemetry(completions.durations); + }).catch(onUnexpectedError); } + private _reportDurationsTelemetry(durations: CompletionDurations): void { + + setTimeout(() => { + type Durations = { data: string; }; + type DurationsClassification = { data: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' } }; + this._telemetryService.publicLog2('suggest.durations.json', { data: JSON.stringify(durations) }); + this._logService.debug('suggest.durations.json', durations); + }); + } + private static _createItemKindFilter(editor: ICodeEditor): Set { // kind filter and snippet sort rules const result = new Set(); @@ -511,6 +534,8 @@ export class SuggestModel implements IDisposable { if (!suggestOptions.showFolders) { result.add(CompletionItemKind.Folder); } if (!suggestOptions.showTypeParameters) { result.add(CompletionItemKind.TypeParameter); } if (!suggestOptions.showSnippets) { result.add(CompletionItemKind.Snippet); } + if (!suggestOptions.showUsers) { result.add(CompletionItemKind.User); } + if (!suggestOptions.showIssues) { result.add(CompletionItemKind.Issue); } return result; } @@ -549,11 +574,25 @@ export class SuggestModel implements IDisposable { return; } + if (ctx.leadingWord.word.length !== 0 && ctx.leadingWord.startColumn > this._context.leadingWord.startColumn) { + // started a new word while IntelliSense shows -> retrigger + + // Select those providers have not contributed to this completion model and re-trigger completions for + // them. Also adopt the existing items and merge them into the new completion model + const inactiveProvider = new Set(CompletionProviderRegistry.all(this._editor.getModel()!)); + for (let provider of this._completionModel.allProvider) { + inactiveProvider.delete(provider); + } + const items = this._completionModel.adopt(new Set()); + this.trigger({ auto: this._context.auto, shy: false }, true, inactiveProvider, { items, clipboardText: this._completionModel.clipboardText }); + return; + } + if (ctx.column > this._context.column && this._completionModel.incomplete.size > 0 && ctx.leadingWord.word.length !== 0) { // typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger const { incomplete } = this._completionModel; - const adopted = this._completionModel.adopt(incomplete); - this.trigger({ auto: this._state === State.Auto, shy: false }, true, incomplete, adopted); + const items = this._completionModel.adopt(incomplete); + this.trigger({ auto: this._state === State.Auto, shy: false, triggerKind: CompletionTriggerKind.TriggerForIncompleteCompletions }, true, incomplete, { items, clipboardText: this._completionModel.clipboardText }); } else { // typed -> moved cursor RIGHT -> update UI diff --git a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts new file mode 100644 index 00000000000..ba7aaae024e --- /dev/null +++ b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { SuggestModel } from 'vs/editor/contrib/suggest/suggestModel'; + +export class OvertypingCapturer implements IDisposable { + + private static readonly _maxSelectionLength = 51200; + private readonly _disposables = new DisposableStore(); + + private _lastOvertyped: { value: string; multiline: boolean }[] = []; + private _empty: boolean = true; + + constructor(editor: ICodeEditor, suggestModel: SuggestModel) { + + this._disposables.add(editor.onWillType(() => { + if (!this._empty) { + return; + } + if (!editor.hasModel()) { + return; + } + + const selections = editor.getSelections(); + const selectionsLength = selections.length; + + // Check if it will overtype any selections + let willOvertype = false; + for (let i = 0; i < selectionsLength; i++) { + if (!selections[i].isEmpty()) { + willOvertype = true; + break; + } + } + if (!willOvertype) { + return; + } + + this._lastOvertyped = []; + const model = editor.getModel(); + for (let i = 0; i < selectionsLength; i++) { + const selection = selections[i]; + // Check for overtyping capturer restrictions + if (model.getValueLengthInRange(selection) > OvertypingCapturer._maxSelectionLength) { + return; + } + this._lastOvertyped[i] = { value: model.getValueInRange(selection), multiline: selection.startLineNumber !== selection.endLineNumber }; + } + this._empty = false; + })); + + this._disposables.add(suggestModel.onDidCancel(e => { + if (!this._empty && !e.retrigger) { + this._empty = true; + } + })); + } + + getLastOvertypedInfo(idx: number): { value: string; multiline: boolean } | undefined { + if (!this._empty && idx >= 0 && idx < this._lastOvertyped.length) { + return this._lastOvertyped[idx]; + } + return undefined; + } + + dispose() { + this._disposables.dispose(); + } +} diff --git a/src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts b/src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts deleted file mode 100644 index 0056a622743..00000000000 --- a/src/vs/editor/contrib/suggest/suggestRangeHighlighter.ts +++ /dev/null @@ -1,125 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { Range } from 'vs/editor/common/core/range'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { CompletionItem } from 'vs/editor/contrib/suggest/suggest'; -import { IModelDeltaDecoration } from 'vs/editor/common/model'; -import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; -import { Emitter } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; - -export class SuggestRangeHighlighter { - - private readonly _disposables = new DisposableStore(); - - private _decorations: string[] = []; - private _widgetListener?: IDisposable; - private _shiftKeyListener?: IDisposable; - private _currentItem?: CompletionItem; - - constructor(private readonly _controller: SuggestController) { - - this._disposables.add(_controller.model.onDidSuggest(e => { - if (!e.shy) { - const widget = this._controller.widget.getValue(); - const focused = widget.getFocusedItem(); - if (focused) { - this._highlight(focused.item); - } - if (!this._widgetListener) { - this._widgetListener = widget.onDidFocus(e => this._highlight(e.item)); - } - } - })); - - this._disposables.add(_controller.model.onDidCancel(() => { - this._reset(); - })); - } - - dispose(): void { - this._reset(); - this._disposables.dispose(); - dispose(this._widgetListener); - dispose(this._shiftKeyListener); - } - - private _reset(): void { - this._decorations = this._controller.editor.deltaDecorations(this._decorations, []); - if (this._shiftKeyListener) { - this._shiftKeyListener.dispose(); - this._shiftKeyListener = undefined; - } - } - - private _highlight(item: CompletionItem) { - - this._currentItem = item; - const opts = this._controller.editor.getOption(EditorOption.suggest); - let newDeco: IModelDeltaDecoration[] = []; - - if (opts.insertHighlight) { - if (!this._shiftKeyListener) { - this._shiftKeyListener = shiftKey.event(() => this._highlight(this._currentItem!)); - } - - const info = this._controller.getOverwriteInfo(item, shiftKey.isPressed); - const position = this._controller.editor.getPosition()!; - - if (opts.insertMode === 'insert' && info.overwriteAfter > 0) { - // wants inserts but got replace-mode -> highlight AFTER range - newDeco = [{ - range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + info.overwriteAfter), - options: { inlineClassName: 'suggest-insert-unexpected' } - }]; - - } else if (opts.insertMode === 'replace' && info.overwriteAfter === 0) { - // want replace but likely got insert -> highlight AFTER range - const wordInfo = this._controller.editor.getModel()?.getWordAtPosition(position); - if (wordInfo && wordInfo.endColumn > position.column) { - newDeco = [{ - range: new Range(position.lineNumber, position.column, position.lineNumber, wordInfo.endColumn), - options: { inlineClassName: 'suggest-insert-unexpected' } - }]; - } - } - } - - // update editor decorations - this._decorations = this._controller.editor.deltaDecorations(this._decorations, newDeco); - } -} - -const shiftKey = new class ShiftKey extends Emitter { - - private readonly _subscriptions = new DisposableStore(); - private _isPressed: boolean = false; - - constructor() { - super(); - this._subscriptions.add(domEvent(document.body, 'keydown')(e => this.isPressed = e.shiftKey)); - this._subscriptions.add(domEvent(document.body, 'keyup')(() => this.isPressed = false)); - this._subscriptions.add(domEvent(document.body, 'mouseleave')(() => this.isPressed = false)); - this._subscriptions.add(domEvent(document.body, 'blur')(() => this.isPressed = false)); - } - - get isPressed(): boolean { - return this._isPressed; - } - - set isPressed(value: boolean) { - if (this._isPressed !== value) { - this._isPressed = value; - this.fire(value); - } - } - - dispose() { - this._subscriptions.dispose(); - super.dispose(); - } -}; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 3f6f7a3b4f1..488c8f80c53 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -4,70 +4,34 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/suggest'; -import 'vs/base/browser/ui/codiconLabel/codiconLabel'; // The codicon symbol styles are defined here and must be loaded +import 'vs/base/browser/ui/codicons/codiconStyles'; // The codicon symbol styles are defined here and must be loaded import 'vs/editor/contrib/documentSymbols/outlineTree'; // The codicon symbol colors are defined here and must be loaded import * as nls from 'vs/nls'; -import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; -import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass, addDisposableListener, addStandardDisposableListener, addClasses } from 'vs/base/browser/dom'; -import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent, IListGestureEvent } from 'vs/base/browser/ui/list/list'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IListEvent, IListMouseEvent, IListGestureEvent } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { Context as SuggestContext, CompletionItem } from './suggest'; import { CompletionModel } from './completionModel'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { attachListStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; -import { CompletionItemKind, completionKindToCssClass, CompletionItemTag } from 'vs/editor/common/modes'; -import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { FileKind } from 'vs/platform/files/common/files'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { flatten } from 'vs/base/common/arrays'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; - -const expandSuggestionDocsByDefault = false; - -interface ISuggestionTemplateData { - root: HTMLElement; - - /** - * Flexbox - * < ------- left ------- > < -------- right -------- > - *